routie dev init since i didn't adhere to any proper guidance up until now

This commit is contained in:
2026-04-29 22:27:29 -06:00
commit e1dabb71e2
15301 changed files with 3562618 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
@layer vuetify-components {
.v-avatar-group {
display: inline-flex;
align-items: center;
gap: 8px;
}
.v-avatar-group--hoverable .v-avatar {
cursor: pointer;
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.v-avatar-group--hoverable .v-avatar:hover {
transform: translateY(-8px);
}
.v-avatar-group--hoverable.v-avatar-group--vertical .v-avatar:hover {
transform: translateX(8px);
}
.v-avatar-group__content {
display: flex;
align-items: center;
}
.v-avatar-group .v-avatar-group__overflow {
background: rgb(var(--v-theme-surface-light));
color: rgb(var(--v-theme-on-surface-light));
}
.v-avatar-group--horizontal .v-avatar:not(:first-child) {
margin-inline-start: var(--v-avatar-group-gap, -12px);
}
.v-avatar-group--horizontal.v-avatar-group--reverse .v-avatar-group__content {
flex-direction: row-reverse;
}
.v-avatar-group--horizontal.v-avatar-group--reverse {
/* upgrade someday: https://caniuse.com/wf-sibling-count */
}
.v-avatar-group--horizontal.v-avatar-group--reverse .v-avatar:not(:first-child) {
margin-inline-start: 0;
margin-inline-end: var(--v-avatar-group-gap, -12px);
}
.v-avatar-group--vertical .v-avatar-group__content {
flex-direction: column;
}
.v-avatar-group--vertical .v-avatar:not(:first-child) {
margin-block-start: var(--v-avatar-group-gap, -12px);
}
.v-avatar-group--vertical.v-avatar-group--reverse .v-avatar-group__content {
flex-direction: column-reverse;
}
.v-avatar-group--vertical.v-avatar-group--reverse .v-avatar:not(:first-child) {
margin-block-start: 0;
margin-block-end: var(--v-avatar-group-gap, -12px);
}
}
+399
View File
@@ -0,0 +1,399 @@
import type { PropType } from 'vue';
import type { SelectItemKey } from '../../util/index.js';
export type AvatarGroupItem = string | Record<string, any>;
export type VAvatarGroupSlots = {
default: never;
prepend: never;
append: never;
item: {
props: any;
index: number;
};
overflow: {
overflow: number;
};
};
export declare const makeVAvatarGroupProps: <Defaults extends {
class?: unknown;
style?: unknown;
tag?: unknown;
border?: unknown;
gap?: unknown;
hoverable?: unknown;
items?: unknown;
itemProps?: unknown;
limit?: unknown;
overflowText?: unknown;
reverse?: unknown;
size?: unknown;
vertical?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
class: unknown extends Defaults["class"] ? PropType<any> : {
type: PropType<unknown extends Defaults["class"] ? any : any>;
default: unknown extends Defaults["class"] ? any : any;
};
style: unknown extends Defaults["style"] ? {
type: PropType<import("vue").StyleValue>;
default: null;
} : Omit<{
type: PropType<import("vue").StyleValue>;
default: null;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | import("vue").StyleValue>;
default: unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | NonNullable<import("vue").StyleValue>;
};
tag: unknown extends Defaults["tag"] ? {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
} : Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : string | Defaults["tag"] | import("../../util/index.js").JSXComponent>;
default: unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : Defaults["tag"] | NonNullable<string | import("../../util/index.js").JSXComponent>;
};
border: unknown extends Defaults["border"] ? (BooleanConstructor | NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["border"] ? string | number | boolean : string | number | boolean | Defaults["border"]>;
default: unknown extends Defaults["border"] ? string | number | boolean : Defaults["border"] | NonNullable<string | number | boolean>;
};
gap: unknown extends Defaults["gap"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["gap"] ? string | number : string | number | Defaults["gap"]>;
default: unknown extends Defaults["gap"] ? string | number : Defaults["gap"] | NonNullable<string | number>;
};
hoverable: unknown extends Defaults["hoverable"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["hoverable"] ? boolean : boolean | Defaults["hoverable"]>;
default: unknown extends Defaults["hoverable"] ? boolean : boolean | Defaults["hoverable"];
};
items: unknown extends Defaults["items"] ? {
type: PropType<AvatarGroupItem[]>;
default: () => never[];
} : Omit<{
type: PropType<AvatarGroupItem[]>;
default: () => never[];
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["items"] ? AvatarGroupItem[] : AvatarGroupItem[] | Defaults["items"]>;
default: unknown extends Defaults["items"] ? AvatarGroupItem[] : AvatarGroupItem[] | Defaults["items"];
};
itemProps: unknown extends Defaults["itemProps"] ? {
type: PropType<SelectItemKey>;
default: null;
} : Omit<{
type: PropType<SelectItemKey>;
default: null;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["itemProps"] ? SelectItemKey : Defaults["itemProps"] | SelectItemKey>;
default: unknown extends Defaults["itemProps"] ? SelectItemKey : Defaults["itemProps"] | NonNullable<SelectItemKey>;
};
limit: unknown extends Defaults["limit"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["limit"] ? string | number : string | number | Defaults["limit"]>;
default: unknown extends Defaults["limit"] ? string | number : Defaults["limit"] | NonNullable<string | number>;
};
overflowText: unknown extends Defaults["overflowText"] ? StringConstructor : {
type: PropType<unknown extends Defaults["overflowText"] ? string : string | Defaults["overflowText"]>;
default: unknown extends Defaults["overflowText"] ? string : string | Defaults["overflowText"];
};
reverse: unknown extends Defaults["reverse"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["reverse"] ? boolean : boolean | Defaults["reverse"]>;
default: unknown extends Defaults["reverse"] ? boolean : boolean | Defaults["reverse"];
};
size: unknown extends Defaults["size"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["size"] ? string | number : string | number | Defaults["size"]>;
default: unknown extends Defaults["size"] ? string | number : Defaults["size"] | NonNullable<string | number>;
};
vertical: unknown extends Defaults["vertical"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["vertical"] ? boolean : boolean | Defaults["vertical"]>;
default: unknown extends Defaults["vertical"] ? boolean : boolean | Defaults["vertical"];
};
};
export declare const VAvatarGroup: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: string | boolean | readonly (string | number)[] | ((item: Record<string, any>, fallback?: any) => any) | null;
reverse: boolean;
vertical: boolean;
} & {
class?: any;
border?: string | number | boolean | undefined;
gap?: string | number | undefined;
limit?: string | number | undefined;
overflowText?: string | undefined;
size?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
prepend?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
item?: ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
prepend?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
item?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:item"?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:overflow"?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
style: import("vue").StyleValue;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: SelectItemKey;
reverse: boolean;
vertical: boolean;
}, true, {}, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
item: (arg: {
props: any;
index: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
overflow: (arg: {
overflow: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: string | boolean | readonly (string | number)[] | ((item: Record<string, any>, fallback?: any) => any) | null;
reverse: boolean;
vertical: boolean;
} & {
class?: any;
border?: string | number | boolean | undefined;
gap?: string | number | undefined;
limit?: string | number | undefined;
overflowText?: string | undefined;
size?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
prepend?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
item?: ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
prepend?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
item?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:item"?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:overflow"?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, {}, {}, {}, {
style: import("vue").StyleValue;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: SelectItemKey;
reverse: boolean;
vertical: boolean;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: string | boolean | readonly (string | number)[] | ((item: Record<string, any>, fallback?: any) => any) | null;
reverse: boolean;
vertical: boolean;
} & {
class?: any;
border?: string | number | boolean | undefined;
gap?: string | number | undefined;
limit?: string | number | undefined;
overflowText?: string | undefined;
size?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
prepend?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
item?: ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
prepend?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
item?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
overflow?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:item"?: false | ((arg: {
props: any;
index: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:overflow"?: false | ((arg: {
overflow: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {
style: import("vue").StyleValue;
tag: string | import("../../util/index.js").JSXComponent;
hoverable: boolean;
items: AvatarGroupItem[];
itemProps: SelectItemKey;
reverse: boolean;
vertical: boolean;
}, {}, string, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
item: (arg: {
props: any;
index: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
overflow: (arg: {
overflow: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
class: PropType<import("../../composables/component.js").ClassValue>;
style: {
type: PropType<import("vue").StyleValue>;
default: null;
};
tag: {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
gap: (NumberConstructor | StringConstructor)[];
hoverable: BooleanConstructor;
items: {
type: PropType<AvatarGroupItem[]>;
default: () => never[];
};
itemProps: {
type: PropType<SelectItemKey>;
default: null;
};
limit: (NumberConstructor | StringConstructor)[];
overflowText: StringConstructor;
reverse: BooleanConstructor;
size: (NumberConstructor | StringConstructor)[];
vertical: BooleanConstructor;
}, import("vue").ExtractPropTypes<{
class: PropType<import("../../composables/component.js").ClassValue>;
style: {
type: PropType<import("vue").StyleValue>;
default: null;
};
tag: {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
gap: (NumberConstructor | StringConstructor)[];
hoverable: BooleanConstructor;
items: {
type: PropType<AvatarGroupItem[]>;
default: () => never[];
};
itemProps: {
type: PropType<SelectItemKey>;
default: null;
};
limit: (NumberConstructor | StringConstructor)[];
overflowText: StringConstructor;
reverse: BooleanConstructor;
size: (NumberConstructor | StringConstructor)[];
vertical: BooleanConstructor;
}>>;
export type VAvatarGroup = InstanceType<typeof VAvatarGroup>;
+93
View File
@@ -0,0 +1,93 @@
import { createVNode as _createVNode, Fragment as _Fragment, mergeProps as _mergeProps, createElementVNode as _createElementVNode, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VAvatarGroup.css";
// Components
import { VAvatar } from "../../components/VAvatar/index.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/index.js"; // Composables
import { makeComponentProps } from "../../composables/component.js";
import { makeTagProps } from "../../composables/tag.js"; // Utilities
import { computed } from 'vue';
import { convertToUnit, genericComponent, getPropertyFromItem, isObject, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVAvatarGroupProps = propsFactory({
border: [Boolean, Number, String],
gap: [Number, String],
hoverable: Boolean,
items: {
type: Array,
default: () => []
},
itemProps: {
type: [Boolean, String, Array, Function],
default: null
},
limit: [Number, String],
overflowText: String,
reverse: Boolean,
size: [Number, String],
vertical: Boolean,
...makeComponentProps(),
...makeTagProps()
}, 'VAvatarGroup');
export const VAvatarGroup = genericComponent()({
name: 'VAvatarGroup',
props: makeVAvatarGroupProps(),
setup(props, {
slots
}) {
const limit = computed(() => props.limit !== null ? Number(props.limit) : null);
const overflow = computed(() => {
return limit.value && !isNaN(limit.value) && props.items.length > limit.value ? Math.max(0, props.items.length - limit.value + 1) : 0;
});
const items = computed(() => {
const visibleItems = limit.value && overflow.value > 1 ? props.items.slice(0, limit.value - 1) : props.items;
const orderedItems = props.reverse ? visibleItems.toReversed() : visibleItems;
return orderedItems.map(item => {
const avatarProps = props.itemProps === true ? isObject(item) ? item : {
image: item
} : getPropertyFromItem(item, props.itemProps, item);
if (avatarProps != null) return avatarProps;
if (!isObject(item)) return {
image: item
};
return item;
});
});
const overflowText = computed(() => props.overflowText ?? (overflow.value ? `+${overflow.value}` : ''));
const overflowItem = () => slots.overflow?.({
overflow: overflow.value
}) ?? _createVNode(VAvatar, {
"class": "v-avatar-group__overflow",
"text": overflowText.value
}, null);
useRender(() => _createVNode(props.tag, {
"class": _normalizeClass(['v-avatar-group', `v-avatar-group--${props.vertical ? 'vertical' : 'horizontal'}`, {
'v-avatar-group--hoverable': props.hoverable,
'v-avatar-group--reverse': props.reverse
}, props.class]),
"style": _normalizeStyle([{
'--v-avatar-group-gap': convertToUnit(props.gap)
}, props.style])
}, {
default: () => [slots.prepend?.(), _createElementVNode("div", {
"class": "v-avatar-group__content"
}, [_createVNode(VDefaultsProvider, {
"defaults": {
VAvatar: {
size: props.size,
border: props.border
}
}
}, {
default: () => [slots.default?.() ?? _createElementVNode(_Fragment, null, [props.reverse && overflowText.value && overflowItem(), items.value.map((item, index) => slots.item?.({
props: item,
index
}) ?? _createVNode(VAvatar, _mergeProps({
"key": index
}, item), null)), !props.reverse && overflowText.value && overflowItem()])]
})]), slots.append?.()]
}));
return {};
}
});
//# sourceMappingURL=VAvatarGroup.js.map
File diff suppressed because one or more lines are too long
+79
View File
@@ -0,0 +1,79 @@
@use '../../styles/settings';
@use '../../styles/tools';
@use './variables' as *;
@include tools.layer('components') {
.v-avatar-group {
display: inline-flex;
align-items: center;
gap: $avatar-group-container-gap;
&--hoverable {
.v-avatar {
cursor: pointer;
transition: transform 0.25s settings.$standard-easing;
&:hover {
transform: $avatar-group-hoverable-horizontal-transform;
}
}
&.v-avatar-group--vertical .v-avatar:hover {
transform: $avatar-group-hoverable-vertical-transform;
}
}
&__content {
display: flex;
align-items: center;
}
.v-avatar-group__overflow {
background: $avatar-group-overflow-background;
color: $avatar-group-overflow-color;
}
&--horizontal {
.v-avatar:not(:first-child) {
margin-inline-start: var(--v-avatar-group-gap, $avatar-group-gap);
}
&.v-avatar-group--reverse {
.v-avatar-group__content {
flex-direction: row-reverse;
}
/* upgrade someday: https://caniuse.com/wf-sibling-count */
// .v-avatar {
// z-index: calc(99 - sibling-index());
// }
.v-avatar:not(:first-child) {
margin-inline-start: 0;
margin-inline-end: var(--v-avatar-group-gap, $avatar-group-gap);
}
}
}
&--vertical {
.v-avatar-group__content {
flex-direction: column;
}
.v-avatar:not(:first-child) {
margin-block-start: var(--v-avatar-group-gap, $avatar-group-gap);
}
&.v-avatar-group--reverse {
.v-avatar-group__content {
flex-direction: column-reverse;
}
.v-avatar:not(:first-child) {
margin-block-start: 0;
margin-block-end: var(--v-avatar-group-gap, $avatar-group-gap);
}
}
}
}
}
+6
View File
@@ -0,0 +1,6 @@
$avatar-group-container-gap: 8px !default;
$avatar-group-gap: -12px !default;
$avatar-group-hoverable-horizontal-transform: translateY(-8px) !default;
$avatar-group-hoverable-vertical-transform: translateX(8px) !default;
$avatar-group-overflow-background: rgb(var(--v-theme-surface-light)) !default;
$avatar-group-overflow-color: rgb(var(--v-theme-on-surface-light)) !default;
+1
View File
@@ -0,0 +1 @@
export { VAvatarGroup } from './VAvatarGroup.js';
+2
View File
@@ -0,0 +1,2 @@
export { VAvatarGroup } from "./VAvatarGroup.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VAvatarGroup"],"sources":["../../../src/labs/VAvatarGroup/index.ts"],"sourcesContent":["export { VAvatarGroup } from './VAvatarGroup'\n"],"mappings":"SAASA,YAAY","ignoreList":[]}
+18
View File
@@ -0,0 +1,18 @@
@layer vuetify-components {
/* region INPUT */
.v-color-input {
padding: 0;
}
.v-color-input.v-input--density-default .v-color-input__pip {
--v-avatar-height: 40px;
}
.v-color-input.v-input--density-comfortable .v-color-input__pip {
--v-avatar-height: 32px;
}
.v-color-input.v-input--density-compact .v-color-input__pip {
--v-avatar-height: 28px;
}
.v-color-input .v-color-input__pip.v-avatar--variant-text {
--v-avatar-height: min-content;
}
}
File diff suppressed because it is too large Load Diff
+149
View File
@@ -0,0 +1,149 @@
import { Fragment as _Fragment, createVNode as _createVNode, createElementVNode as _createElementVNode, mergeProps as _mergeProps } from "vue";
// Styles
import "./VColorInput.css";
// Components
import { VAvatar } from "../../components/VAvatar/index.js";
import { makeVColorPickerProps, VColorPicker } from "../../components/VColorPicker/VColorPicker.js";
import { makeVConfirmEditProps, VConfirmEdit } from "../../components/VConfirmEdit/VConfirmEdit.js";
import { VMenu } from "../../components/VMenu/VMenu.js";
import { makeVTextFieldProps, VTextField } from "../../components/VTextField/VTextField.js"; // Composables
import { makeFocusProps } from "../../composables/focus.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { computed, shallowRef } from 'vue';
import { genericComponent, omit, propsFactory, useRender } from "../../util/index.js"; // Types
const availablePipLocations = ['prepend', 'prepend-inner', 'append', 'append-inner'];
export const makeVColorInputProps = propsFactory({
hidePip: Boolean,
colorPip: Boolean,
menuProps: Object,
pipIcon: {
type: String,
default: '$color'
},
pipLocation: {
type: String,
default: 'prepend',
validator: v => availablePipLocations.includes(v)
},
pipVariant: {
type: String,
default: 'text'
},
pickerProps: Object,
...makeFocusProps(),
...makeVConfirmEditProps(),
...makeVTextFieldProps(),
...omit(makeVColorPickerProps(), ['location', 'height', 'minHeight', 'maxHeight'])
}, 'VColorInput');
export const VColorInput = genericComponent()({
name: 'VColorInput',
props: makeVColorInputProps(),
emits: {
'update:modelValue': val => true
},
setup(props, {
slots
}) {
const model = useProxiedModel(props, 'modelValue');
const menu = shallowRef(false);
const isFocused = shallowRef(props.focused);
const isInteractive = computed(() => !props.disabled && !props.readonly);
const display = computed(() => model.value || null);
function onKeydown(e) {
if (e.key !== 'Enter') return;
if (!menu.value || !isFocused.value) {
menu.value = true;
return;
}
const target = e.target;
model.value = target.value;
}
function onClick(e) {
e.preventDefault();
e.stopPropagation();
menu.value = true;
}
function onSave() {
menu.value = false;
}
function onCancel() {
menu.value = false;
}
useRender(() => {
const confirmEditProps = VConfirmEdit.filterProps(props);
const colorPickerProps = {
...VColorPicker.filterProps(omit(props, ['active', 'bgColor', 'color', 'rounded', 'maxWidth', 'minWidth', 'width'])),
...props.pickerProps
};
const textFieldProps = VTextField.filterProps(props);
const slotWithPip = props.hidePip ? undefined : {
[props.pipLocation]: arg => _createElementVNode(_Fragment, null, [_createVNode(VAvatar, {
"class": "v-color-input__pip",
"color": props.colorPip ? model.value : undefined,
"variant": props.pipVariant,
"icon": props.pipIcon
}, null), slots[props.pipLocation]?.(arg)])
};
return _createVNode(VTextField, _mergeProps(textFieldProps, {
"class": ['v-color-input', props.class],
"style": props.style,
"modelValue": display.value,
"onKeydown": isInteractive.value ? onKeydown : undefined,
"focused": menu.value || isFocused.value,
"onClick:control": !props.disabled ? onClick : undefined,
"onClick:prependInner": !props.disabled ? onClick : undefined,
"onUpdate:focused": event => isFocused.value = event,
"onClick:appendInner": !props.disabled ? onClick : undefined,
"onUpdate:modelValue": val => {
model.value = val;
}
}), {
...slots,
...slotWithPip,
default: () => _createElementVNode(_Fragment, null, [_createVNode(VMenu, _mergeProps({
"modelValue": menu.value,
"onUpdate:modelValue": $event => menu.value = $event,
"activator": "parent",
"minWidth": "0",
"closeOnContentClick": false,
"openOnClick": false
}, props.menuProps), {
default: () => [_createVNode(VConfirmEdit, _mergeProps(confirmEditProps, {
"modelValue": model.value,
"onUpdate:modelValue": $event => model.value = $event,
"onSave": onSave,
"onCancel": onCancel
}), {
default: ({
actions,
model: proxyModel,
save,
cancel,
isPristine
}) => {
function onUpdateModel(value) {
if (!props.hideActions) {
proxyModel.value = value;
} else {
model.value = value;
}
}
return _createVNode(VColorPicker, _mergeProps(colorPickerProps, {
"modelValue": props.hideActions ? model.value : proxyModel.value,
"onUpdate:modelValue": value => onUpdateModel(value)
}), {
actions: !props.hideActions ? () => slots.actions?.({
save,
cancel,
isPristine
}) ?? actions() : undefined
});
}
})]
}), slots.default?.()])
});
});
}
});
//# sourceMappingURL=VColorInput.js.map
File diff suppressed because one or more lines are too long
+15
View File
@@ -0,0 +1,15 @@
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
/* region INPUT */
.v-color-input
padding: 0
@at-root
@include tools.density('v-color-input.v-input', $color-input-pip-density) using ($modifier)
.v-color-input__pip
--v-avatar-height: #{$color-input-pip-avatar-size + $modifier}
.v-color-input__pip.v-avatar--variant-text
--v-avatar-height: min-content
+6
View File
@@ -0,0 +1,6 @@
@use '../../styles/tools';
// Defaults
$color-input-pip-default-color: tools.theme-color('on-surface', var(--v-medium-emphasis-opacity)) !default;
$color-input-pip-avatar-size: 40px !default;
$color-input-pip-density: ('default': 0, 'comfortable': -2, 'compact': -3) !default;
+1
View File
@@ -0,0 +1 @@
export { VColorInput } from './VColorInput.js';
+2
View File
@@ -0,0 +1,2 @@
export { VColorInput } from "./VColorInput.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VColorInput"],"sources":["../../../src/labs/VColorInput/index.ts"],"sourcesContent":["export { VColorInput } from './VColorInput'\n"],"mappings":"SAASA,WAAW","ignoreList":[]}
@@ -0,0 +1,34 @@
@layer vuetify-components {
.v-command-palette > .v-overlay__content > .v-sheet {
display: flex;
flex: 1 1 100%;
flex-direction: column;
}
.v-command-palette--density-default .v-command-palette__input-container {
padding-block: 8px;
padding-inline: 16px;
}
.v-command-palette--density-comfortable .v-command-palette__input-container {
padding-block: 4px;
padding-inline: 12px;
}
.v-command-palette--density-compact .v-command-palette__input-container {
padding-block: 0px;
padding-inline: 8px;
}
.v-command-palette__content {
overflow-y: auto;
}
.v-command-palette__no-data {
opacity: 0.6;
padding: 16px;
text-align: center;
}
.v-command-palette__list .v-list-subheader {
opacity: 0.7;
user-select: none;
}
.v-command-palette__list .v-divider {
margin-block: 4px;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,313 @@
import { createVNode as _createVNode, createElementVNode as _createElementVNode, mergeProps as _mergeProps, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VCommandPalette.css";
// Components
import { VCommandPaletteSymbol } from "./shared.js";
import { VCommandPaletteItem } from "./VCommandPaletteItem.js";
import { VDialog } from "../../components/VDialog/index.js";
import { makeVDialogProps } from "../../components/VDialog/VDialog.js";
import { VList } from "../../components/VList/index.js";
import { VSheet } from "../../components/VSheet/index.js";
import { VTextField } from "../../components/VTextField/index.js"; // Composables
import { useCommandPaletteNavigation } from "./composables/useCommandPaletteNavigation.js";
import { makeDensityProps } from "../../composables/density.js";
import { makeFilterProps, useFilter } from "../../composables/filter.js";
import { useHotkey } from "../../composables/hotkey/index.js";
import { useLocale } from "../../composables/locale.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { computed, nextTick, onUnmounted, provide, ref, shallowRef, toRef, watch, watchEffect } from 'vue';
import { isActionItem } from "./types.js";
import { genericComponent, omit, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVCommandPaletteProps = propsFactory({
modelValue: Boolean,
search: String,
items: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: '$vuetify.command.search'
},
inputIcon: {
type: String,
default: '$search'
},
hotkey: String,
closeOnSelect: {
type: Boolean,
default: true
},
noDataText: {
type: String,
default: '$vuetify.noDataText'
},
listProps: Object,
...makeFilterProps({
filterKeys: ['title', 'subtitle']
}),
...makeDensityProps(),
...omit(makeVDialogProps({
maxWidth: 500
}), ['modelValue'])
}, 'VCommandPalette');
export const VCommandPalette = genericComponent()({
name: 'VCommandPalette',
props: makeVCommandPaletteProps(),
emits: {
'update:modelValue': value => true,
'update:search': value => true,
'click:item': (item, event) => true,
'before-select': payload => true
},
setup(props, {
emit,
slots
}) {
const {
t
} = useLocale();
const isOpen = useProxiedModel(props, 'modelValue');
const searchQuery = useProxiedModel(props, 'search');
const searchInputRef = ref();
const dialogRef = ref();
const previouslyFocusedElement = shallowRef(null);
const internalItems = computed(() => props.items.map((item, index) => ({
value: index,
type: item.type,
raw: item,
...('title' in item && {
title: item.title
}),
...('subtitle' in item && {
subtitle: item.subtitle
})
})));
const {
filteredItems: filterResult
} = useFilter(props, internalItems, searchQuery);
const filteredItems = computed(() => filterResult.value.map(item => item.raw));
const itemsForList = computed(() => {
return filteredItems.value.map((item, idx) => ({
...item,
value: idx
}));
});
function executeItem(item, event) {
if ('onClick' in item && item.onClick) {
item.onClick(event, item.value);
}
emit('click:item', item, event);
if (!isActionItem(item) || !props.closeOnSelect) return;
let shouldClose = true;
emit('before-select', {
item,
event,
preventDefault: () => {
shouldClose = false;
}
});
if (shouldClose) {
isOpen.value = false;
}
}
const navigation = useCommandPaletteNavigation({
filteredItems,
onItemClick: (item, event) => {
executeItem(item, event);
}
});
provide(VCommandPaletteSymbol, {
items: computed(() => props.items),
filteredItems,
selectedIndex: navigation.selectedIndex,
search: searchQuery,
setSelectedIndex: navigation.setSelectedIndex
});
// Register main hotkey with cleanup - using toRef for reactivity
const hotkeyUnsubscribe = useHotkey(toRef(props, 'hotkey'), () => {
isOpen.value = !isOpen.value;
});
watchEffect(onCleanup => {
if (!isOpen.value) {
return;
}
const hotkeyUnsubscribes = [];
function registerItemHotkeys(items) {
items.forEach(item => {
if (isActionItem(item) && item.hotkey) {
const unsubscribe = useHotkey(item.hotkey, event => {
event.preventDefault();
executeItem(item, event);
}, {
inputs: true
});
hotkeyUnsubscribes.push(unsubscribe);
}
});
}
registerItemHotkeys(props.items);
onCleanup(() => {
hotkeyUnsubscribes.forEach(unsubscribe => unsubscribe?.());
});
});
function findNextSelectableIndex(startIndex, direction) {
const items = filteredItems.value;
if (items.length === 0) return -1;
let index = startIndex;
const maxIterations = items.length;
for (let i = 0; i < maxIterations; i++) {
index += direction;
if (index >= items.length) index = 0;
if (index < 0) index = items.length - 1;
if (isActionItem(items[index])) {
return index;
}
}
return -1;
}
function handleSearchKeydown(e) {
switch (e.key) {
case 'ArrowDown':
{
e.preventDefault();
const nextIndex = findNextSelectableIndex(navigation.selectedIndex.value, 1);
if (nextIndex !== -1) {
navigation.setSelectedIndex(nextIndex);
}
break;
}
case 'ArrowUp':
{
e.preventDefault();
const prevIndex = findNextSelectableIndex(navigation.selectedIndex.value, -1);
if (prevIndex !== -1) {
navigation.setSelectedIndex(prevIndex);
}
break;
}
case 'Enter':
e.preventDefault();
navigation.executeSelected(e);
break;
case 'Escape':
e.preventDefault();
isOpen.value = false;
break;
}
}
watch(isOpen, (newValue, oldValue) => {
if (newValue && !oldValue) {
previouslyFocusedElement.value = document.activeElement;
searchQuery.value = '';
navigation.reset();
// Use requestAnimationFrame to ensure DOM is fully rendered
nextTick(() => {
requestAnimationFrame(() => {
if (searchInputRef.value && typeof searchInputRef.value.focus === 'function') {
searchInputRef.value.focus();
}
});
});
} else if (!newValue && oldValue) {
nextTick(() => {
previouslyFocusedElement.value?.focus({
preventScroll: true
});
previouslyFocusedElement.value = null;
});
}
}, {
immediate: true
});
onUnmounted(() => {
hotkeyUnsubscribe();
previouslyFocusedElement.value = null;
});
useRender(() => {
const dialogProps = VDialog.filterProps(omit(props, ['modelValue', 'class', 'style']));
return _createVNode(VDialog, _mergeProps({
"ref": dialogRef,
"class": "v-command-palette",
"modelValue": isOpen.value,
"onUpdate:modelValue": $event => isOpen.value = $event,
"scrollable": true
}, dialogProps), {
activator: slots.activator,
default: () => _createVNode(VSheet, {
"class": _normalizeClass(props.class),
"style": _normalizeStyle(props.style)
}, {
default: () => [slots.prepend?.(), _createElementVNode("div", {
"class": "v-command-palette__input-container"
}, [slots.input?.() ?? _createVNode(VTextField, {
"ref": searchInputRef,
"modelValue": searchQuery.value,
"onUpdate:modelValue": $event => searchQuery.value = $event,
"density": props.density,
"placeholder": t(props.placeholder),
"prependInnerIcon": props.inputIcon,
"autocomplete": "off",
"autofocus": true,
"singleLine": true,
"hideDetails": true,
"variant": "solo",
"flat": true,
"bgColor": "transparent",
"onKeydown": handleSearchKeydown
}, {
'append-inner': slots['input.append-inner']
})]), _createElementVNode("div", {
"class": "v-command-palette__content"
}, [filteredItems.value.length > 0 ? _createVNode(VList, _mergeProps({
"key": "list",
"class": "v-command-palette__list",
"density": props.density,
"items": itemsForList.value,
"itemType": "type",
"itemProps": true,
"activatable": true
}, props.listProps, {
"navigationStrategy": "track",
"navigationIndex": navigation.selectedIndex.value,
"onUpdate:navigationIndex": navigation.setSelectedIndex
}), {
prepend: slots['list.prepend'],
subheader: slots['list.subheader'],
item: ({
props: itemProps
}) => slots.item?.({
item: itemProps,
index: itemProps.index
}) ?? _createVNode(VCommandPaletteItem, {
"key": `item-${itemProps.index}`,
"item": itemProps,
"index": itemProps.index,
"onExecute": event => navigation.execute(itemProps.index, event)
}, {
prepend: slots['item.prepend'] ? () => slots['item.prepend']?.({
item: itemProps,
index: itemProps.index
}) : undefined,
title: slots['item.title'] ? () => slots['item.title']?.({
item: itemProps,
index: itemProps.index
}) : undefined,
append: slots['item.append'] ? () => slots['item.append']?.({
item: itemProps,
index: itemProps.index
}) : undefined
})
}) : _createElementVNode("div", {
"key": "no-data",
"class": "v-command-palette__no-data"
}, [slots['no-data']?.() || t(props.noDataText)])]), slots.append?.()]
})
});
});
}
});
//# sourceMappingURL=VCommandPalette.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
@use '../../styles/tools';
@use './variables' as *;
@include tools.layer('components') {
.v-command-palette {
> .v-overlay__content > .v-sheet {
display: flex;
flex: 1 1 100%;
flex-direction: column;
}
@at-root {
@include tools.density('v-command-palette', $command-palette-density) using ($modifier) {
.v-command-palette__input-container {
padding-block: $command-palette-input-padding-block + $modifier;
padding-inline: $command-palette-input-padding-inline + $modifier;
}
}
}
&__content {
overflow-y: auto;
}
&__no-data {
opacity: $command-palette-no-data-opacity;
padding: $command-palette-no-data-padding;
text-align: center;
}
&__list {
.v-list-subheader {
opacity: $command-palette-subheader-opacity;
user-select: none;
}
.v-divider {
margin-block: $command-palette-divider-margin;
}
}
}
}
@@ -0,0 +1,157 @@
import type { PropType } from 'vue';
import type { VCommandPaletteActionItem } from './types.js';
export declare const makeVCommandPaletteItemProps: <Defaults extends {
item?: unknown;
index?: unknown;
onExecute?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
item: unknown extends Defaults["item"] ? {
type: PropType<VCommandPaletteActionItem>;
required: true;
} : Omit<{
type: PropType<VCommandPaletteActionItem>;
required: true;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["item"] ? VCommandPaletteActionItem : VCommandPaletteActionItem | Defaults["item"]>;
default: unknown extends Defaults["item"] ? VCommandPaletteActionItem : VCommandPaletteActionItem | Defaults["item"];
};
index: unknown extends Defaults["index"] ? {
type: NumberConstructor;
required: true;
} : Omit<{
type: NumberConstructor;
required: true;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["index"] ? number : number | Defaults["index"]>;
default: unknown extends Defaults["index"] ? number : number | Defaults["index"];
};
onExecute: unknown extends Defaults["onExecute"] ? PropType<(event: MouseEvent | KeyboardEvent) => void> : {
type: PropType<unknown extends Defaults["onExecute"] ? (event: MouseEvent | KeyboardEvent) => void : ((event: MouseEvent | KeyboardEvent) => void) | Defaults["onExecute"]>;
default: unknown extends Defaults["onExecute"] ? (event: MouseEvent | KeyboardEvent) => void : ((event: MouseEvent | KeyboardEvent) => void) | Defaults["onExecute"];
};
};
export type VCommandPaletteItemSlots = {
prepend: never;
title: never;
append: never;
};
export declare const VCommandPaletteItem: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
item: VCommandPaletteActionItem;
index: number;
} & {
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
} & {
$children?: {
prepend?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
prepend?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, void, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {}, true, {}, import("vue").SlotsType<Partial<{
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
item: VCommandPaletteActionItem;
index: number;
} & {
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
} & {
$children?: {
prepend?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
prepend?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, {}, {}, {}, {}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
item: VCommandPaletteActionItem;
index: number;
} & {
onExecute?: ((event: MouseEvent | KeyboardEvent) => void) | undefined;
} & {
$children?: {
prepend?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
append?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
prepend?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
append?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:append"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, void, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {}, {}, string, import("vue").SlotsType<Partial<{
prepend: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
append: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
item: {
type: PropType<VCommandPaletteActionItem>;
required: true;
};
index: {
type: NumberConstructor;
required: true;
};
onExecute: PropType<(event: MouseEvent | KeyboardEvent) => void>;
}, import("vue").ExtractPropTypes<{
item: {
type: PropType<VCommandPaletteActionItem>;
required: true;
};
index: {
type: NumberConstructor;
required: true;
};
onExecute: PropType<(event: MouseEvent | KeyboardEvent) => void>;
}>>;
export type VCommandPaletteItem = InstanceType<typeof VCommandPaletteItem>;
@@ -0,0 +1,42 @@
import { createVNode as _createVNode } from "vue";
// Components
import { VHotkey } from "../../components/VHotkey/index.js";
import { VListItem } from "../../components/VList/index.js"; // Utilities
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVCommandPaletteItemProps = propsFactory({
item: {
type: Object,
required: true
},
index: {
type: Number,
required: true
},
onExecute: Function
}, 'VCommandPaletteItem');
export const VCommandPaletteItem = genericComponent()({
name: 'VCommandPaletteItem',
props: makeVCommandPaletteItemProps(),
setup(props, {
slots
}) {
useRender(() => _createVNode(VListItem, {
"index": props.index,
"value": props.item.value,
"title": props.item.title,
"subtitle": props.item.subtitle,
"prependIcon": props.item.prependIcon,
"prependAvatar": props.item.prependAvatar,
"appendIcon": props.item.appendIcon,
"appendAvatar": props.item.appendAvatar,
"onClick": props.onExecute
}, {
prepend: slots.prepend,
title: slots.title,
append: slots.append ?? (props.item.hotkey ? () => _createVNode(VHotkey, {
"keys": props.item.hotkey
}, null) : undefined)
}));
}
});
//# sourceMappingURL=VCommandPaletteItem.js.map
@@ -0,0 +1 @@
{"version":3,"file":"VCommandPaletteItem.js","names":["VHotkey","VListItem","genericComponent","propsFactory","useRender","makeVCommandPaletteItemProps","item","type","Object","required","index","Number","onExecute","Function","VCommandPaletteItem","name","props","setup","slots","_createVNode","value","title","subtitle","prependIcon","prependAvatar","appendIcon","appendAvatar","prepend","append","hotkey","undefined"],"sources":["../../../src/labs/VCommandPalette/VCommandPaletteItem.tsx"],"sourcesContent":["// Components\nimport { VHotkey } from '@/components/VHotkey'\nimport { VListItem } from '@/components/VList'\n\n// Utilities\nimport { genericComponent, propsFactory, useRender } from '@/util'\n\n// Types\nimport type { PropType } from 'vue'\nimport type { VCommandPaletteActionItem } from './types'\n\nexport const makeVCommandPaletteItemProps = propsFactory({\n item: {\n type: Object as PropType<VCommandPaletteActionItem>,\n required: true,\n },\n index: {\n type: Number,\n required: true,\n },\n onExecute: Function as PropType<(event: MouseEvent | KeyboardEvent) => void>,\n}, 'VCommandPaletteItem')\n\nexport type VCommandPaletteItemSlots = {\n prepend: never\n title: never\n append: never\n}\n\nexport const VCommandPaletteItem = genericComponent<VCommandPaletteItemSlots>()({\n name: 'VCommandPaletteItem',\n\n props: makeVCommandPaletteItemProps(),\n\n setup (props, { slots }) {\n useRender(() => (\n <VListItem\n index={ props.index }\n value={ props.item.value }\n title={ props.item.title }\n subtitle={ props.item.subtitle }\n prependIcon={ props.item.prependIcon }\n prependAvatar={ props.item.prependAvatar }\n appendIcon={ props.item.appendIcon }\n appendAvatar={ props.item.appendAvatar }\n onClick={ props.onExecute }\n v-slots={{\n prepend: slots.prepend,\n title: slots.title,\n append: slots.append ?? (props.item.hotkey ? () => <VHotkey keys={ props.item.hotkey } /> : undefined),\n }}\n />\n ))\n },\n})\n\nexport type VCommandPaletteItem = InstanceType<typeof VCommandPaletteItem>\n"],"mappings":";AAAA;AAAA,SACSA,OAAO;AAAA,SACPC,SAAS,2CAElB;AAAA,SACSC,gBAAgB,EAAEC,YAAY,EAAEC,SAAS,+BAElD;AAIA,OAAO,MAAMC,4BAA4B,GAAGF,YAAY,CAAC;EACvDG,IAAI,EAAE;IACJC,IAAI,EAAEC,MAA6C;IACnDC,QAAQ,EAAE;EACZ,CAAC;EACDC,KAAK,EAAE;IACLH,IAAI,EAAEI,MAAM;IACZF,QAAQ,EAAE;EACZ,CAAC;EACDG,SAAS,EAAEC;AACb,CAAC,EAAE,qBAAqB,CAAC;AAQzB,OAAO,MAAMC,mBAAmB,GAAGZ,gBAAgB,CAA2B,CAAC,CAAC;EAC9Ea,IAAI,EAAE,qBAAqB;EAE3BC,KAAK,EAAEX,4BAA4B,CAAC,CAAC;EAErCY,KAAKA,CAAED,KAAK,EAAE;IAAEE;EAAM,CAAC,EAAE;IACvBd,SAAS,CAAC,MAAAe,YAAA,CAAAlB,SAAA;MAAA,SAEEe,KAAK,CAACN,KAAK;MAAA,SACXM,KAAK,CAACV,IAAI,CAACc,KAAK;MAAA,SAChBJ,KAAK,CAACV,IAAI,CAACe,KAAK;MAAA,YACbL,KAAK,CAACV,IAAI,CAACgB,QAAQ;MAAA,eAChBN,KAAK,CAACV,IAAI,CAACiB,WAAW;MAAA,iBACpBP,KAAK,CAACV,IAAI,CAACkB,aAAa;MAAA,cAC3BR,KAAK,CAACV,IAAI,CAACmB,UAAU;MAAA,gBACnBT,KAAK,CAACV,IAAI,CAACoB,YAAY;MAAA,WAC5BV,KAAK,CAACJ;IAAS,GAChB;MACPe,OAAO,EAAET,KAAK,CAACS,OAAO;MACtBN,KAAK,EAAEH,KAAK,CAACG,KAAK;MAClBO,MAAM,EAAEV,KAAK,CAACU,MAAM,KAAKZ,KAAK,CAACV,IAAI,CAACuB,MAAM,GAAG,MAAAV,YAAA,CAAAnB,OAAA;QAAA,QAAsBgB,KAAK,CAACV,IAAI,CAACuB;MAAM,QAAK,GAAGC,SAAS;IACvG,CAAC,CAEJ,CAAC;EACJ;AACF,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,7 @@
$command-palette-density: ('default': 0, 'comfortable': -1, 'compact': -2) !default;
$command-palette-input-padding-block: 8px !default;
$command-palette-input-padding-inline: 16px !default;
$command-palette-no-data-padding: 16px !default;
$command-palette-no-data-opacity: 0.6 !default;
$command-palette-subheader-opacity: 0.7 !default;
$command-palette-divider-margin: 4px !default;
@@ -0,0 +1,15 @@
import type { ComputedRef, Ref } from 'vue';
import type { VCommandPaletteItem } from '../types.js';
export interface UseCommandPaletteNavigationOptions {
filteredItems: ComputedRef<VCommandPaletteItem[]>;
onItemClick: (item: VCommandPaletteItem, event: KeyboardEvent | MouseEvent) => void;
}
export interface UseCommandPaletteNavigationReturn {
selectedIndex: Readonly<Ref<number>>;
getSelectedItem: () => VCommandPaletteItem | undefined;
execute: (index: number, event: KeyboardEvent | MouseEvent) => void;
executeSelected: (event: KeyboardEvent | MouseEvent) => void;
reset: () => void;
setSelectedIndex: (index: number) => void;
}
export declare function useCommandPaletteNavigation(options: UseCommandPaletteNavigationOptions): UseCommandPaletteNavigationReturn;
@@ -0,0 +1,89 @@
// Utilities
import { readonly, ref, shallowRef, watch } from 'vue';
// Types
import { isActionItem } from "../types.js";
function getItemKey(item) {
if (!isActionItem(item)) return undefined;
return item.value !== undefined ? String(item.value) : item.title;
}
function findFirstSelectableIndex(items) {
return items.findIndex(item => isActionItem(item));
}
export function useCommandPaletteNavigation(options) {
const selectedIndex = ref(0);
const selectedItemKey = shallowRef(undefined);
watch(() => options.filteredItems.value, (newItems, oldItems) => {
if (newItems.length === 0) {
selectedIndex.value = -1;
selectedItemKey.value = undefined;
return;
}
if (selectedItemKey.value !== undefined) {
const newIndex = newItems.findIndex(item => isActionItem(item) && getItemKey(item) === selectedItemKey.value);
if (newIndex !== -1) {
selectedIndex.value = newIndex;
return;
}
}
const firstSelectableIndex = findFirstSelectableIndex(newItems);
if (firstSelectableIndex !== -1) {
selectedIndex.value = firstSelectableIndex;
selectedItemKey.value = getItemKey(newItems[firstSelectableIndex]);
return;
}
selectedIndex.value = 0;
selectedItemKey.value = undefined;
}, {
immediate: true
});
function getSelectedItem() {
return options.filteredItems.value[selectedIndex.value];
}
function execute(index, event) {
const item = options.filteredItems.value[index];
if (item) {
options.onItemClick(item, event);
}
}
function executeSelected(event) {
const item = getSelectedItem();
if (item) {
options.onItemClick(item, event);
}
}
function reset() {
const items = options.filteredItems.value;
if (items.length === 0) {
selectedIndex.value = -1;
selectedItemKey.value = undefined;
return;
}
const firstSelectableIndex = findFirstSelectableIndex(items);
if (firstSelectableIndex !== -1) {
selectedIndex.value = firstSelectableIndex;
selectedItemKey.value = getItemKey(items[firstSelectableIndex]);
return;
}
selectedIndex.value = 0;
selectedItemKey.value = undefined;
}
function setSelectedIndex(index) {
// Ignore VList's reset to -1 when we have items - we manage selection on filter changes
if (index === -1 && options.filteredItems.value.length > 0) {
return;
}
selectedIndex.value = index;
const item = options.filteredItems.value[index];
selectedItemKey.value = item ? getItemKey(item) : undefined;
}
return {
selectedIndex: readonly(selectedIndex),
getSelectedItem,
execute,
executeSelected,
reset,
setSelectedIndex
};
}
//# sourceMappingURL=useCommandPaletteNavigation.js.map
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
export { VCommandPalette } from './VCommandPalette.js';
export { VCommandPaletteItem } from './VCommandPaletteItem.js';
+3
View File
@@ -0,0 +1,3 @@
export { VCommandPalette } from "./VCommandPalette.js";
export { VCommandPaletteItem } from "./VCommandPaletteItem.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VCommandPalette","VCommandPaletteItem"],"sources":["../../../src/labs/VCommandPalette/index.ts"],"sourcesContent":["export { VCommandPalette } from './VCommandPalette'\nexport { VCommandPaletteItem } from './VCommandPaletteItem'\n"],"mappings":"SAASA,eAAe;AAAA,SACfC,mBAAmB","ignoreList":[]}
+11
View File
@@ -0,0 +1,11 @@
import type { InjectionKey, Ref } from 'vue';
import type { VCommandPaletteItem } from './types.js';
export interface CommandPaletteProvide {
items: Ref<VCommandPaletteItem[]>;
filteredItems: Ref<VCommandPaletteItem[]>;
selectedIndex: Ref<number>;
search: Ref<string>;
setSelectedIndex: (index: number) => void;
}
export declare const VCommandPaletteSymbol: InjectionKey<CommandPaletteProvide>;
export declare function useCommandPalette(): CommandPaletteProvide;
+14
View File
@@ -0,0 +1,14 @@
// Utilities
import { inject } from 'vue';
// Types
export const VCommandPaletteSymbol = Symbol.for('vuetify:command-palette');
export function useCommandPalette() {
const commandPalette = inject(VCommandPaletteSymbol);
if (!commandPalette) {
throw new Error('[Vuetify] useCommandPalette must be used within VCommandPalette');
}
return commandPalette;
}
//# sourceMappingURL=shared.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"shared.js","names":["inject","VCommandPaletteSymbol","Symbol","for","useCommandPalette","commandPalette","Error"],"sources":["../../../src/labs/VCommandPalette/shared.ts"],"sourcesContent":["// Utilities\nimport { inject } from 'vue'\n\n// Types\nimport type { InjectionKey, Ref } from 'vue'\nimport type { VCommandPaletteItem } from './types'\n\nexport interface CommandPaletteProvide {\n items: Ref<VCommandPaletteItem[]>\n filteredItems: Ref<VCommandPaletteItem[]>\n selectedIndex: Ref<number>\n search: Ref<string>\n setSelectedIndex: (index: number) => void\n}\n\nexport const VCommandPaletteSymbol: InjectionKey<CommandPaletteProvide> = Symbol.for('vuetify:command-palette')\n\nexport function useCommandPalette () {\n const commandPalette = inject(VCommandPaletteSymbol)\n\n if (!commandPalette) {\n throw new Error('[Vuetify] useCommandPalette must be used within VCommandPalette')\n }\n\n return commandPalette\n}\n"],"mappings":"AAAA;AACA,SAASA,MAAM,QAAQ,KAAK;;AAE5B;;AAYA,OAAO,MAAMC,qBAA0D,GAAGC,MAAM,CAACC,GAAG,CAAC,yBAAyB,CAAC;AAE/G,OAAO,SAASC,iBAAiBA,CAAA,EAAI;EACnC,MAAMC,cAAc,GAAGL,MAAM,CAACC,qBAAqB,CAAC;EAEpD,IAAI,CAACI,cAAc,EAAE;IACnB,MAAM,IAAIC,KAAK,CAAC,iEAAiE,CAAC;EACpF;EAEA,OAAOD,cAAc;AACvB","ignoreList":[]}
+32
View File
@@ -0,0 +1,32 @@
// @ts-ignore
import type { RouteLocationRaw } from 'vue-router';
export interface BaseVListItem {
title?: string;
subtitle?: string;
prependIcon?: string;
appendIcon?: string;
prependAvatar?: string;
appendAvatar?: string;
}
interface NavigableItemProps {
to?: RouteLocationRaw;
href?: string;
}
export interface VCommandPaletteActionItem extends BaseVListItem, NavigableItemProps {
type?: 'item';
onClick?: (event: MouseEvent | KeyboardEvent, value?: any) => void;
value?: any;
hotkey?: string;
}
export interface VCommandPaletteSubheader {
type: 'subheader';
title: string;
inset?: boolean;
}
export interface VCommandPaletteDivider {
type: 'divider';
inset?: boolean;
}
export type VCommandPaletteItem = VCommandPaletteActionItem | VCommandPaletteSubheader | VCommandPaletteDivider;
export declare function isActionItem(item: any): item is VCommandPaletteActionItem;
+6
View File
@@ -0,0 +1,6 @@
// Types
export function isActionItem(item) {
return !item.type || item.type === 'item';
}
//# sourceMappingURL=types.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.js","names":["isActionItem","item","type"],"sources":["../../../src/labs/VCommandPalette/types.ts"],"sourcesContent":["// Types\nimport type { RouteLocationRaw } from 'vue-router'\n\nexport interface BaseVListItem {\n title?: string\n subtitle?: string\n prependIcon?: string\n appendIcon?: string\n prependAvatar?: string\n appendAvatar?: string\n}\n\ninterface NavigableItemProps {\n to?: RouteLocationRaw\n href?: string\n}\n\nexport interface VCommandPaletteActionItem extends BaseVListItem, NavigableItemProps {\n type?: 'item'\n onClick?: (event: MouseEvent | KeyboardEvent, value?: any) => void\n value?: any\n hotkey?: string\n}\n\nexport interface VCommandPaletteSubheader {\n type: 'subheader'\n title: string\n inset?: boolean\n}\n\nexport interface VCommandPaletteDivider {\n type: 'divider'\n inset?: boolean\n}\n\nexport type VCommandPaletteItem =\n | VCommandPaletteActionItem\n | VCommandPaletteSubheader\n | VCommandPaletteDivider\n\nexport function isActionItem (item: any): item is VCommandPaletteActionItem {\n return !item.type || item.type === 'item'\n}\n"],"mappings":"AAAA;;AAwCA,OAAO,SAASA,YAAYA,CAAEC,IAAS,EAAqC;EAC1E,OAAO,CAACA,IAAI,CAACC,IAAI,IAAID,IAAI,CAACC,IAAI,KAAK,MAAM;AAC3C","ignoreList":[]}
File diff suppressed because one or more lines are too long
+285
View File
@@ -0,0 +1,285 @@
import { Fragment as _Fragment, mergeProps as _mergeProps, createVNode as _createVNode, createElementVNode as _createElementVNode } from "vue";
// Components
import { makeVConfirmEditProps, VConfirmEdit } from "../../components/VConfirmEdit/VConfirmEdit.js";
import { makeVDatePickerProps, VDatePicker } from "../../components/VDatePicker/VDatePicker.js";
import { useInputIcon } from "../../components/VInput/InputIcon.js";
import { VMenu } from "../../components/VMenu/VMenu.js";
import { makeVTextFieldProps, VTextField } from "../../components/VTextField/VTextField.js"; // Composables
import { useCalendarRange } from "../../composables/calendar.js";
import { useDate } from "../../composables/date/index.js";
import { createDateRange } from "../../composables/date/date.js";
import { makeDateFormatProps, useDateFormat } from "../../composables/dateFormat.js";
import { makeDisplayProps, useDisplay } from "../../composables/display.js";
import { makeFocusProps } from "../../composables/focus.js";
import { forwardRefs } from "../../composables/forwardRefs.js";
import { useLocale } from "../../composables/locale.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { computed, ref, shallowRef, watch } from 'vue';
import { genericComponent, omit, pick, propsFactory, useRender, wrapInArray } from "../../util/index.js"; // Types
// Types
export const makeVDateInputProps = propsFactory({
displayFormat: {
type: [Function, String],
default: undefined
},
location: {
type: String,
default: 'bottom start'
},
menu: Boolean,
menuProps: Object,
updateOn: {
type: Array,
default: () => ['blur', 'enter']
},
pickerProps: Object,
...makeDateFormatProps(),
...makeDisplayProps({
mobile: null
}),
...makeFocusProps(),
...makeVConfirmEditProps({
hideActions: true
}),
...makeVTextFieldProps({
prependIcon: '$calendar'
}),
...omit(makeVDatePickerProps({
hideHeader: true,
showAdjacentMonths: true
}), ['location', 'rounded', 'height', 'minHeight', 'maxHeight'])
}, 'VDateInput');
export const VDateInput = genericComponent()({
name: 'VDateInput',
props: makeVDateInputProps(),
emits: {
save: value => true,
cancel: () => true,
'update:focused': val => true,
'update:modelValue': val => true,
'update:menu': val => true
},
setup(props, {
emit,
slots
}) {
const {
t,
current: currentLocale
} = useLocale();
const adapter = useDate();
const {
isValid,
parseDate,
formatDate,
parserFormat
} = useDateFormat(props, currentLocale);
const {
mobile
} = useDisplay(props);
const {
InputIcon
} = useInputIcon(props);
const {
clampDate,
isInAllowedRange
} = useCalendarRange(props);
const emptyModelValue = () => props.multiple ? [] : null;
const model = useProxiedModel(props, 'modelValue', emptyModelValue(), val => Array.isArray(val) ? val.map(item => adapter.toJsDate(item)) : val ? adapter.toJsDate(val) : val, val => Array.isArray(val) ? val.map(item => adapter.date(item)) : val ? adapter.date(val) : val);
const menu = useProxiedModel(props, 'menu');
const isEditingInput = shallowRef(false);
const isFocused = shallowRef(props.focused);
const vTextFieldRef = ref();
const disabledActions = ref(['save']);
function format(date) {
if (typeof props.displayFormat === 'function') {
return props.displayFormat(date);
}
if (props.displayFormat) {
return adapter.format(date, props.displayFormat ?? 'keyboardDate');
}
return formatDate(date);
}
const display = computed(() => {
const value = wrapInArray(model.value);
if (!value.length) return null;
if (props.multiple === true) {
return t('$vuetify.datePicker.itemsSelected', value.length);
}
if (props.multiple === 'range') {
const start = value[0];
const end = value[value.length - 1];
if (!adapter.isValid(start) || !adapter.isValid(end)) return '';
return `${format(adapter.date(start))} - ${format(adapter.date(end))}`;
}
return adapter.isValid(model.value) ? format(adapter.date(model.value)) : '';
});
const inputmode = computed(() => {
if (!mobile.value) return undefined;
if (isEditingInput.value) return 'text';
return 'none';
});
const isInteractive = computed(() => !props.disabled && !props.readonly);
const isReadonly = computed(() => {
if (!props.updateOn.length) return true;
return !(mobile.value && isEditingInput.value) && props.readonly;
});
watch(menu, val => {
if (val) return;
isEditingInput.value = false;
disabledActions.value = ['save'];
});
function onKeydown(e) {
if (e.key !== 'Enter') return;
if (!menu.value || !isFocused.value) {
menu.value = true;
}
if (props.updateOn.includes('enter') && !props.readonly) {
onUserInput(e.target);
}
}
function onClick(e) {
if (props.disabled) return;
e.preventDefault();
e.stopPropagation();
if (menu.value && mobile.value) {
isEditingInput.value = !props.readonly;
} else {
menu.value = true;
}
}
function onCancel() {
emit('cancel');
menu.value = false;
isEditingInput.value = false;
}
function onSave(value) {
emit('save', value);
menu.value = false;
}
function onUpdateDisplayModel(value) {
if (value != null) return;
model.value = emptyModelValue();
}
function onBlur(e) {
if (props.updateOn.includes('blur') && !props.readonly) {
onUserInput(e.target);
}
// When in mobile mode and editing is done (due to keyboard dismissal), close the menu
if (mobile.value && isEditingInput.value && !isFocused.value) {
menu.value = false;
isEditingInput.value = false;
}
}
function onUserInput({
value
}) {
if (!value.trim()) {
model.value = emptyModelValue();
} else if (!props.multiple) {
if (isValid(value)) {
model.value = clampDate(parseDate(value));
}
} else {
const parts = value.trim().split(/\D+-\D+|[^\d\-/.]+/);
if (parts.every(isValid)) {
if (props.multiple === 'range') {
const [start, stop] = parts.map(parseDate).map(clampDate).toSorted((a, b) => adapter.isAfter(a, b) ? 1 : -1);
model.value = createDateRange(adapter, start, stop);
} else {
model.value = parts.map(parseDate).filter(isInAllowedRange);
}
}
}
}
useRender(() => {
const hasPrepend = !!(props.prependIcon || slots.prepend);
const confirmEditProps = VConfirmEdit.filterProps(props);
const datePickerProps = {
...VDatePicker.filterProps(omit(props, ['active', 'bgColor', 'color', 'location', 'rounded', 'maxWidth', 'minWidth', 'width'])),
...props.pickerProps
};
const datePickerSlots = pick(slots, ['title', 'header', 'day', 'month', 'year']);
const textFieldProps = VTextField.filterProps(omit(props, ['placeholder']));
return _createVNode(VTextField, _mergeProps({
"ref": vTextFieldRef
}, textFieldProps, {
"class": ['v-date-input', props.class],
"style": props.style,
"modelValue": display.value,
"inputmode": inputmode.value,
"placeholder": props.placeholder ?? parserFormat.value,
"readonly": isReadonly.value,
"onKeydown": isInteractive.value ? onKeydown : undefined,
"focused": menu.value || isFocused.value,
"onBlur": onBlur,
"validationValue": model.value,
"onClick:control": onClick,
"onUpdate:modelValue": onUpdateDisplayModel,
"onUpdate:focused": event => isFocused.value = event
}), {
...slots,
default: () => _createElementVNode(_Fragment, null, [_createVNode(VMenu, _mergeProps({
"modelValue": menu.value,
"onUpdate:modelValue": $event => menu.value = $event,
"activator": "parent",
"minWidth": "0",
"eager": isFocused.value,
"location": props.location,
"closeOnContentClick": false,
"openOnClick": false
}, props.menuProps), {
default: () => [_createVNode(VConfirmEdit, _mergeProps(confirmEditProps, {
"modelValue": model.value,
"onUpdate:modelValue": $event => model.value = $event,
"disabled": disabledActions.value,
"onSave": onSave,
"onCancel": onCancel
}), {
default: ({
actions,
model: proxyModel,
save,
cancel,
isPristine
}) => {
function onUpdateModel(value) {
if (!props.hideActions) {
proxyModel.value = value;
} else {
model.value = value;
if (!props.multiple) {
menu.value = false;
}
}
emit('save', value);
disabledActions.value = [];
}
return _createVNode(VDatePicker, _mergeProps(datePickerProps, {
"modelValue": props.hideActions ? model.value : proxyModel.value,
"onUpdate:modelValue": value => onUpdateModel(value),
"onMousedown": e => e.preventDefault()
}), {
...datePickerSlots,
actions: !props.hideActions ? () => slots.actions?.({
save,
cancel,
isPristine
}) ?? actions() : undefined
});
}
})]
}), slots.default?.()]),
prepend: hasPrepend ? prependSlotProps => slots.prepend ? slots.prepend(prependSlotProps) : props.prependIcon && _createVNode(InputIcon, {
"key": "prepend-icon",
"name": "prepend",
"tabindex": props['onClick:prepend'] ? undefined : -1,
"onClick": isInteractive.value ? onClick : undefined
}, null) : undefined
});
});
return forwardRefs({}, vTextFieldRef);
}
});
//# sourceMappingURL=VDateInput.js.map
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
export { VDateInput } from './VDateInput.js';
+2
View File
@@ -0,0 +1,2 @@
export { VDateInput } from "./VDateInput.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VDateInput"],"sources":["../../../src/labs/VDateInput/index.ts"],"sourcesContent":["export { VDateInput } from './VDateInput'\n"],"mappings":"SAASA,UAAU","ignoreList":[]}
+96
View File
@@ -0,0 +1,96 @@
@layer vuetify-components {
.v-file-upload .v-input__control {
flex-direction: column;
}
.v-file-upload-dropzone {
padding: 64px 16px;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
}
.v-file-upload-dropzone.v-sheet {
display: flex;
border-radius: 4px;
border-style: dashed;
border-width: 2px;
}
.v-file-upload-dropzone.v-file-upload-dropzone--density-compact {
padding: 32px 0;
flex-direction: row;
gap: 1rem;
}
.v-file-upload-dropzone .v-overlay__scrim {
pointer-events: none;
}
.v-file-upload-dropzone--disabled {
pointer-events: none;
opacity: var(--v-disabled-opacity);
}
.v-file-upload-dropzone--dragging > * {
pointer-events: none;
}
.v-file-upload-dropzone--clickable {
cursor: pointer;
}
.v-file-upload-dropzone--has-files:not(.v-file-upload-dropzone--clickable) {
cursor: default;
}
.v-file-upload-dropzone--error.v-sheet {
border-color: rgb(var(--v-theme-error));
}
.v-file-upload-dropzone--inset {
padding: 16px;
}
.v-file-upload-dropzone input[type=file] {
left: 0;
opacity: 0;
position: absolute;
cursor: pointer;
top: 0;
z-index: -1;
}
.v-file-upload-title {
font-size: 1.5rem;
font-weight: 600;
text-align: center;
}
.v-file-upload-icon {
opacity: var(--v-medium-emphasis-opacity);
font-size: 3rem;
margin-bottom: 1rem;
}
.v-file-upload-dropzone--density-comfortable .v-file-upload-icon {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
.v-file-upload-dropzone--density-compact .v-file-upload-icon {
font-size: 2rem;
margin-bottom: 0rem;
}
.v-file-upload-divider {
align-items: center;
display: flex;
margin: 32px 0;
justify-content: center;
width: 100%;
}
.v-file-upload-divider .v-divider__wrapper {
max-width: 100%;
}
.v-file-upload-inset {
width: 100%;
}
.v-file-upload-inset__action {
display: flex;
justify-content: center;
width: 100%;
padding: 8px 0;
}
.v-file-upload-list {
margin: 16px 0;
}
.v-file-upload-item:not(:first-child) {
margin-top: 8px;
}
}
File diff suppressed because it is too large Load Diff
+216
View File
@@ -0,0 +1,216 @@
import { mergeProps as _mergeProps, createElementVNode as _createElementVNode, Fragment as _Fragment, createVNode as _createVNode } from "vue";
// Styles
import "./VFileUpload.css";
// Components
import { VFileUploadDropzone, VFileUploadKey } from "./VFileUploadDropzone.js";
import { VFileUploadList } from "./VFileUploadList.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVInputProps, VInput } from "../../components/VInput/VInput.js"; // Composables
import { makeFileFilterProps, useFileFilter } from "../../composables/fileFilter.js";
import { useFocus } from "../../composables/focus.js";
import { useForm } from "../../composables/form.js";
import { forwardRefs } from "../../composables/forwardRefs.js";
import { IconValue } from "../../composables/icons.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { provide, ref, toRef, watch } from 'vue';
import { filterInputAttrs, genericComponent, omit, propsFactory, useRender, wrapInArray } from "../../util/index.js"; // Types
export const makeVFileUploadProps = propsFactory({
browseText: {
type: String,
default: '$vuetify.fileUpload.browse'
},
dividerText: {
type: String,
default: '$vuetify.fileUpload.divider'
},
title: {
type: String,
default: '$vuetify.fileUpload.title'
},
subtitle: String,
icon: {
type: IconValue,
default: '$upload'
},
clearable: Boolean,
insetFileList: Boolean,
hideBrowse: Boolean,
multiple: Boolean,
scrim: {
type: [Boolean, String],
default: true
},
showSize: Boolean,
...makeFileFilterProps(),
...omit(makeVInputProps(), ['direction']),
modelValue: {
type: [Array, Object],
default: null,
validator: val => {
return wrapInArray(val).every(v => v != null && typeof v === 'object');
}
}
}, 'VFileUpload');
export const VFileUpload = genericComponent()({
name: 'VFileUpload',
inheritAttrs: false,
props: makeVFileUploadProps(),
emits: {
'update:modelValue': files => true,
'update:focused': focused => true,
rejected: files => true
},
setup(props, {
attrs,
emit,
slots
}) {
const {
filterAccepted
} = useFileFilter(props);
const {
isFocused
} = useFocus(props);
const form = useForm(props);
const model = useProxiedModel(props, 'modelValue', props.modelValue, val => wrapInArray(val), val => props.multiple || Array.isArray(props.modelValue) ? val : val[0]);
const vInputRef = ref();
const vDropzoneRef = ref();
const inputRef = ref(null);
const isError = toRef(() => vInputRef.value?.isValid === false);
provide(VFileUploadKey, {
files: model,
disabled: form.isDisabled,
error: isError,
onDrop,
onClickBrowse: onClick,
onClickRemove
});
watch(model, newValue => {
const hasModelReset = !Array.isArray(newValue) || !newValue.length;
if (hasModelReset && inputRef.value) {
inputRef.value.value = '';
}
});
function onDrop(files) {
selectAccepted(files);
}
function onFileSelection(e) {
if (!e.target || e.repack) return; // prevent loop
const target = e.target;
const selectedFiles = [...(target.files ?? [])];
if (!selectedFiles.length) return;
if (!props.filterByType) {
model.value = props.multiple ? [...model.value, ...selectedFiles] : selectedFiles;
} else {
selectAccepted(selectedFiles);
}
}
function selectAccepted(files) {
const dataTransfer = new DataTransfer();
const {
accepted,
rejected
} = filterAccepted(files);
if (rejected.length) {
emit('rejected', rejected);
}
for (const file of accepted) {
dataTransfer.items.add(file);
}
inputRef.value.files = dataTransfer.files;
const newFiles = [...dataTransfer.files];
model.value = props.multiple ? [...model.value, ...newFiles] : newFiles;
const event = new Event('change', {
bubbles: true
});
event.repack = true;
inputRef.value.dispatchEvent(event);
}
function onClick() {
inputRef.value?.click();
}
function onClickRemove(index) {
const newValue = model.value.filter((_, i) => i !== index);
model.value = newValue;
if (newValue.length > 0 || !inputRef.value) return;
inputRef.value.value = '';
}
useRender(() => {
const {
modelValue: _,
...inputProps
} = VInput.filterProps(props);
const {
modelValue: __,
...dropzoneProps
} = VFileUploadDropzone.filterProps(props);
const [rootAttrs, inputAttrs] = filterInputAttrs(attrs);
const expectsDirectory = attrs.webkitdirectory !== undefined && attrs.webkitdirectory !== false;
const acceptFallback = attrs.accept ? String(attrs.accept) : undefined;
const inputAccept = expectsDirectory ? undefined : props.filterByType ?? acceptFallback;
const inputNode = _createElementVNode("input", _mergeProps({
"ref": inputRef,
"type": "file",
"accept": inputAccept,
"disabled": props.disabled ?? undefined,
"multiple": props.multiple,
"name": props.name,
"onChange": onFileSelection
}, inputAttrs), null);
return _createVNode(VInput, _mergeProps({
"ref": vInputRef,
"modelValue": props.multiple ? model.value : model.value[0],
"onUpdate:modelValue": val => {
if (val == null || Array.isArray(val) && !val.length) {
model.value = [];
}
},
"class": ['v-file-upload', props.class],
"style": props.style,
"focused": isFocused.value
}, rootAttrs, inputProps), {
...slots,
default: () => {
return _createElementVNode(_Fragment, null, [slots.default ? _createElementVNode(_Fragment, null, [slots.default(), _createElementVNode("input", _mergeProps({
"ref": inputRef,
"type": "file",
"accept": inputAccept,
"disabled": props.disabled ?? undefined,
"multiple": props.multiple,
"name": props.name,
"style": "display: none;",
"onChange": onFileSelection
}, inputAttrs), null)]) : _createVNode(VFileUploadDropzone, _mergeProps({
"ref": vDropzoneRef
}, dropzoneProps), {
browse: slots.browse,
icon: slots.icon,
title: slots.title,
divider: slots.divider,
single: slots.single,
item: slots.item,
input: () => slots.input?.({
inputNode
}) ?? inputNode
}), !slots.default && !props.insetFileList && _createVNode(VDefaultsProvider, {
"defaults": {
VFileUploadList: {
clearable: props.clearable,
showSize: props.showSize
}
}
}, {
default: () => [_createVNode(VFileUploadList, null, {
item: slots.item
})]
})]);
}
});
});
return forwardRefs({
controlRef: inputRef
}, vInputRef, vDropzoneRef);
}
});
//# sourceMappingURL=VFileUpload.js.map
File diff suppressed because one or more lines are too long
+101
View File
@@ -0,0 +1,101 @@
@use '../../styles/tools'
@use '../../styles/settings'
@use './variables' as *
@include tools.layer('components')
.v-file-upload
.v-input__control
flex-direction: column
.v-file-upload-dropzone
padding: $file-upload-padding
flex-direction: column
justify-content: center
align-items: center
position: relative
&.v-sheet
display: flex
border-radius: 4px
border-style: dashed
border-width: 2px
&.v-file-upload-dropzone--density-compact
padding: 32px 0
flex-direction: row
gap: 1rem
.v-overlay__scrim
pointer-events: none
&--disabled
pointer-events: none
opacity: var(--v-disabled-opacity)
&--dragging
> *
pointer-events: none
&--clickable
cursor: pointer
&--has-files:not(&--clickable)
cursor: default
&--error.v-sheet
border-color: rgb(var(--v-theme-error))
&--inset
padding: 16px
input[type="file"]
left: 0
opacity: 0
position: absolute
cursor: pointer
top: 0
z-index: -1
.v-file-upload-title
font-size: $file-upload-title-font-size
font-weight: 600
text-align: center
.v-file-upload-icon
opacity: var(--v-medium-emphasis-opacity)
font-size: $file-upload-icon-font-size
margin-bottom: $file-upload-icon-margin-bottom
.v-file-upload-dropzone--density-comfortable &
font-size: $file-upload-icon-font-size - .5rem
margin-bottom: $file-upload-icon-margin-bottom - .5rem
.v-file-upload-dropzone--density-compact &
font-size: $file-upload-icon-font-size - 1rem
margin-bottom: $file-upload-icon-margin-bottom - 1rem
.v-file-upload-divider
align-items: center
display: flex
margin: $file-upload-divider-margin
justify-content: center
width: 100%
.v-divider__wrapper
max-width: 100%
.v-file-upload-inset
width: 100%
.v-file-upload-inset__action
display: flex
justify-content: center
width: 100%
padding: $file-upload-inset-action-padding
.v-file-upload-list
margin: $file-upload-items-margin
.v-file-upload-item
&:not(:first-child)
margin-top: 8px
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,278 @@
import { createVNode as _createVNode, createElementVNode as _createElementVNode, Fragment as _Fragment, mergeProps as _mergeProps } from "vue";
// Components
import { VFileUploadItem } from "./VFileUploadItem.js";
import { VBtn } from "../../components/VBtn/VBtn.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVDividerProps, VDivider } from "../../components/VDivider/VDivider.js";
import { VIcon } from "../../components/VIcon/VIcon.js";
import { VOverlay } from "../../components/VOverlay/VOverlay.js";
import { makeVSheetProps, VSheet } from "../../components/VSheet/VSheet.js"; // Composables
import { makeDelayProps } from "../../composables/delay.js";
import { makeDensityProps, useDensity } from "../../composables/density.js";
import { useFileDrop } from "../../composables/fileDrop.js";
import { forwardRefs } from "../../composables/forwardRefs.js";
import { IconValue } from "../../composables/icons.js";
import { useLocale } from "../../composables/locale.js"; // Utilities
import { inject, ref, shallowRef } from 'vue';
import { genericComponent, pick, propsFactory, useRender } from "../../util/index.js"; // Types
export const VFileUploadKey = Symbol.for('vuetify:file-upload');
export const makeVFileUploadDropzoneProps = propsFactory({
browseText: {
type: String,
default: '$vuetify.fileUpload.browse'
},
dividerText: {
type: String,
default: '$vuetify.fileUpload.divider'
},
title: {
type: String,
default: '$vuetify.fileUpload.title'
},
subtitle: String,
icon: {
type: IconValue,
default: '$upload'
},
clearable: Boolean,
disabled: Boolean,
error: Boolean,
hideBrowse: Boolean,
insetFileList: Boolean,
multiple: Boolean,
scrim: {
type: [Boolean, String],
default: true
},
showSize: Boolean,
...makeDelayProps(),
...makeDensityProps(),
...pick(makeVDividerProps({
length: 150
}), ['length', 'thickness', 'opacity']),
...makeVSheetProps(),
modelValue: {
type: Array,
default: () => []
}
}, 'VFileUploadDropzone');
export const VFileUploadDropzone = genericComponent()({
name: 'VFileUploadDropzone',
props: makeVFileUploadDropzoneProps(),
emits: {
'click:browse': () => true,
'click:remove': index => true,
drop: files => true
},
setup(props, {
emit,
slots
}) {
const {
t
} = useLocale();
const {
densityClasses
} = useDensity(props);
const {
handleDrop
} = useFileDrop();
const context = inject(VFileUploadKey, null);
const vSheetRef = ref();
const isDragging = shallowRef(false);
function onDragover(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = true;
}
function onDragleave(e) {
e.preventDefault();
const container = e.currentTarget;
if (!container.contains(e.relatedTarget)) {
isDragging.value = false;
}
}
async function onDrop(e) {
e.preventDefault();
e.stopImmediatePropagation();
isDragging.value = false;
const files = await handleDrop(e);
if (context) {
context.onDrop(files);
} else {
emit('drop', files);
}
}
function onClickBrowse() {
if (context) {
context.onClickBrowse();
} else {
emit('click:browse');
}
}
function onClickRemove(index) {
if (context) {
context.onClickRemove(index);
} else {
emit('click:remove', index);
}
}
useRender(() => {
const modelValue = context?.files.value ?? props.modelValue;
const disabled = context?.disabled.value ?? props.disabled;
const error = context?.error.value || props.error;
const hasTitle = !!(slots.title || props.title);
const hasIcon = !!(slots.icon || props.icon);
const hasBrowse = !!(!props.hideBrowse && (slots.browse || props.density === 'default'));
const hasFiles = modelValue.length > 0;
const isInset = props.insetFileList && hasFiles;
const sheetProps = VSheet.filterProps(props);
const dividerProps = VDivider.filterProps(props);
return _createVNode(VSheet, _mergeProps({
"ref": vSheetRef
}, sheetProps, {
"class": ['v-file-upload-dropzone', {
'v-file-upload-dropzone--clickable': !hasBrowse,
'v-file-upload-dropzone--disabled': disabled,
'v-file-upload-dropzone--dragging': isDragging.value,
'v-file-upload-dropzone--has-files': hasFiles,
'v-file-upload-dropzone--inset': isInset,
'v-file-upload-dropzone--error': error
}, densityClasses.value, props.class],
"style": props.style,
"onDragleave": onDragleave,
"onDragover": onDragover,
"onDrop": onDrop,
"onClick": !hasBrowse && !(isInset && hasFiles) ? onClickBrowse : undefined
}), {
default: () => [slots.default?.({
isDragging: isDragging.value,
hasFiles,
files: modelValue,
props: {
onClick: onClickBrowse
}
}) ?? (isInset ? _createElementVNode("div", {
"key": "inset",
"class": "v-file-upload-inset"
}, [modelValue.length === 1 && !props.multiple ? slots.single?.({
file: modelValue[0],
props: {
'onClick:remove': () => onClickRemove(0)
}
}) ?? _createVNode(VDefaultsProvider, {
"defaults": {
VFileUploadItem: {
file: modelValue[0],
clearable: props.clearable,
disabled,
showSize: props.showSize,
border: false
}
}
}, {
default: () => [_createVNode(VFileUploadItem, {
"onClick:remove": () => onClickRemove(0)
}, null)]
}) : modelValue.map((file, i) => {
const slotProps = {
file,
props: {
'onClick:remove': () => onClickRemove(i)
}
};
return _createVNode(VDefaultsProvider, {
"key": i,
"defaults": {
VFileUploadItem: {
file,
clearable: props.clearable,
disabled,
showSize: props.showSize,
border: false
}
}
}, {
default: () => [slots.item?.(slotProps) ?? _createVNode(VFileUploadItem, {
"key": i,
"onClick:remove": () => onClickRemove(i)
}, null)]
});
}), _createVNode(VDivider, null, null), _createElementVNode("div", {
"class": "v-file-upload-inset__action"
}, [!slots.browse ? _createVNode(VBtn, {
"readonly": disabled,
"text": t(props.browseText),
"variant": "text",
"onClick": onClickBrowse
}, null) : _createVNode(VDefaultsProvider, {
"defaults": {
VBtn: {
readonly: disabled,
text: t(props.browseText),
variant: 'text'
}
}
}, {
default: () => [slots.browse({
props: {
onClick: onClickBrowse
}
})]
})])]) : _createElementVNode(_Fragment, null, [hasIcon && _createElementVNode("div", {
"key": "icon",
"class": "v-file-upload-icon"
}, [!slots.icon ? _createVNode(VIcon, {
"key": "icon-icon",
"icon": props.icon
}, null) : _createVNode(VDefaultsProvider, {
"key": "icon-defaults",
"defaults": {
VIcon: {
icon: props.icon
}
}
}, {
default: () => [slots.icon()]
})]), hasTitle && _createElementVNode("div", {
"key": "title",
"class": "v-file-upload-title"
}, [slots.title?.() ?? t(props.title)]), props.density === 'default' && _createElementVNode(_Fragment, null, [hasBrowse && _createElementVNode(_Fragment, null, [_createElementVNode("div", {
"key": "upload-divider",
"class": "v-file-upload-divider"
}, [slots.divider?.() ?? _createVNode(VDivider, dividerProps, {
default: () => [t(props.dividerText)]
})]), !slots.browse ? _createVNode(VBtn, {
"readonly": disabled,
"size": "large",
"text": t(props.browseText),
"variant": "tonal",
"onClick": onClickBrowse
}, null) : _createVNode(VDefaultsProvider, {
"defaults": {
VBtn: {
readonly: disabled,
size: 'large',
text: t(props.browseText),
variant: 'tonal'
}
}
}, {
default: () => [slots.browse({
props: {
onClick: onClickBrowse
}
})]
})]), props.subtitle && _createElementVNode("div", {
"class": "v-file-upload-subtitle"
}, [props.subtitle])])])), _createVNode(VOverlay, {
"modelValue": isDragging.value,
"contained": true,
"scrim": props.scrim
}, null), slots.input?.()]
});
});
return forwardRefs({}, vSheetRef);
}
});
//# sourceMappingURL=VFileUploadDropzone.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+96
View File
@@ -0,0 +1,96 @@
import { Fragment as _Fragment, createVNode as _createVNode, createElementVNode as _createElementVNode, mergeProps as _mergeProps } from "vue";
// Components
import { VAvatar } from "../../components/VAvatar/VAvatar.js";
import { VBtn } from "../../components/VBtn/VBtn.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVListItemProps, VListItem } from "../../components/VList/VListItem.js"; // Utilities
import { computed, ref, watchEffect } from 'vue';
import { genericComponent, humanReadableFileSize, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVFileUploadItemProps = propsFactory({
clearable: Boolean,
file: {
type: Object,
default: null
},
fileIcon: {
type: String,
// TODO: setup up a proper aliased icon
default: 'mdi-file-document'
},
showSize: Boolean,
...makeVListItemProps({
border: true,
rounded: true,
lines: 'two'
})
}, 'VFileUploadItem');
export const VFileUploadItem = genericComponent()({
name: 'VFileUploadItem',
props: makeVFileUploadItemProps(),
emits: {
'click:remove': () => true,
click: e => true
},
setup(props, {
emit,
slots
}) {
const preview = ref();
const base = computed(() => typeof props.showSize !== 'boolean' ? props.showSize : undefined);
function onClickRemove() {
emit('click:remove');
}
watchEffect(() => {
preview.value = props.file?.type.startsWith('image') ? URL.createObjectURL(props.file) : undefined;
});
useRender(() => {
const listItemProps = VListItem.filterProps(props);
return _createVNode(VListItem, _mergeProps(listItemProps, {
"class": ['v-file-upload-item', props.class],
"title": props.title ?? props.file?.name,
"subtitle": props.showSize ? humanReadableFileSize(props.file?.size, base.value) : props.file?.type,
"style": props.style
}), {
...slots,
title: slots.title ?? (() => props?.title ?? props.file?.name),
prepend: slotProps => _createElementVNode(_Fragment, null, [!slots.prepend ? _createVNode(VAvatar, {
"icon": props.fileIcon,
"image": preview.value,
"rounded": true
}, null) : _createVNode(VDefaultsProvider, {
"defaults": {
VAvatar: {
image: preview.value,
icon: !preview.value ? props.fileIcon : undefined,
rounded: true
}
}
}, {
default: () => [slots.prepend?.(slotProps) ?? _createVNode(VAvatar, null, null)]
})]),
append: slotProps => _createElementVNode(_Fragment, null, [props.clearable && _createElementVNode(_Fragment, null, [!slots.clear ? _createVNode(VBtn, {
"icon": "$clear",
"density": "comfortable",
"variant": "text",
"onClick": onClickRemove
}, null) : _createVNode(VDefaultsProvider, {
"defaults": {
VBtn: {
icon: '$clear',
density: 'comfortable',
variant: 'text'
}
}
}, {
default: () => [slots.clear?.({
...slotProps,
props: {
onClick: onClickRemove
}
}) ?? _createVNode(VBtn, null, null)]
})]), slots.append?.(slotProps)])
});
});
}
});
//# sourceMappingURL=VFileUploadItem.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
import { Fragment as _Fragment, createElementVNode as _createElementVNode, createVNode as _createVNode, mergeProps as _mergeProps } from "vue";
// Components
import { VFileUploadKey } from "./VFileUploadDropzone.js";
import { VFileUploadItem } from "./VFileUploadItem.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVListProps, VList } from "../../components/VList/VList.js"; // Utilities
import { inject } from 'vue';
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVFileUploadListProps = propsFactory({
clearable: Boolean,
showSize: Boolean,
files: Array,
...makeVListProps({
border: false,
elevation: 0,
lines: false
})
}, 'VFileUploadList');
export const VFileUploadList = genericComponent()({
name: 'VFileUploadList',
props: makeVFileUploadListProps(),
setup(props, {
slots
}) {
const context = inject(VFileUploadKey, null);
useRender(() => {
const files = props.files ?? context?.files.value ?? [];
const disabled = context?.disabled.value ?? props.disabled;
const listProps = VList.filterProps(props);
if (!slots.default && !files.length) return _createElementVNode(_Fragment, null, null);
return _createVNode(VList, _mergeProps(listProps, {
"disabled": disabled,
"class": ['v-file-upload-list', props.class],
"style": props.style,
"bgColor": "transparent"
}), {
default: () => [slots.default?.({
files,
onClickRemove: i => context?.onClickRemove(i)
}) ?? files.map((file, index) => {
const slotProps = {
file,
props: {
'onClick:remove': () => context?.onClickRemove(index)
}
};
return _createVNode(VDefaultsProvider, {
"key": index,
"defaults": {
VFileUploadItem: {
file,
clearable: props.clearable,
disabled,
showSize: props.showSize,
variant: 'flat'
}
}
}, {
default: () => [slots.item?.(slotProps) ?? _createVNode(VFileUploadItem, {
"key": index,
"onClick:remove": () => context?.onClickRemove(index)
}, null)]
});
})]
});
});
}
});
//# sourceMappingURL=VFileUploadList.js.map
File diff suppressed because one or more lines are too long
+14
View File
@@ -0,0 +1,14 @@
@use '../../styles/tools';
@use '../../styles/settings';
$file-upload-title-font-size: 1.5rem !default;
$file-upload-padding: 64px 16px !default;
$file-upload-border-radius: 4px !default;
$file-upload-border-width: 2px !default;
$file-upload-title-font-weight: 600 !default;
$file-upload-icon-font-size: 3rem !default;
$file-upload-icon-margin-bottom: 1rem !default;
$file-upload-divider-margin: 32px 0 !default;
$file-upload-items-margin: 16px 0 !default;
$file-upload-inset-action-padding: 8px 0 !default;
$file-upload-inset-padding: 16px !default;
+4
View File
@@ -0,0 +1,4 @@
export { VFileUpload } from './VFileUpload.js';
export { VFileUploadDropzone } from './VFileUploadDropzone.js';
export { VFileUploadItem } from './VFileUploadItem.js';
export { VFileUploadList } from './VFileUploadList.js';
+5
View File
@@ -0,0 +1,5 @@
export { VFileUpload } from "./VFileUpload.js";
export { VFileUploadDropzone } from "./VFileUploadDropzone.js";
export { VFileUploadItem } from "./VFileUploadItem.js";
export { VFileUploadList } from "./VFileUploadList.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VFileUpload","VFileUploadDropzone","VFileUploadItem","VFileUploadList"],"sources":["../../../src/labs/VFileUpload/index.ts"],"sourcesContent":["export { VFileUpload } from './VFileUpload'\nexport { VFileUploadDropzone } from './VFileUploadDropzone'\nexport { VFileUploadItem } from './VFileUploadItem'\nexport { VFileUploadList } from './VFileUploadList'\n"],"mappings":"SAASA,WAAW;AAAA,SACXC,mBAAmB;AAAA,SACnBC,eAAe;AAAA,SACfC,eAAe","ignoreList":[]}
+202
View File
@@ -0,0 +1,202 @@
@layer vuetify-components {
.v-icon-btn {
border-color: rgba(var(--v-border-color), var(--v-border-opacity));
border-style: solid;
border-width: 0;
}
.v-icon-btn--border {
border-width: thin;
box-shadow: none;
}
.v-icon-btn {
border-radius: 50%;
}
.v-icon-btn:hover > .v-icon-btn__overlay {
opacity: calc(var(--v-hover-opacity) * var(--v-theme-overlay-multiplier));
}
.v-icon-btn:focus-visible > .v-icon-btn__overlay {
opacity: calc(var(--v-focus-opacity) * var(--v-theme-overlay-multiplier));
}
@supports not selector(:focus-visible) {
.v-icon-btn:focus > .v-icon-btn__overlay {
opacity: calc(var(--v-focus-opacity) * var(--v-theme-overlay-multiplier));
}
}
.v-icon-btn--active > .v-icon-btn__overlay, .v-icon-btn[aria-haspopup=menu][aria-expanded=true] > .v-icon-btn__overlay {
opacity: calc(var(--v-activated-opacity) * var(--v-theme-overlay-multiplier));
}
.v-icon-btn--active:hover > .v-icon-btn__overlay, .v-icon-btn[aria-haspopup=menu][aria-expanded=true]:hover > .v-icon-btn__overlay {
opacity: calc((var(--v-activated-opacity) + var(--v-hover-opacity)) * var(--v-theme-overlay-multiplier));
}
.v-icon-btn--active:focus-visible > .v-icon-btn__overlay, .v-icon-btn[aria-haspopup=menu][aria-expanded=true]:focus-visible > .v-icon-btn__overlay {
opacity: calc((var(--v-activated-opacity) + var(--v-focus-opacity)) * var(--v-theme-overlay-multiplier));
}
@supports not selector(:focus-visible) {
.v-icon-btn--active:focus > .v-icon-btn__overlay, .v-icon-btn[aria-haspopup=menu][aria-expanded=true]:focus > .v-icon-btn__overlay {
opacity: calc((var(--v-activated-opacity) + var(--v-focus-opacity)) * var(--v-theme-overlay-multiplier));
}
}
.v-icon-btn--variant-plain, .v-icon-btn--variant-outlined, .v-icon-btn--variant-text, .v-icon-btn--variant-tonal {
background: transparent;
color: inherit;
}
.v-icon-btn--variant-plain {
opacity: 0.62;
}
.v-icon-btn--variant-plain:focus, .v-icon-btn--variant-plain:hover {
opacity: 1;
}
.v-icon-btn--variant-plain .v-icon-btn__overlay {
display: none;
}
.v-icon-btn--variant-elevated, .v-icon-btn--variant-flat {
background: rgb(var(--v-theme-surface));
color: inherit;
}
.v-icon-btn--variant-elevated {
box-shadow: 0px 1px 2px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 1px 3px 1px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 2%, transparent);
}
.v-icon-btn--variant-flat {
box-shadow: 0px 0px 0px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 0px 0px 0px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 0%, transparent);
}
.v-icon-btn--variant-outlined {
border: thin solid currentColor;
}
.v-icon-btn--variant-text .v-icon-btn__overlay {
background: currentColor;
}
.v-icon-btn--variant-tonal .v-icon-btn__underlay {
background: currentColor;
opacity: var(--v-activated-opacity);
border-radius: inherit;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
.v-icon-btn .v-icon-btn__underlay {
position: absolute;
}
.v-icon-btn {
align-items: center;
cursor: pointer;
display: inline-flex;
flex: none;
font-size: 0.875rem;
font-weight: 500;
line-height: normal;
height: var(--v-icon-btn-height);
justify-content: center;
outline: none;
position: relative;
transition-property: width, height, transform;
transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
vertical-align: middle;
width: var(--v-icon-btn-width);
}
@supports selector(:focus-visible) {
.v-icon-btn::after {
pointer-events: none;
border: 2px solid currentColor;
border-radius: inherit;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.v-icon-btn::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.v-icon-btn:focus-visible::after {
opacity: calc(0.25 * var(--v-theme-overlay-multiplier));
}
}
.v-icon-btn--disabled, .v-icon-btn--loading, .v-icon-btn--readonly {
pointer-events: none;
}
.v-icon-btn--disabled {
opacity: 0.26;
}
.v-icon-btn--start {
margin-inline-end: 8px;
}
.v-icon-btn--end {
margin-inline-start: 8px;
}
.v-icon-btn__content {
align-items: center;
justify-content: center;
display: inline-flex;
transition: inherit;
transition-property: transform;
transform: rotate(var(--v-icon-btn-rotate, 0deg));
}
.v-icon-btn--loading .v-icon-btn__content {
opacity: 0;
}
.v-icon-btn__content .v-icon {
transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
transition-property: opacity, font-size, width, height;
transform-origin: center;
}
.v-icon-btn__loader {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.v-icon-btn__overlay,
.v-icon-btn__underlay {
border-radius: inherit;
pointer-events: none;
}
.v-icon-btn__overlay,
.v-icon-btn__underlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.v-icon-btn__overlay {
background-color: currentColor;
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.v-icon-btn--active:not(:hover) .v-icon-btn__overlay {
--v-activated-opacity: 0;
}
}
@layer vuetify-final.trumps {
@media (forced-colors: active) {
.v-icon-btn:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}
.v-icon-btn:not(.v-icon-btn--active):hover, .v-icon-btn:not(.v-icon-btn--active):focus {
color: highlight;
}
.v-icon-btn--active:not(.v-icon-btn--disabled), .v-icon-btn--active:not(.v-icon-btn--disabled)[class*=bg-] {
outline-color: canvastext;
background: highlight;
color: highlighttext;
}
.v-icon-btn--disabled {
color: graytext;
}
.v-icon-btn__overlay, .v-icon-btn__underlay,
.v-icon-btn .v-icon {
forced-color-adjust: preserve-parent-color;
}
}
}
+608
View File
@@ -0,0 +1,608 @@
import type { PropType } from 'vue';
import type { IconValue } from '../../composables/icons.js';
import type { Variant } from '../../composables/variant.js';
export type VIconBtnSlots = {
default: never;
loader: never;
};
export type VIconBtnSizes = 'x-small' | 'small' | 'default' | 'large' | 'x-large';
export declare const makeVIconBtnProps: <Defaults extends {
theme?: unknown;
class?: unknown;
style?: unknown;
border?: unknown;
elevation?: unknown;
rounded?: unknown;
tile?: unknown;
tag?: unknown;
color?: unknown;
variant?: unknown;
active?: unknown;
activeColor?: unknown;
activeIcon?: unknown;
activeVariant?: unknown;
baseVariant?: unknown;
disabled?: unknown;
height?: unknown;
width?: unknown;
hideOverlay?: unknown;
icon?: unknown;
iconColor?: unknown;
loading?: unknown;
opacity?: unknown;
readonly?: unknown;
rotate?: unknown;
size?: unknown;
sizes?: unknown;
text?: unknown;
iconSize?: unknown;
iconSizes?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
theme: unknown extends Defaults["theme"] ? StringConstructor : {
type: PropType<unknown extends Defaults["theme"] ? string : string | Defaults["theme"]>;
default: unknown extends Defaults["theme"] ? string : string | Defaults["theme"];
};
class: unknown extends Defaults["class"] ? PropType<any> : {
type: PropType<unknown extends Defaults["class"] ? any : any>;
default: unknown extends Defaults["class"] ? any : any;
};
style: unknown extends Defaults["style"] ? {
type: PropType<import("vue").StyleValue>;
default: null;
} : Omit<{
type: PropType<import("vue").StyleValue>;
default: null;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | import("vue").StyleValue>;
default: unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | NonNullable<import("vue").StyleValue>;
};
border: unknown extends Defaults["border"] ? (BooleanConstructor | NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["border"] ? string | number | boolean : string | number | boolean | Defaults["border"]>;
default: unknown extends Defaults["border"] ? string | number | boolean : Defaults["border"] | NonNullable<string | number | boolean>;
};
elevation: unknown extends Defaults["elevation"] ? {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
} : Omit<{
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["elevation"] ? string | number : string | number | Defaults["elevation"]>;
default: unknown extends Defaults["elevation"] ? string | number : Defaults["elevation"] | NonNullable<string | number>;
};
rounded: unknown extends Defaults["rounded"] ? {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
} : Omit<{
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["rounded"] ? string | number | boolean : string | number | boolean | Defaults["rounded"]>;
default: unknown extends Defaults["rounded"] ? string | number | boolean : Defaults["rounded"] | NonNullable<string | number | boolean>;
};
tile: unknown extends Defaults["tile"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["tile"] ? boolean : boolean | Defaults["tile"]>;
default: unknown extends Defaults["tile"] ? boolean : boolean | Defaults["tile"];
};
tag: unknown extends Defaults["tag"] ? Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: NonNullable<string | import("../../util/index.js").JSXComponent>;
} : Omit<Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: NonNullable<string | import("../../util/index.js").JSXComponent>;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : string | Defaults["tag"] | import("../../util/index.js").JSXComponent>;
default: unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : Defaults["tag"] | NonNullable<string | import("../../util/index.js").JSXComponent>;
};
color: unknown extends Defaults["color"] ? StringConstructor : {
type: PropType<unknown extends Defaults["color"] ? string : string | Defaults["color"]>;
default: unknown extends Defaults["color"] ? string : string | Defaults["color"];
};
variant: unknown extends Defaults["variant"] ? Omit<{
type: PropType<Variant>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
default: NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
} : Omit<Omit<{
type: PropType<Variant>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
default: NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["variant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | Defaults["variant"]>;
default: unknown extends Defaults["variant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : Defaults["variant"] | NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
};
active: unknown extends Defaults["active"] ? {
type: BooleanConstructor;
default: undefined;
} : Omit<{
type: BooleanConstructor;
default: undefined;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["active"] ? boolean : boolean | Defaults["active"]>;
default: unknown extends Defaults["active"] ? boolean : boolean | Defaults["active"];
};
activeColor: unknown extends Defaults["activeColor"] ? StringConstructor : {
type: PropType<unknown extends Defaults["activeColor"] ? string : string | Defaults["activeColor"]>;
default: unknown extends Defaults["activeColor"] ? string : string | Defaults["activeColor"];
};
activeIcon: unknown extends Defaults["activeIcon"] ? PropType<IconValue> : {
type: PropType<unknown extends Defaults["activeIcon"] ? IconValue : Defaults["activeIcon"] | IconValue>;
default: unknown extends Defaults["activeIcon"] ? IconValue : Defaults["activeIcon"] | NonNullable<IconValue>;
};
activeVariant: unknown extends Defaults["activeVariant"] ? PropType<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal"> : {
type: PropType<unknown extends Defaults["activeVariant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | Defaults["activeVariant"]>;
default: unknown extends Defaults["activeVariant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : Defaults["activeVariant"] | NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
};
baseVariant: unknown extends Defaults["baseVariant"] ? {
type: PropType<Variant>;
default: string;
} : Omit<{
type: PropType<Variant>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["baseVariant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | Defaults["baseVariant"]>;
default: unknown extends Defaults["baseVariant"] ? "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" : Defaults["baseVariant"] | NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
};
disabled: unknown extends Defaults["disabled"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["disabled"] ? boolean : boolean | Defaults["disabled"]>;
default: unknown extends Defaults["disabled"] ? boolean : boolean | Defaults["disabled"];
};
height: unknown extends Defaults["height"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["height"] ? string | number : string | number | Defaults["height"]>;
default: unknown extends Defaults["height"] ? string | number : Defaults["height"] | NonNullable<string | number>;
};
width: unknown extends Defaults["width"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["width"] ? string | number : string | number | Defaults["width"]>;
default: unknown extends Defaults["width"] ? string | number : Defaults["width"] | NonNullable<string | number>;
};
hideOverlay: unknown extends Defaults["hideOverlay"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["hideOverlay"] ? boolean : boolean | Defaults["hideOverlay"]>;
default: unknown extends Defaults["hideOverlay"] ? boolean : boolean | Defaults["hideOverlay"];
};
icon: unknown extends Defaults["icon"] ? PropType<IconValue> : {
type: PropType<unknown extends Defaults["icon"] ? IconValue : Defaults["icon"] | IconValue>;
default: unknown extends Defaults["icon"] ? IconValue : Defaults["icon"] | NonNullable<IconValue>;
};
iconColor: unknown extends Defaults["iconColor"] ? StringConstructor : {
type: PropType<unknown extends Defaults["iconColor"] ? string : string | Defaults["iconColor"]>;
default: unknown extends Defaults["iconColor"] ? string : string | Defaults["iconColor"];
};
loading: unknown extends Defaults["loading"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["loading"] ? boolean : boolean | Defaults["loading"]>;
default: unknown extends Defaults["loading"] ? boolean : boolean | Defaults["loading"];
};
opacity: unknown extends Defaults["opacity"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["opacity"] ? string | number : string | number | Defaults["opacity"]>;
default: unknown extends Defaults["opacity"] ? string | number : Defaults["opacity"] | NonNullable<string | number>;
};
readonly: unknown extends Defaults["readonly"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["readonly"] ? boolean : boolean | Defaults["readonly"]>;
default: unknown extends Defaults["readonly"] ? boolean : boolean | Defaults["readonly"];
};
rotate: unknown extends Defaults["rotate"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["rotate"] ? string | number : string | number | Defaults["rotate"]>;
default: unknown extends Defaults["rotate"] ? string | number : Defaults["rotate"] | NonNullable<string | number>;
};
size: unknown extends Defaults["size"] ? {
type: PropType<VIconBtnSizes | number | string>;
default: string;
} : Omit<{
type: PropType<VIconBtnSizes | number | string>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["size"] ? string | number : string | number | Defaults["size"]>;
default: unknown extends Defaults["size"] ? string | number : Defaults["size"] | NonNullable<string | number>;
};
sizes: unknown extends Defaults["sizes"] ? {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
} : Omit<{
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["sizes"] ? [VIconBtnSizes, number][] : [VIconBtnSizes, number][] | Defaults["sizes"]>;
default: unknown extends Defaults["sizes"] ? [VIconBtnSizes, number][] : [VIconBtnSizes, number][] | Defaults["sizes"];
};
text: unknown extends Defaults["text"] ? {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
} : Omit<{
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["text"] ? string | number | boolean : string | number | boolean | Defaults["text"]>;
default: unknown extends Defaults["text"] ? string | number | boolean : Defaults["text"] | NonNullable<string | number | boolean>;
};
iconSize: unknown extends Defaults["iconSize"] ? PropType<string | number> : {
type: PropType<unknown extends Defaults["iconSize"] ? string | number : string | number | Defaults["iconSize"]>;
default: unknown extends Defaults["iconSize"] ? string | number : Defaults["iconSize"] | NonNullable<string | number>;
};
iconSizes: unknown extends Defaults["iconSizes"] ? {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
} : Omit<{
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["iconSizes"] ? [VIconBtnSizes, number][] : [VIconBtnSizes, number][] | Defaults["iconSizes"]>;
default: unknown extends Defaults["iconSizes"] ? [VIconBtnSizes, number][] : [VIconBtnSizes, number][] | Defaults["iconSizes"];
};
};
export declare const VIconBtn: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
iconSizes: [VIconBtnSizes, number][];
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
color?: string | undefined;
active?: boolean | undefined;
activeColor?: string | undefined;
activeIcon?: IconValue | undefined;
activeVariant?: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | undefined;
height?: string | number | undefined;
width?: string | number | undefined;
icon?: IconValue | undefined;
iconColor?: string | undefined;
opacity?: string | number | undefined;
rotate?: string | number | undefined;
text?: string | number | boolean | undefined;
iconSize?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loader?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loader?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:loader"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((value: boolean) => any) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
'update:active': (value: boolean) => true;
}, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
active: boolean;
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
text: string | number | boolean;
iconSizes: [VIconBtnSizes, number][];
}, true, {}, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
loader: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
iconSizes: [VIconBtnSizes, number][];
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
color?: string | undefined;
active?: boolean | undefined;
activeColor?: string | undefined;
activeIcon?: IconValue | undefined;
activeVariant?: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | undefined;
height?: string | number | undefined;
width?: string | number | undefined;
icon?: IconValue | undefined;
iconColor?: string | undefined;
opacity?: string | number | undefined;
rotate?: string | number | undefined;
text?: string | number | boolean | undefined;
iconSize?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loader?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loader?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:loader"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((value: boolean) => any) | undefined;
}, {}, {}, {}, {}, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
active: boolean;
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
text: string | number | boolean;
iconSizes: [VIconBtnSizes, number][];
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
iconSizes: [VIconBtnSizes, number][];
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
color?: string | undefined;
active?: boolean | undefined;
activeColor?: string | undefined;
activeIcon?: IconValue | undefined;
activeVariant?: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal" | undefined;
height?: string | number | undefined;
width?: string | number | undefined;
icon?: IconValue | undefined;
iconColor?: string | undefined;
opacity?: string | number | undefined;
rotate?: string | number | undefined;
text?: string | number | boolean | undefined;
iconSize?: string | number | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
loader?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
loader?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:loader"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((value: boolean) => any) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
'update:active': (value: boolean) => true;
}, string, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
variant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
active: boolean;
baseVariant: "elevated" | "flat" | "outlined" | "plain" | "text" | "tonal";
disabled: boolean;
hideOverlay: boolean;
loading: boolean;
readonly: boolean;
size: string | number;
sizes: [VIconBtnSizes, number][];
text: string | number | boolean;
iconSizes: [VIconBtnSizes, number][];
}, {}, string, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
loader: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
theme: StringConstructor;
class: PropType<import("../../composables/component.js").ClassValue>;
style: {
type: PropType<import("vue").StyleValue>;
default: null;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
elevation: {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
};
rounded: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
tile: BooleanConstructor;
tag: Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: NonNullable<string | import("../../util/index.js").JSXComponent>;
};
color: StringConstructor;
variant: Omit<{
type: PropType<Variant>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
default: NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
};
active: {
type: BooleanConstructor;
default: undefined;
};
activeColor: StringConstructor;
activeIcon: PropType<IconValue>;
activeVariant: PropType<Variant>;
baseVariant: {
type: PropType<Variant>;
default: string;
};
disabled: BooleanConstructor;
height: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
hideOverlay: BooleanConstructor;
icon: PropType<IconValue>;
iconColor: StringConstructor;
loading: BooleanConstructor;
opacity: (NumberConstructor | StringConstructor)[];
readonly: BooleanConstructor;
rotate: (NumberConstructor | StringConstructor)[];
size: {
type: PropType<VIconBtnSizes | number | string>;
default: string;
};
sizes: {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
};
text: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
iconSize: PropType<VIconBtnSizes | number | string>;
iconSizes: {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
};
}, import("vue").ExtractPropTypes<{
theme: StringConstructor;
class: PropType<import("../../composables/component.js").ClassValue>;
style: {
type: PropType<import("vue").StyleValue>;
default: null;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
elevation: {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
};
rounded: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
tile: BooleanConstructor;
tag: Omit<{
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: PropType<string | import("../../util/index.js").JSXComponent>;
default: NonNullable<string | import("../../util/index.js").JSXComponent>;
};
color: StringConstructor;
variant: Omit<{
type: PropType<Variant>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
default: NonNullable<"elevated" | "flat" | "outlined" | "plain" | "text" | "tonal">;
};
active: {
type: BooleanConstructor;
default: undefined;
};
activeColor: StringConstructor;
activeIcon: PropType<IconValue>;
activeVariant: PropType<Variant>;
baseVariant: {
type: PropType<Variant>;
default: string;
};
disabled: BooleanConstructor;
height: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
hideOverlay: BooleanConstructor;
icon: PropType<IconValue>;
iconColor: StringConstructor;
loading: BooleanConstructor;
opacity: (NumberConstructor | StringConstructor)[];
readonly: BooleanConstructor;
rotate: (NumberConstructor | StringConstructor)[];
size: {
type: PropType<VIconBtnSizes | number | string>;
default: string;
};
sizes: {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
};
text: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
iconSize: PropType<VIconBtnSizes | number | string>;
iconSizes: {
type: PropType<[VIconBtnSizes, number][]>;
default: () => (string | number)[][];
};
}>>;
export type VIconBtn = InstanceType<typeof VIconBtn>;
+178
View File
@@ -0,0 +1,178 @@
import { mergeProps as _mergeProps, createVNode as _createVNode, createElementVNode as _createElementVNode, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle } from "vue";
// Styles
import "./VIconBtn.css";
// Components
import { VDefaultsProvider } from "../../components/VDefaultsProvider/index.js";
import { VIcon } from "../../components/VIcon/index.js";
import { VProgressCircular } from "../../components/VProgressCircular/index.js"; // Composables
import { makeBorderProps, useBorder } from "../../composables/border.js";
import { makeComponentProps } from "../../composables/component.js";
import { makeElevationProps, useElevation } from "../../composables/elevation.js";
import { makeIconSizeProps, useIconSizes } from "../../composables/iconSizes.js";
import { useProxiedModel } from "../../composables/proxiedModel.js";
import { makeRoundedProps, useRounded } from "../../composables/rounded.js";
import { makeTagProps } from "../../composables/tag.js";
import { makeThemeProps, provideTheme } from "../../composables/theme.js";
import { genOverlays, makeVariantProps, useVariant } from "../../composables/variant.js"; // Utilities
import { toDisplayString } from 'vue';
import { convertToUnit, genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVIconBtnProps = propsFactory({
active: {
type: Boolean,
default: undefined
},
activeColor: String,
activeIcon: [String, Function, Object],
activeVariant: String,
baseVariant: {
type: String,
default: 'tonal'
},
disabled: Boolean,
height: [Number, String],
width: [Number, String],
hideOverlay: Boolean,
icon: [String, Function, Object],
iconColor: String,
loading: Boolean,
opacity: [Number, String],
readonly: Boolean,
rotate: [Number, String],
size: {
type: [Number, String],
default: 'default'
},
sizes: {
type: Array,
default: () => [['x-small', 16], ['small', 24], ['default', 40], ['large', 48], ['x-large', 56]]
},
text: {
type: [String, Number, Boolean],
default: undefined
},
...makeBorderProps(),
...makeComponentProps(),
...makeElevationProps(),
...makeIconSizeProps(),
...makeRoundedProps(),
...makeTagProps({
tag: 'button'
}),
...makeThemeProps(),
...makeVariantProps({
variant: 'flat'
})
}, 'VIconBtn');
export const VIconBtn = genericComponent()({
name: 'VIconBtn',
props: makeVIconBtnProps(),
emits: {
'update:active': value => true
},
setup(props, {
attrs,
slots
}) {
const isActive = useProxiedModel(props, 'active');
const {
themeClasses
} = provideTheme(props);
const {
borderClasses
} = useBorder(props);
const {
elevationClasses
} = useElevation(props);
const {
roundedClasses
} = useRounded(props);
const {
colorClasses,
colorStyles,
variantClasses
} = useVariant(() => ({
color: (() => {
if (props.disabled) return undefined;
if (!isActive.value) return props.color;
// Use an inline fallback as opposed to setting a default color
// because non-toggle buttons are default flat whereas toggle
// buttons are default tonal and active flat. The exact use
// case for this is a toggle button with no active color.
return props.activeColor ?? props.color ?? 'surface-variant';
})(),
variant: (() => {
if (isActive.value === undefined) return props.variant;
if (isActive.value) return props.activeVariant ?? props.variant;
return props.baseVariant ?? props.variant;
})()
}));
const btnSizeMap = new Map(props.sizes);
function onClick() {
if (props.disabled || props.readonly || isActive.value === undefined || props.tag === 'a' && attrs.href) return;
isActive.value = !isActive.value;
}
useRender(() => {
const icon = isActive.value ? props.activeIcon ?? props.icon : props.icon;
const _btnSize = props.size;
const hasNamedSize = btnSizeMap.has(_btnSize);
const btnSize = hasNamedSize ? btnSizeMap.get(_btnSize) : _btnSize;
const btnHeight = props.height ?? btnSize;
const btnWidth = props.width ?? btnSize;
const {
iconSize
} = useIconSizes(props, () => new Map(props.iconSizes).get(_btnSize));
const iconProps = {
icon,
size: iconSize.value,
color: props.iconColor,
opacity: props.opacity
};
return _createVNode(props.tag, {
"type": props.tag === 'button' ? 'button' : undefined,
"class": _normalizeClass([{
'v-icon-btn': true,
'v-icon-btn--active': isActive.value,
'v-icon-btn--disabled': props.disabled,
'v-icon-btn--loading': props.loading,
'v-icon-btn--readonly': props.readonly,
[`v-icon-btn--${props.size}`]: true
}, themeClasses.value, colorClasses.value, borderClasses.value, elevationClasses.value, roundedClasses.value, variantClasses.value, props.class]),
"style": _normalizeStyle([{
'--v-icon-btn-rotate': convertToUnit(props.rotate, 'deg'),
'--v-icon-btn-height': convertToUnit(btnHeight),
'--v-icon-btn-width': convertToUnit(btnWidth)
}, colorStyles.value, props.style]),
"tabindex": props.disabled || props.readonly ? -1 : 0,
"onClick": onClick
}, {
default: () => [genOverlays(!props.hideOverlay, 'v-icon-btn'), _createElementVNode("div", {
"class": "v-icon-btn__content",
"data-no-activator": ""
}, [!slots.default && icon ? _createVNode(VIcon, _mergeProps({
"key": "content-icon"
}, iconProps), null) : _createVNode(VDefaultsProvider, {
"key": "content-defaults",
"disabled": !icon,
"defaults": {
VIcon: {
...iconProps
}
}
}, {
default: () => slots.default?.() ?? toDisplayString(props.text)
})]), !!props.loading && _createElementVNode("span", {
"key": "loader",
"class": "v-icon-btn__loader"
}, [slots.loader?.() ?? _createVNode(VProgressCircular, {
"color": typeof props.loading === 'boolean' ? undefined : props.loading,
"indeterminate": "disable-shrink",
"width": "2",
"size": iconSize.value
}, null)])]
});
});
return {};
}
});
//# sourceMappingURL=VIconBtn.js.map
File diff suppressed because one or more lines are too long
+144
View File
@@ -0,0 +1,144 @@
@use '../../styles/settings';
@use '../../styles/tools';
@use './variables' as *;
@include tools.layer('components') {
.v-icon-btn {
@include tools.border($icon-btn-border...);
@include tools.rounded($icon-btn-border-radius);
@include tools.states('.v-icon-btn__overlay');
@include tools.variant($icon-btn-variants...);
& {
align-items: center;
cursor: pointer;
display: inline-flex;
flex: none;
font-size: $icon-btn-font-size;
font-weight: $icon-btn-font-weight;
line-height: $icon-btn-line-height;
height: #{$icon-btn-height};
justify-content: center;
outline: none;
position: relative;
transition-property: width, height, transform;
transition: 0.2s settings.$standard-easing;
vertical-align: middle;
width: #{$icon-btn-width};
@supports selector(:focus-visible) {
&::after {
pointer-events: none;
border: 2px solid currentColor;
border-radius: inherit;
opacity: 0;
transition: opacity .2s ease-in-out;
@include tools.absolute(true);
}
&:focus-visible::after {
opacity: calc(.25 * var(--v-theme-overlay-multiplier));
}
}
}
&--disabled,
&--loading,
&--readonly {
pointer-events: none;
}
&--disabled {
opacity: $icon-btn-disabled-opacity;
}
&--start {
margin-inline-end: $icon-btn-margin-start;
}
&--end {
margin-inline-start: $icon-btn-margin-end;
}
}
.v-icon-btn__content {
align-items: center;
justify-content: center;
display: inline-flex;
transition: inherit;
transition-property: transform;
transform: rotate(var(--v-icon-btn-rotate, 0deg));
.v-icon-btn--loading & {
opacity: 0;
}
.v-icon {
transition: 0.2s settings.$standard-easing;
transition-property: opacity, font-size, width, height;
transform-origin: center;
}
}
.v-icon-btn__loader {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.v-icon-btn__overlay,
.v-icon-btn__underlay {
border-radius: inherit;
pointer-events: none;
@include tools.absolute();
}
.v-icon-btn__overlay {
background-color: currentColor;
opacity: 0;
transition: opacity .2s ease-in-out;
.v-icon-btn--active:not(:hover) & {
--v-activated-opacity: 0;
}
}
}
@include tools.layer('trumps') {
@media (forced-colors: active) {
.v-icon-btn {
&:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}
&:not(&--active):hover,
&:not(&--active):focus {
color: highlight;
}
&--active:not(&--disabled),
&--active:not(&--disabled)[class*="bg-"] {
outline-color: canvastext;
background: highlight;
color: highlighttext;
}
&--disabled {
color: graytext;
}
&__overlay,
&__underlay,
.v-icon {
forced-color-adjust: preserve-parent-color;
}
}
}
}
+37
View File
@@ -0,0 +1,37 @@
@use 'sass:map';
@use '../../styles/settings';
@use '../../styles/tools';
// VIconBtn
$icon-btn-background: rgb(var(--v-theme-surface)) !default;
$icon-btn-color: inherit !default;
$icon-btn-border-color: settings.$border-color-root !default;
$icon-btn-border-radius: map.get(settings.$rounded, 'circle') !default;
$icon-btn-border-style: settings.$border-style-root !default;
$icon-btn-border-thin-width: thin !default;
$icon-btn-border-width: 0 !default;
$icon-btn-disabled-opacity: 0.26 !default;
$icon-btn-elevation: 1 !default;
$icon-btn-font-size: tools.map-deep-get(settings.$typography, 'label-large', 'size') !default;
$icon-btn-font-weight: tools.map-deep-get(settings.$typography, 'label-large', 'weight') !default;
$icon-btn-line-height: normal !default;
$icon-btn-height: var(--v-icon-btn-height) !default;
$icon-btn-width: var(--v-icon-btn-width) !default;
$icon-btn-margin-start: 8px !default;
$icon-btn-margin-end: 8px !default;
$icon-btn-plain-opacity: .62 !default;
$icon-btn-border: (
$icon-btn-border-color,
$icon-btn-border-style,
$icon-btn-border-width,
$icon-btn-border-thin-width
) !default;
$icon-btn-variants: (
$icon-btn-background,
$icon-btn-color,
$icon-btn-elevation,
$icon-btn-plain-opacity,
'v-icon-btn'
) !default;
+1
View File
@@ -0,0 +1 @@
export { VIconBtn } from './VIconBtn.js';
+2
View File
@@ -0,0 +1,2 @@
export { VIconBtn } from "./VIconBtn.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VIconBtn"],"sources":["../../../src/labs/VIconBtn/index.ts"],"sourcesContent":["export { VIconBtn } from './VIconBtn'\n"],"mappings":"SAASA,QAAQ","ignoreList":[]}
File diff suppressed because one or more lines are too long
+181
View File
@@ -0,0 +1,181 @@
import { mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
// Components
import { makeVTextFieldProps, VTextField } from "../../components/VTextField/VTextField.js"; // Composables
import { forwardRefs } from "../../composables/forwardRefs.js";
import { makeMaskProps, useMask } from "../../composables/mask/index.js";
import { useProxiedModel } from "../../composables/proxiedModel.js"; // Utilities
import { computed, nextTick, onBeforeMount, ref, shallowRef, toRef } from 'vue';
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVMaskInputProps = propsFactory({
returnMaskedValue: Boolean,
...makeVTextFieldProps(),
...makeMaskProps()
}, 'VMaskInput');
export const VMaskInput = genericComponent()({
name: 'VMaskInput',
props: makeVMaskInputProps(),
emits: {
'update:modelValue': val => true
},
setup(props, {
slots,
emit
}) {
const vTextFieldRef = ref();
const inputAction = shallowRef();
const caretPosition = shallowRef(0);
const mask = useMask(props);
const returnMaskedValue = computed(() => props.mask && props.returnMaskedValue);
const model = useProxiedModel(props, 'modelValue', undefined,
// Always display masked value in input when mask is applied
val => props.mask ? mask.mask(mask.unmask(val)) : val, val => {
if (props.mask) {
// E.g. mask is #-# and the input value is '2-23'
// model-value should be enforced to '2-2'
const newMaskedValue = mask.mask(mask.unmask(val));
const newUnmaskedValue = mask.unmask(newMaskedValue);
const newCaretPosition = getNewCaretPosition({
oldValue: model.value,
newValue: newMaskedValue,
oldCaret: caretPosition.value
});
vTextFieldRef.value.value = newMaskedValue;
vTextFieldRef.value.setSelectionRange(newCaretPosition, newCaretPosition);
return returnMaskedValue.value ? mask.mask(newUnmaskedValue) : newUnmaskedValue;
}
return val;
});
const validationValue = toRef(() => returnMaskedValue.value ? model.value : mask.unmask(model.value));
function getNewCaretPosition({
oldValue,
newValue,
oldCaret
}) {
if (!newValue) return 0;
if (!oldValue) return newValue.length;
let newCaret;
if (inputAction.value === 'Backspace') {
newCaret = oldCaret - 1;
while (newCaret > 0 && mask.isDelimiter(newValue, newCaret - 1)) newCaret--;
} else if (inputAction.value === 'Delete') {
newCaret = oldCaret;
} else {
// insertion
newCaret = oldCaret + 1;
while (mask.isDelimiter(newValue, newCaret)) newCaret++;
if (mask.isDelimiter(newValue, oldCaret)) newCaret++;
}
return newCaret;
}
onBeforeMount(() => {
if (props.returnMaskedValue) {
emit('update:modelValue', model.value);
}
});
function onKeyDown(e) {
if (e.metaKey) return;
const inputElement = e.target;
caretPosition.value = inputElement.selectionStart || 0;
inputAction.value = e.key;
const hasSelection = inputElement.selectionStart !== inputElement.selectionEnd;
if (e.key === 'Backspace' && hasSelection) {
e.preventDefault();
deleteSelection(e);
}
}
async function onCut(e) {
e.preventDefault();
await copySelectionToClipboard(e);
await deleteSelection(e);
}
async function onPaste(e) {
e.preventDefault();
const inputElement = e.target;
const pastedString = e.clipboardData?.getData('text') || '';
if (!pastedString) return;
const pastedCharacters = [...pastedString];
const hasSelection = inputElement.selectionStart !== inputElement.selectionEnd;
if (hasSelection) {
replaceSelection(inputElement, pastedCharacters);
} else {
insertCharacters(inputElement, pastedCharacters);
}
}
async function copySelectionToClipboard(e) {
const inputElement = e.target;
const start = inputElement.selectionStart || 0;
const end = inputElement.selectionEnd || 0;
const selectedText = inputElement.value.substring(start, end);
await navigator.clipboard.writeText(selectedText);
}
async function deleteSelection(e) {
const inputElement = e.target;
const curStart = inputElement.selectionStart || 0;
caretPosition.value = inputElement.selectionEnd || 0;
while (caretPosition.value > curStart) {
const success = await simulateBackspace(inputElement);
if (!success) break;
}
}
async function simulateBackspace(inputElement) {
inputAction.value = 'Backspace';
model.value = inputElement.value.slice(0, caretPosition.value - 1) + inputElement.value.slice(caretPosition.value);
inputAction.value = '';
if (caretPosition.value === inputElement.selectionEnd) return false;
caretPosition.value = inputElement.selectionEnd || 0;
await nextTick();
return true;
}
async function insertCharacters(inputElement, pastedCharacters) {
for (let i = 0; i < pastedCharacters.length; i++) {
await insertCharacter(inputElement, pastedCharacters[i]);
}
}
async function insertCharacter(inputElement, character) {
caretPosition.value = inputElement.selectionEnd || 0;
model.value = inputElement.value.slice(0, caretPosition.value) + character + inputElement.value.slice(caretPosition.value);
await nextTick();
}
async function replaceSelection(inputElement, pastedCharacters) {
caretPosition.value = inputElement.selectionStart || 0;
for (let i = 0; i < pastedCharacters.length; i++) {
if (await replaceCharacter(caretPosition.value, pastedCharacters[i])) {
caretPosition.value++;
}
}
}
async function replaceCharacter(index, character) {
let targetIndex = index;
// Find next non-delimiter position
while (targetIndex < model.value.length && mask.isDelimiter(model.value, targetIndex)) {
targetIndex++;
}
const newValue = model.value.slice(0, targetIndex) + character + model.value.slice(targetIndex + 1);
if (mask.isValid(newValue)) {
model.value = newValue;
await nextTick();
return true;
}
return false;
}
useRender(() => {
const textFieldProps = VTextField.filterProps(props);
return _createVNode(VTextField, _mergeProps(textFieldProps, {
"modelValue": model.value,
"onUpdate:modelValue": $event => model.value = $event,
"ref": vTextFieldRef,
"class": ['v-mask-input', props.class],
"style": props.style,
"validationValue": validationValue.value,
"onCut": onCut,
"onPaste": onPaste,
"onKeydown": onKeyDown
}), {
...slots
});
});
return forwardRefs({}, vTextFieldRef);
}
});
//# sourceMappingURL=VMaskInput.js.map
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
export { VMaskInput } from './VMaskInput.js';
+2
View File
@@ -0,0 +1,2 @@
export { VMaskInput } from "./VMaskInput.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VMaskInput"],"sources":["../../../src/labs/VMaskInput/index.ts"],"sourcesContent":["export { VMaskInput } from './VMaskInput'\n"],"mappings":"SAASA,UAAU","ignoreList":[]}
+64
View File
@@ -0,0 +1,64 @@
@layer vuetify-components {
.v-picker.v-sheet {
display: grid;
grid-auto-rows: min-content;
grid-template-areas: "header" "body";
grid-template-columns: minmax(0, 1fr);
overflow: hidden;
}
.v-picker.v-sheet {
box-shadow: 0px 0px 0px 0px rgba(var(--v-shadow-color), var(--v-shadow-key-opacity, 0.3)), 0px 0px 0px 0px rgba(var(--v-shadow-color), var(--v-shadow-ambient-opacity, 0.15));
--v-elevation-overlay: color-mix(in srgb, var(--v-elevation-overlay-color) 0%, transparent);
}
.v-picker.v-sheet {
border-radius: 4px;
}
.v-picker.v-sheet.v-picker--with-actions {
grid-template-areas: "header" "body" "actions";
}
.v-picker.v-sheet.v-picker--landscape {
grid-template-columns: auto 1fr;
grid-template-areas: "header body" "header body";
}
.v-picker.v-sheet.v-picker--landscape.v-picker--with-actions {
grid-template-areas: "header body" "header actions";
}
.v-picker__body {
grid-area: body;
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.v-picker__header-wrapper {
grid-area: header;
}
.v-picker__actions {
grid-area: actions;
padding: 0 12px 12px;
display: flex;
align-items: center;
justify-content: flex-end;
}
.v-picker__actions .v-btn {
min-width: 48px;
}
.v-picker__actions .v-btn:not(:last-child) {
margin-inline-end: 8px;
}
.v-picker--divided .v-picker__header {
border-bottom-color: rgba(var(--v-border-color), var(--v-border-opacity));
border-bottom-style: solid;
border-bottom-width: thin;
}
.v-picker-title {
text-transform: uppercase;
font-size: 0.75rem;
padding-inline: 24px 12px;
padding-top: 16px;
padding-bottom: 16px;
font-weight: 400;
letter-spacing: 0.1666666667em;
}
}
+435
View File
@@ -0,0 +1,435 @@
export type VPickerSlots = {
header: never;
default: never;
actions: never;
title: never;
};
export declare const makeVPickerProps: <Defaults extends {
theme?: unknown;
class?: unknown;
style?: unknown;
border?: unknown;
elevation?: unknown;
rounded?: unknown;
tile?: unknown;
tag?: unknown;
height?: unknown;
maxHeight?: unknown;
maxWidth?: unknown;
minHeight?: unknown;
minWidth?: unknown;
width?: unknown;
location?: unknown;
position?: unknown;
color?: unknown;
bgColor?: unknown;
divided?: unknown;
landscape?: unknown;
title?: unknown;
hideHeader?: unknown;
hideTitle?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
theme: unknown extends Defaults["theme"] ? StringConstructor : {
type: import("vue").PropType<unknown extends Defaults["theme"] ? string : string | Defaults["theme"]>;
default: unknown extends Defaults["theme"] ? string : string | Defaults["theme"];
};
class: unknown extends Defaults["class"] ? import("vue").PropType<any> : {
type: import("vue").PropType<unknown extends Defaults["class"] ? any : any>;
default: unknown extends Defaults["class"] ? any : any;
};
style: unknown extends Defaults["style"] ? {
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
} : Omit<{
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
}, "default" | "type"> & {
type: import("vue").PropType<unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | import("vue").StyleValue>;
default: unknown extends Defaults["style"] ? import("vue").StyleValue : Defaults["style"] | NonNullable<import("vue").StyleValue>;
};
border: unknown extends Defaults["border"] ? (BooleanConstructor | NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["border"] ? string | number | boolean : string | number | boolean | Defaults["border"]>;
default: unknown extends Defaults["border"] ? string | number | boolean : Defaults["border"] | NonNullable<string | number | boolean>;
};
elevation: unknown extends Defaults["elevation"] ? {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
} : Omit<{
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
}, "default" | "type"> & {
type: import("vue").PropType<unknown extends Defaults["elevation"] ? string | number : string | number | Defaults["elevation"]>;
default: unknown extends Defaults["elevation"] ? string | number : Defaults["elevation"] | NonNullable<string | number>;
};
rounded: unknown extends Defaults["rounded"] ? {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
} : Omit<{
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
}, "default" | "type"> & {
type: import("vue").PropType<unknown extends Defaults["rounded"] ? string | number | boolean : string | number | boolean | Defaults["rounded"]>;
default: unknown extends Defaults["rounded"] ? string | number | boolean : Defaults["rounded"] | NonNullable<string | number | boolean>;
};
tile: unknown extends Defaults["tile"] ? BooleanConstructor : {
type: import("vue").PropType<unknown extends Defaults["tile"] ? boolean : boolean | Defaults["tile"]>;
default: unknown extends Defaults["tile"] ? boolean : boolean | Defaults["tile"];
};
tag: unknown extends Defaults["tag"] ? {
type: import("vue").PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
} : Omit<{
type: import("vue").PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
}, "default" | "type"> & {
type: import("vue").PropType<unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : string | Defaults["tag"] | import("../../util/index.js").JSXComponent>;
default: unknown extends Defaults["tag"] ? string | import("../../util/index.js").JSXComponent : Defaults["tag"] | NonNullable<string | import("../../util/index.js").JSXComponent>;
};
height: unknown extends Defaults["height"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["height"] ? string | number : string | number | Defaults["height"]>;
default: unknown extends Defaults["height"] ? string | number : Defaults["height"] | NonNullable<string | number>;
};
maxHeight: unknown extends Defaults["maxHeight"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["maxHeight"] ? string | number : string | number | Defaults["maxHeight"]>;
default: unknown extends Defaults["maxHeight"] ? string | number : Defaults["maxHeight"] | NonNullable<string | number>;
};
maxWidth: unknown extends Defaults["maxWidth"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["maxWidth"] ? string | number : string | number | Defaults["maxWidth"]>;
default: unknown extends Defaults["maxWidth"] ? string | number : Defaults["maxWidth"] | NonNullable<string | number>;
};
minHeight: unknown extends Defaults["minHeight"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["minHeight"] ? string | number : string | number | Defaults["minHeight"]>;
default: unknown extends Defaults["minHeight"] ? string | number : Defaults["minHeight"] | NonNullable<string | number>;
};
minWidth: unknown extends Defaults["minWidth"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["minWidth"] ? string | number : string | number | Defaults["minWidth"]>;
default: unknown extends Defaults["minWidth"] ? string | number : Defaults["minWidth"] | NonNullable<string | number>;
};
width: unknown extends Defaults["width"] ? (NumberConstructor | StringConstructor)[] : {
type: import("vue").PropType<unknown extends Defaults["width"] ? string | number : string | number | Defaults["width"]>;
default: unknown extends Defaults["width"] ? string | number : Defaults["width"] | NonNullable<string | number>;
};
location: unknown extends Defaults["location"] ? import("vue").PropType<import("../../util/index.js").Anchor | null> : {
type: import("vue").PropType<unknown extends Defaults["location"] ? import("../../util/index.js").Anchor | null : Defaults["location"] | import("../../util/index.js").Anchor | null>;
default: unknown extends Defaults["location"] ? import("../../util/index.js").Anchor | null : Defaults["location"] | NonNullable<import("../../util/index.js").Anchor | null>;
};
position: unknown extends Defaults["position"] ? {
type: import("vue").PropType<"absolute" | "fixed" | "relative" | "static" | "sticky">;
validator: (v: any) => boolean;
} : Omit<{
type: import("vue").PropType<"absolute" | "fixed" | "relative" | "static" | "sticky">;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: import("vue").PropType<unknown extends Defaults["position"] ? "absolute" | "fixed" | "relative" | "static" | "sticky" : "absolute" | "fixed" | "relative" | "static" | "sticky" | Defaults["position"]>;
default: unknown extends Defaults["position"] ? "absolute" | "fixed" | "relative" | "static" | "sticky" : Defaults["position"] | NonNullable<"absolute" | "fixed" | "relative" | "static" | "sticky">;
};
color: unknown extends Defaults["color"] ? StringConstructor : {
type: import("vue").PropType<unknown extends Defaults["color"] ? string : string | Defaults["color"]>;
default: unknown extends Defaults["color"] ? string : string | Defaults["color"];
};
bgColor: unknown extends Defaults["bgColor"] ? StringConstructor : {
type: import("vue").PropType<unknown extends Defaults["bgColor"] ? string : string | Defaults["bgColor"]>;
default: unknown extends Defaults["bgColor"] ? string : string | Defaults["bgColor"];
};
divided: unknown extends Defaults["divided"] ? BooleanConstructor : {
type: import("vue").PropType<unknown extends Defaults["divided"] ? boolean : boolean | Defaults["divided"]>;
default: unknown extends Defaults["divided"] ? boolean : boolean | Defaults["divided"];
};
landscape: unknown extends Defaults["landscape"] ? BooleanConstructor : {
type: import("vue").PropType<unknown extends Defaults["landscape"] ? boolean : boolean | Defaults["landscape"]>;
default: unknown extends Defaults["landscape"] ? boolean : boolean | Defaults["landscape"];
};
title: unknown extends Defaults["title"] ? StringConstructor : {
type: import("vue").PropType<unknown extends Defaults["title"] ? string : string | Defaults["title"]>;
default: unknown extends Defaults["title"] ? string : string | Defaults["title"];
};
hideHeader: unknown extends Defaults["hideHeader"] ? BooleanConstructor : {
type: import("vue").PropType<unknown extends Defaults["hideHeader"] ? boolean : boolean | Defaults["hideHeader"]>;
default: unknown extends Defaults["hideHeader"] ? boolean : boolean | Defaults["hideHeader"];
};
hideTitle: unknown extends Defaults["hideTitle"] ? BooleanConstructor : {
type: import("vue").PropType<unknown extends Defaults["hideTitle"] ? boolean : boolean | Defaults["hideTitle"]>;
default: unknown extends Defaults["hideTitle"] ? boolean : boolean | Defaults["hideTitle"];
};
};
export declare const VPicker: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
location?: import("../../util/index.js").Anchor | null | undefined;
position?: "absolute" | "fixed" | "relative" | "static" | "sticky" | undefined;
color?: string | undefined;
bgColor?: string | undefined;
title?: string | undefined;
} & {
$children?: {
header?: (() => import("vue").VNodeChild) | undefined;
default?: (() => import("vue").VNodeChild) | undefined;
actions?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
header?: false | (() => import("vue").VNodeChild) | undefined;
default?: false | (() => import("vue").VNodeChild) | undefined;
actions?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:actions"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:header"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
}, true, {}, import("vue").SlotsType<Partial<{
header: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
actions: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
location?: import("../../util/index.js").Anchor | null | undefined;
position?: "absolute" | "fixed" | "relative" | "static" | "sticky" | undefined;
color?: string | undefined;
bgColor?: string | undefined;
title?: string | undefined;
} & {
$children?: {
header?: (() => import("vue").VNodeChild) | undefined;
default?: (() => import("vue").VNodeChild) | undefined;
actions?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
header?: false | (() => import("vue").VNodeChild) | undefined;
default?: false | (() => import("vue").VNodeChild) | undefined;
actions?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:actions"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:header"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, {}, {}, {}, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
} & {
theme?: string | undefined;
class?: any;
border?: string | number | boolean | undefined;
elevation?: string | number | undefined;
rounded?: string | number | boolean | undefined;
height?: string | number | undefined;
maxHeight?: string | number | undefined;
maxWidth?: string | number | undefined;
minHeight?: string | number | undefined;
minWidth?: string | number | undefined;
width?: string | number | undefined;
location?: import("../../util/index.js").Anchor | null | undefined;
position?: "absolute" | "fixed" | "relative" | "static" | "sticky" | undefined;
color?: string | undefined;
bgColor?: string | undefined;
title?: string | undefined;
} & {
$children?: {
header?: (() => import("vue").VNodeChild) | undefined;
default?: (() => import("vue").VNodeChild) | undefined;
actions?: (() => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
header?: false | (() => import("vue").VNodeChild) | undefined;
default?: false | (() => import("vue").VNodeChild) | undefined;
actions?: false | (() => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:actions"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:header"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {
style: import("vue").StyleValue;
rounded: string | number | boolean;
tile: boolean;
tag: string | import("../../util/index.js").JSXComponent;
divided: boolean;
landscape: boolean;
hideHeader: boolean;
hideTitle: boolean;
}, {}, string, import("vue").SlotsType<Partial<{
header: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
actions: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
theme: StringConstructor;
class: import("vue").PropType<any>;
style: {
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
elevation: {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
};
rounded: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
tile: BooleanConstructor;
tag: {
type: import("vue").PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
height: (NumberConstructor | StringConstructor)[];
maxHeight: (NumberConstructor | StringConstructor)[];
maxWidth: (NumberConstructor | StringConstructor)[];
minHeight: (NumberConstructor | StringConstructor)[];
minWidth: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
location: import("vue").PropType<import("../../util/index.js").Anchor | null>;
position: {
type: import("vue").PropType<"absolute" | "fixed" | "relative" | "static" | "sticky">;
validator: (v: any) => boolean;
};
color: StringConstructor;
bgColor: StringConstructor;
divided: BooleanConstructor;
landscape: BooleanConstructor;
title: StringConstructor;
hideHeader: BooleanConstructor;
hideTitle: BooleanConstructor;
}, import("vue").ExtractPropTypes<{
theme: StringConstructor;
class: import("vue").PropType<any>;
style: {
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
};
border: (BooleanConstructor | NumberConstructor | StringConstructor)[];
elevation: {
type: (NumberConstructor | StringConstructor)[];
validator: (value: string | number) => boolean;
};
rounded: {
type: (BooleanConstructor | NumberConstructor | StringConstructor)[];
default: undefined;
};
tile: BooleanConstructor;
tag: {
type: import("vue").PropType<string | import("../../util/index.js").JSXComponent>;
default: string;
};
height: (NumberConstructor | StringConstructor)[];
maxHeight: (NumberConstructor | StringConstructor)[];
maxWidth: (NumberConstructor | StringConstructor)[];
minHeight: (NumberConstructor | StringConstructor)[];
minWidth: (NumberConstructor | StringConstructor)[];
width: (NumberConstructor | StringConstructor)[];
location: import("vue").PropType<import("../../util/index.js").Anchor | null>;
position: {
type: import("vue").PropType<"absolute" | "fixed" | "relative" | "static" | "sticky">;
validator: (v: any) => boolean;
};
color: StringConstructor;
bgColor: StringConstructor;
divided: BooleanConstructor;
landscape: BooleanConstructor;
title: StringConstructor;
hideHeader: BooleanConstructor;
hideTitle: BooleanConstructor;
}>>;
export type VPicker = InstanceType<typeof VPicker>;
+71
View File
@@ -0,0 +1,71 @@
import { createVNode as _createVNode, createElementVNode as _createElementVNode, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle, mergeProps as _mergeProps } from "vue";
// Styles
import "./VPicker.css";
// Components
import { VPickerTitle } from "./VPickerTitle.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/VDefaultsProvider.js";
import { makeVSheetProps, VSheet } from "../../components/VSheet/VSheet.js"; // Composables
import { useBackgroundColor } from "../../composables/color.js"; // Utilities
import { genericComponent, propsFactory, useRender } from "../../util/index.js"; // Types
export const makeVPickerProps = propsFactory({
bgColor: String,
divided: Boolean,
landscape: Boolean,
title: String,
hideHeader: Boolean,
hideTitle: Boolean,
...makeVSheetProps()
}, 'VPicker');
export const VPicker = genericComponent()({
name: 'VPicker',
props: makeVPickerProps(),
setup(props, {
slots
}) {
const {
backgroundColorClasses,
backgroundColorStyles
} = useBackgroundColor(() => props.color);
useRender(() => {
const sheetProps = VSheet.filterProps(props);
const hasTitle = !props.hideTitle && !!(props.title || slots.title);
return _createVNode(VSheet, _mergeProps(sheetProps, {
"color": props.bgColor,
"class": ['v-picker', {
'v-picker--divided': props.divided,
'v-picker--landscape': props.landscape,
'v-picker--with-actions': !!slots.actions
}, props.class],
"style": props.style
}), {
default: () => [!props.hideHeader && _createElementVNode("div", {
"key": "header",
"class": _normalizeClass(['v-picker__header-wrapper', backgroundColorClasses.value]),
"style": _normalizeStyle([backgroundColorStyles.value])
}, [hasTitle && _createVNode(VPickerTitle, {
"key": "picker-title"
}, {
default: () => [slots.title?.() ?? props.title]
}), slots.header && _createElementVNode("div", {
"class": "v-picker__header"
}, [slots.header()])]), _createElementVNode("div", {
"class": "v-picker__body"
}, [slots.default?.()]), slots.actions && _createVNode(VDefaultsProvider, {
"defaults": {
VBtn: {
slim: true,
variant: 'text'
}
}
}, {
default: () => [_createElementVNode("div", {
"class": "v-picker__actions"
}, [slots.actions()])]
})]
});
});
return {};
}
});
//# sourceMappingURL=VPicker.js.map
File diff suppressed because one or more lines are too long
+62
View File
@@ -0,0 +1,62 @@
@use '../../styles/settings'
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-picker.v-sheet
display: grid
grid-auto-rows: min-content
grid-template-areas: "header" "body"
grid-template-columns: minmax(0, 1fr)
overflow: hidden
@include tools.elevation($picker-elevation)
@include tools.rounded($picker-border-radius)
&.v-picker--with-actions
grid-template-areas: "header" "body" "actions"
&.v-picker--landscape
grid-template-columns: auto 1fr
grid-template-areas: "header body" "header body"
&.v-picker--with-actions
grid-template-areas: "header body" "header actions"
.v-picker__body
grid-area: body
overflow: hidden
position: relative
display: flex
justify-content: center
flex-wrap: wrap
.v-picker__header-wrapper
grid-area: header
.v-picker__actions
grid-area: actions
padding: $picker-actions-padding
display: flex
align-items: center
justify-content: flex-end
.v-btn
min-width: 48px
&:not(:last-child)
margin-inline-end: 8px
.v-picker--divided
.v-picker__header
border-bottom-color: $picker-border-color
border-bottom-style: $picker-border-style
border-bottom-width: $picker-border-thin-width
.v-picker-title
text-transform: uppercase
font-size: .75rem
padding-inline: 24px 12px
padding-top: 16px
padding-bottom: 16px
font-weight: $picker-title-font-weight
letter-spacing: .1666666667em
+105
View File
@@ -0,0 +1,105 @@
export declare const VPickerTitle: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string;
} & {
class?: any;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
style: import("vue").StyleValue;
tag: string;
}, true, {}, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string;
} & {
class?: any;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, {}, {}, {}, {
style: import("vue").StyleValue;
tag: string;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
style: string | false | import("vue").StyleValue[] | import("vue").CSSProperties | null;
tag: string;
} & {
class?: any;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {
style: import("vue").StyleValue;
tag: string;
}, {}, string, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
class: import("vue").PropType<any>;
style: {
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
};
tag: {
type: StringConstructor;
default: string;
};
}, import("vue").ExtractPropTypes<{
class: import("vue").PropType<any>;
style: {
type: import("vue").PropType<import("vue").StyleValue>;
default: null;
};
tag: {
type: StringConstructor;
default: string;
};
}>>;
export type VPickerTitle = InstanceType<typeof VPickerTitle>;
+4
View File
@@ -0,0 +1,4 @@
// Utilities
import { createSimpleFunctional } from "../../util/index.js";
export const VPickerTitle = createSimpleFunctional('v-picker-title');
//# sourceMappingURL=VPickerTitle.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"VPickerTitle.js","names":["createSimpleFunctional","VPickerTitle"],"sources":["../../../src/labs/VPicker/VPickerTitle.ts"],"sourcesContent":["// Utilities\nimport { createSimpleFunctional } from '@/util'\n\nexport const VPickerTitle = createSimpleFunctional('v-picker-title')\n\nexport type VPickerTitle = InstanceType<typeof VPickerTitle>\n"],"mappings":"AAAA;AAAA,SACSA,sBAAsB;AAE/B,OAAO,MAAMC,YAAY,GAAGD,sBAAsB,CAAC,gBAAgB,CAAC","ignoreList":[]}
+11
View File
@@ -0,0 +1,11 @@
@use '../../styles/settings';
$picker-actions-padding: 0 12px 12px !default;
$picker-border-color: settings.$border-color-root !default;
$picker-border-radius: settings.$border-radius-root !default;
$picker-border-radius: settings.$border-radius-root !default;
$picker-border-style: settings.$border-style-root !default;
$picker-border-thin-width: thin !default;
$picker-elevation: 0 !default;
$picker-inactive-btn-opacity: .6 !default;
$picker-title-font-weight: 400 !default;
+2
View File
@@ -0,0 +1,2 @@
export { VPicker } from './VPicker.js';
export { VPickerTitle } from './VPickerTitle.js';
+3
View File
@@ -0,0 +1,3 @@
export { VPicker } from "./VPicker.js";
export { VPickerTitle } from "./VPickerTitle.js";
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":["VPicker","VPickerTitle"],"sources":["../../../src/labs/VPicker/index.ts"],"sourcesContent":["export { VPicker } from './VPicker'\nexport { VPickerTitle } from './VPickerTitle'\n"],"mappings":"SAASA,OAAO;AAAA,SACPC,YAAY","ignoreList":[]}
+116
View File
@@ -0,0 +1,116 @@
@layer vuetify-components {
.v-pie {
display: grid;
align-items: center;
column-gap: 24px;
--v-pie-size: 250px;
}
.v-pie--legend-top {
grid-template-areas: "title" "legend" "content";
grid-template-columns: var(--v-pie-size);
}
.v-pie--legend-bottom {
grid-template-areas: "title" "content" "legend";
grid-template-columns: var(--v-pie-size);
}
.v-pie--legend-right {
grid-template-areas: "title ." "content legend";
}
.v-pie--legend-left {
grid-template-areas: ". title" "legend content";
}
.v-pie--legend-hidden {
grid-template-areas: "title" "content";
}
.v-pie__title {
grid-area: title;
text-align: center;
padding-bottom: 12px;
}
.v-pie__content {
grid-area: content;
position: relative;
width: var(--v-pie-size);
height: var(--v-pie-size);
}
}
@layer vuetify-overrides {
.v-pie__content .v-overlay__scrim,
.v-pie__content .v-overlay__content {
pointer-events: none;
}
}
@layer vuetify-final.trumps {
.v-pie__content {
background: none;
}
}
@layer vuetify-components {
.v-pie__segments {
border-radius: 50%;
}
.v-pie__content-underlay {
border-radius: 50%;
position: absolute;
inset: -8px;
pointer-events: none;
z-index: -1;
}
.v-pie__center-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.v-pie__center-content > div {
pointer-events: auto;
}
.v-pie__legend {
grid-area: legend;
padding-block: 12px;
}
.v-pie__legend .v-avatar {
border: thin solid color-mix(in srgb, rgb(var(--v-theme-on-surface)) 20%, transparent);
}
.v-pie__legend .v-chip__content {
width: 100%;
}
.v-pie__legend .v-chip-group .v-chip:not(.v-chip--selected) {
opacity: 0.4;
}
.v-pie__legend__text {
font-size: 0.8125rem;
white-space: nowrap;
width: 100%;
}
.v-chip--density-compact .v-pie__legend__text {
font-size: 0.66rem;
}
.v-pie .v-chip.v-chip--density-comfortable .v-avatar--start {
margin-inline-start: -6px;
}
.v-pie .v-chip.v-chip--density-default .v-avatar--start {
margin-inline-start: -4px;
}
.v-pie-segment {
pointer-events: none;
position: absolute;
inset: 0;
}
.v-pie-segment .v-pie-segment__overlay {
pointer-events: auto;
opacity: 0;
}
.v-pie__tooltip-content .v-list-item {
padding-inline: 0;
min-width: 100px;
zoom: 0.88;
}
.v-pie__tooltip-content .v-list-item-subtitle {
opacity: 1;
}
.v-pie__tooltip-content .v-avatar {
border: thin solid color-mix(in srgb, rgb(var(--v-theme-on-surface-variant)) 20%, transparent);
}
}
+962
View File
@@ -0,0 +1,962 @@
import type { PropType, TransitionProps } from 'vue';
import type { PieItem, TextTemplate } from './types.js';
export type VPieSlots = {
center: {
total: number;
};
legend: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
};
'legend-text': {
item: PieItem;
total: number;
};
title: never;
tooltip: {
item: PieItem;
total: number;
};
};
export declare const makeVPieProps: <Defaults extends {
density?: unknown;
reveal?: unknown;
innerCut?: unknown;
hoverScale?: unknown;
gap?: unknown;
rounded?: unknown;
animation?: unknown;
hideSlice?: unknown;
title?: unknown;
bgColor?: unknown;
items?: unknown;
palette?: unknown;
itemKey?: unknown;
itemValue?: unknown;
itemTitle?: unknown;
size?: unknown;
rotate?: unknown;
gaugeCut?: unknown;
legend?: unknown;
tooltip?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
density: unknown extends Defaults["density"] ? {
type: PropType<import("../../composables/density.js").Density>;
default: string;
validator: (v: any) => boolean;
} : Omit<{
type: PropType<import("../../composables/density.js").Density>;
default: string;
validator: (v: any) => boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["density"] ? import("../../composables/density.js").Density : Defaults["density"] | import("../../composables/density.js").Density>;
default: unknown extends Defaults["density"] ? import("../../composables/density.js").Density : Defaults["density"] | NonNullable<import("../../composables/density.js").Density>;
};
reveal: unknown extends Defaults["reveal"] ? {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["reveal"] ? boolean | {
duration?: number;
} : boolean | {
duration?: number;
} | Defaults["reveal"]>;
default: unknown extends Defaults["reveal"] ? boolean | {
duration?: number;
} : Defaults["reveal"] | NonNullable<boolean | {
duration?: number;
}>;
};
innerCut: unknown extends Defaults["innerCut"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["innerCut"] ? string | number : string | number | Defaults["innerCut"]>;
default: unknown extends Defaults["innerCut"] ? string | number : Defaults["innerCut"] | NonNullable<string | number>;
};
hoverScale: unknown extends Defaults["hoverScale"] ? {
type: (NumberConstructor | StringConstructor)[];
default: number;
} : Omit<{
type: (NumberConstructor | StringConstructor)[];
default: number;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["hoverScale"] ? string | number : string | number | Defaults["hoverScale"]>;
default: unknown extends Defaults["hoverScale"] ? string | number : Defaults["hoverScale"] | NonNullable<string | number>;
};
gap: unknown extends Defaults["gap"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["gap"] ? string | number : string | number | Defaults["gap"]>;
default: unknown extends Defaults["gap"] ? string | number : Defaults["gap"] | NonNullable<string | number>;
};
rounded: unknown extends Defaults["rounded"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["rounded"] ? string | number : string | number | Defaults["rounded"]>;
default: unknown extends Defaults["rounded"] ? string | number : Defaults["rounded"] | NonNullable<string | number>;
};
animation: unknown extends Defaults["animation"] ? {
type: PropType<boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["animation"] ? boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
} : boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
} | Defaults["animation"]>;
default: unknown extends Defaults["animation"] ? boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
} : Defaults["animation"] | NonNullable<boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
}>;
};
hideSlice: unknown extends Defaults["hideSlice"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["hideSlice"] ? boolean : boolean | Defaults["hideSlice"]>;
default: unknown extends Defaults["hideSlice"] ? boolean : boolean | Defaults["hideSlice"];
};
title: unknown extends Defaults["title"] ? StringConstructor : {
type: PropType<unknown extends Defaults["title"] ? string : string | Defaults["title"]>;
default: unknown extends Defaults["title"] ? string : string | Defaults["title"];
};
bgColor: unknown extends Defaults["bgColor"] ? StringConstructor : {
type: PropType<unknown extends Defaults["bgColor"] ? string : string | Defaults["bgColor"]>;
default: unknown extends Defaults["bgColor"] ? string : string | Defaults["bgColor"];
};
items: unknown extends Defaults["items"] ? {
type: PropType<Record<string, any> | {
color?: string;
pattern?: string;
}[]>;
default: () => never[];
} : Omit<{
type: PropType<Record<string, any> | {
color?: string;
pattern?: string;
}[]>;
default: () => never[];
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["items"] ? {
color?: string;
pattern?: string;
}[] | Record<string, any> : {
color?: string;
pattern?: string;
}[] | Record<string, any> | Defaults["items"]>;
default: unknown extends Defaults["items"] ? {
color?: string;
pattern?: string;
}[] | Record<string, any> : Defaults["items"] | NonNullable<{
color?: string;
pattern?: string;
}[] | Record<string, any>>;
};
palette: unknown extends Defaults["palette"] ? {
type: PropType<({
color?: string;
pattern?: string;
} | string)[]>;
default: () => never[];
} : Omit<{
type: PropType<({
color?: string;
pattern?: string;
} | string)[]>;
default: () => never[];
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["palette"] ? (string | {
color?: string;
pattern?: string;
})[] : (string | {
color?: string;
pattern?: string;
})[] | Defaults["palette"]>;
default: unknown extends Defaults["palette"] ? (string | {
color?: string;
pattern?: string;
})[] : (string | {
color?: string;
pattern?: string;
})[] | Defaults["palette"];
};
itemKey: unknown extends Defaults["itemKey"] ? {
type: StringConstructor;
default: string;
} : Omit<{
type: StringConstructor;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["itemKey"] ? string : string | Defaults["itemKey"]>;
default: unknown extends Defaults["itemKey"] ? string : string | Defaults["itemKey"];
};
itemValue: unknown extends Defaults["itemValue"] ? {
type: StringConstructor;
default: string;
} : Omit<{
type: StringConstructor;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["itemValue"] ? string : string | Defaults["itemValue"]>;
default: unknown extends Defaults["itemValue"] ? string : string | Defaults["itemValue"];
};
itemTitle: unknown extends Defaults["itemTitle"] ? {
type: StringConstructor;
default: string;
} : Omit<{
type: StringConstructor;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["itemTitle"] ? string : string | Defaults["itemTitle"]>;
default: unknown extends Defaults["itemTitle"] ? string : string | Defaults["itemTitle"];
};
size: unknown extends Defaults["size"] ? {
type: (NumberConstructor | StringConstructor)[];
default: number;
} : Omit<{
type: (NumberConstructor | StringConstructor)[];
default: number;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["size"] ? string | number : string | number | Defaults["size"]>;
default: unknown extends Defaults["size"] ? string | number : Defaults["size"] | NonNullable<string | number>;
};
rotate: unknown extends Defaults["rotate"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["rotate"] ? string | number : string | number | Defaults["rotate"]>;
default: unknown extends Defaults["rotate"] ? string | number : Defaults["rotate"] | NonNullable<string | number>;
};
gaugeCut: unknown extends Defaults["gaugeCut"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["gaugeCut"] ? string | number : string | number | Defaults["gaugeCut"]>;
default: unknown extends Defaults["gaugeCut"] ? string | number : Defaults["gaugeCut"] | NonNullable<string | number>;
};
legend: unknown extends Defaults["legend"] ? {
type: PropType<boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["legend"] ? boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
} : boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
} | Defaults["legend"]>;
default: unknown extends Defaults["legend"] ? boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
} : Defaults["legend"] | NonNullable<boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
}>;
};
tooltip: unknown extends Defaults["tooltip"] ? {
type: PropType<boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["tooltip"] ? boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
} : boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
} | Defaults["tooltip"]>;
default: unknown extends Defaults["tooltip"] ? boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
} : Defaults["tooltip"] | NonNullable<boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
}>;
};
};
export declare const VPie: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
} & {
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
title?: string | undefined;
bgColor?: string | undefined;
rotate?: string | number | undefined;
gaugeCut?: string | number | undefined;
} & {
$children?: {
center?: ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
tooltip?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
center?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
tooltip?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:center"?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend"?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend-text"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:tooltip"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
}, true, {}, import("vue").SlotsType<Partial<{
center: (arg: {
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
legend: (arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
'legend-text': (arg: {
item: PieItem;
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
tooltip: (arg: {
item: PieItem;
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
} & {
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
title?: string | undefined;
bgColor?: string | undefined;
rotate?: string | number | undefined;
gaugeCut?: string | number | undefined;
} & {
$children?: {
center?: ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
tooltip?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
center?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
tooltip?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:center"?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend"?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend-text"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:tooltip"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, {}, {}, {}, {
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
} & {
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
title?: string | undefined;
bgColor?: string | undefined;
rotate?: string | number | undefined;
gaugeCut?: string | number | undefined;
} & {
$children?: {
center?: ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: (() => import("vue").VNodeChild) | undefined;
tooltip?: ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | {} | import("vue").VNodeChild;
'v-slots'?: {
center?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
legend?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
'legend-text'?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
title?: false | (() => import("vue").VNodeChild) | undefined;
tooltip?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:center"?: false | ((arg: {
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend"?: false | ((arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:legend-text"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
"v-slot:title"?: false | (() => import("vue").VNodeChild) | undefined;
"v-slot:tooltip"?: false | ((arg: {
item: PieItem;
total: number;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {
density: import("../../composables/density.js").Density;
reveal: boolean | {
duration?: number;
};
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
};
hideSlice: boolean;
items: {
color?: string;
pattern?: string;
}[] | Record<string, any>;
palette: (string | {
color?: string;
pattern?: string;
})[];
itemKey: string;
itemValue: string;
itemTitle: string;
size: string | number;
legend: boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
};
tooltip: boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
};
}, {}, string, import("vue").SlotsType<Partial<{
center: (arg: {
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
legend: (arg: {
isActive: (item: PieItem) => boolean;
toggle: (item: PieItem) => void;
items: PieItem[];
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
'legend-text': (arg: {
item: PieItem;
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
title: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
tooltip: (arg: {
item: PieItem;
total: number;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
density: {
type: PropType<import("../../composables/density.js").Density>;
default: string;
validator: (v: any) => boolean;
};
reveal: {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
};
innerCut: (NumberConstructor | StringConstructor)[];
hoverScale: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
gap: (NumberConstructor | StringConstructor)[];
rounded: (NumberConstructor | StringConstructor)[];
animation: {
type: PropType<boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
}>;
default: boolean;
};
hideSlice: BooleanConstructor;
title: StringConstructor;
bgColor: StringConstructor;
items: {
type: PropType<Record<string, any> | {
color?: string;
pattern?: string;
}[]>;
default: () => never[];
};
palette: {
type: PropType<({
color?: string;
pattern?: string;
} | string)[]>;
default: () => never[];
};
itemKey: {
type: StringConstructor;
default: string;
};
itemValue: {
type: StringConstructor;
default: string;
};
itemTitle: {
type: StringConstructor;
default: string;
};
size: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
rotate: (NumberConstructor | StringConstructor)[];
gaugeCut: (NumberConstructor | StringConstructor)[];
legend: {
type: PropType<boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
}>;
default: boolean;
};
tooltip: {
type: PropType<boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
}>;
default: boolean;
};
}, import("vue").ExtractPropTypes<{
density: {
type: PropType<import("../../composables/density.js").Density>;
default: string;
validator: (v: any) => boolean;
};
reveal: {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
};
innerCut: (NumberConstructor | StringConstructor)[];
hoverScale: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
gap: (NumberConstructor | StringConstructor)[];
rounded: (NumberConstructor | StringConstructor)[];
animation: {
type: PropType<boolean | {
duration?: number;
easing?: "easeInCubic" | "easeInOutCubic" | "easeInOutQuad" | "easeInOutQuart" | "easeInOutQuint" | "easeInQuad" | "easeInQuart" | "easeInQuint" | "easeOutCubic" | "easeOutQuad" | "easeOutQuart" | "easeOutQuint" | "instant" | "linear";
}>;
default: boolean;
};
hideSlice: BooleanConstructor;
title: StringConstructor;
bgColor: StringConstructor;
items: {
type: PropType<Record<string, any> | {
color?: string;
pattern?: string;
}[]>;
default: () => never[];
};
palette: {
type: PropType<({
color?: string;
pattern?: string;
} | string)[]>;
default: () => never[];
};
itemKey: {
type: StringConstructor;
default: string;
};
itemValue: {
type: StringConstructor;
default: string;
};
itemTitle: {
type: StringConstructor;
default: string;
};
size: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
rotate: (NumberConstructor | StringConstructor)[];
gaugeCut: (NumberConstructor | StringConstructor)[];
legend: {
type: PropType<boolean | {
position?: 'left' | 'top' | 'right' | 'bottom';
textFormat?: TextTemplate;
}>;
default: boolean;
};
tooltip: {
type: PropType<boolean | {
titleFormat?: TextTemplate;
subtitleFormat?: TextTemplate;
avatarSize?: number;
transition?: string | boolean | TransitionProps;
offset?: number;
}>;
default: boolean;
};
}>>;
export type VPie = InstanceType<typeof VPie>;
+333
View File
@@ -0,0 +1,333 @@
import { createElementVNode as _createElementVNode, createVNode as _createVNode, normalizeClass as _normalizeClass, normalizeStyle as _normalizeStyle, mergeProps as _mergeProps, withDirectives as _withDirectives } from "vue";
// Styles
import "./VPie.css";
// Components
import { makeVPieSegmentProps, VPieSegment } from "./VPieSegment.js";
import { VPieTooltip } from "./VPieTooltip.js";
import { VAvatar } from "../../components/VAvatar/index.js";
import { VChip } from "../../components/VChip/index.js";
import { VChipGroup } from "../../components/VChipGroup/index.js";
import { VDefaultsProvider } from "../../components/VDefaultsProvider/index.js"; // Composables
import { useColor } from "../../composables/color.js";
import { makeDensityProps } from "../../composables/density.js"; // Directives
import vClickOutside from "../../directives/click-outside/index.js"; // Utilities
import { computed, shallowRef, toRef, watch } from 'vue';
import { formatTextTemplate } from "./utils.js";
import { convertToUnit, genericComponent, pick, propsFactory } from "../../util/index.js"; // Types
export const makeVPieProps = propsFactory({
title: String,
bgColor: String,
items: {
type: Array,
default: () => []
},
palette: {
type: Array,
default: () => []
},
itemKey: {
type: String,
default: 'key'
},
itemValue: {
type: String,
default: 'value'
},
itemTitle: {
type: String,
default: 'title'
},
size: {
type: [Number, String],
default: 250
},
rotate: [Number, String],
gaugeCut: [Number, String],
legend: {
type: [Boolean, Object],
default: false
},
tooltip: {
type: [Boolean, Object],
default: false
},
...makeDensityProps(),
...pick(makeVPieSegmentProps(), ['animation', 'gap', 'rounded', 'innerCut', 'hoverScale', 'hideSlice', 'reveal'])
}, 'VPie');
export const VPie = genericComponent()({
name: 'VPie',
directives: {
vClickOutside
},
props: makeVPieProps(),
setup(props, {
slots
}) {
const legendConfig = computed(() => ({
visible: !!props.legend,
position: 'bottom',
textFormat: '[title]',
...(typeof props.legend === 'object' ? props.legend : {})
}));
const {
colorClasses,
colorStyles
} = useColor(() => ({
background: props.bgColor
}));
const textColorStyles = toRef(() => pick(colorStyles.value, ['color', 'caretColor']));
const legendAvatarSize = toRef(() => ({
default: 20,
comfortable: 18,
compact: 16
})[props.density ?? 'default']);
const legendDirection = toRef(() => ['left', 'right'].includes(legendConfig.value.position) ? 'vertical' : 'horizontal');
const legendMode = toRef(() => !legendConfig.value.visible ? 'hidden' : legendConfig.value.position);
const legendTextFormatFunction = toRef(() => item => {
return typeof legendConfig.value.textFormat === 'function' ? legendConfig.value.textFormat(item) : formatTextTemplate(legendConfig.value.textFormat, item);
});
const arcs = computed(() => {
// hidden items get (value: 0) to trigger disappearing animation
return props.items.filter(Boolean).map((item, index) => {
return {
key: item[props.itemKey],
color: item.color ?? colorFromPalette(index),
value: item[props.itemValue],
title: String(item[props.itemTitle]),
pattern: item.pattern ?? patternFromPalette(index),
raw: item
};
});
});
const visibleItemsKeys = shallowRef([]);
watch(() => arcs.value.length, () => {
// reset when number of items changes
visibleItemsKeys.value = arcs.value.map(a => a.key);
}, {
immediate: true
});
const visibleItems = computed(() => {
// hidden items get (value: 0) to trigger disappearing animation
return arcs.value.map(item => {
return isVisible(item) ? item : {
...item,
value: 0
};
});
});
const total = computed(() => visibleItems.value.reduce((sum, item) => sum + item.value, 0));
const gaugeCut = toRef(() => Number(props.gaugeCut ?? 0));
const gaugeOffset = computed(() => (1 - Math.cos(Math.PI * Math.min(90, gaugeCut.value / 2) / 180)) / 2);
const rotateDeg = computed(() => `${gaugeCut.value ? 180 + gaugeCut.value / 2 : props.rotate ?? 0}deg`);
function arcOffset(index) {
return visibleItems.value.slice(0, index).reduce((acc, s) => acc + (total.value > 0 ? s.value / total.value : 0) * (360 - gaugeCut.value), 0);
}
function arcSize(v) {
return v / total.value * (100 - gaugeCut.value / 3.6);
}
function colorFromPalette(index) {
if (props.palette.length === 0) return undefined;
const paletteItem = props.palette[index % props.palette.length];
return typeof paletteItem === 'object' ? paletteItem.color : paletteItem;
}
function patternFromPalette(index) {
if (props.palette.length === 0) return undefined;
const paletteItem = props.palette[index % props.palette.length];
return typeof paletteItem === 'object' ? paletteItem.pattern : undefined;
}
function isVisible(item) {
return visibleItemsKeys.value.includes(item.key);
}
function toggle(item) {
if (isVisible(item)) {
visibleItemsKeys.value = visibleItemsKeys.value.filter(x => x !== item.key);
} else {
visibleItemsKeys.value = [...visibleItemsKeys.value, item.key];
}
}
const activeItemKey = shallowRef(null);
const tooltipItem = shallowRef(null);
const tooltipVisible = shallowRef(false);
const tooltipTarget = shallowRef([0, 0]);
let mouseLeaveTimeout = null;
function setItemActive(item, active) {
activeItemKey.value = active ? item.key : null;
if (props.tooltip) {
setTooltip(item, active);
}
}
function setTooltip(item, active) {
clearTimeout(mouseLeaveTimeout);
if (active) {
tooltipVisible.value = true;
tooltipItem.value = item;
} else {
mouseLeaveTimeout = setTimeout(() => {
tooltipVisible.value = false;
// intentionally reusing timeout here
mouseLeaveTimeout = setTimeout(() => {
tooltipItem.value = null;
}, 500);
}, 100);
}
}
let frame = -1;
function onSvgMousemove({
clientX,
clientY
}) {
cancelAnimationFrame(frame);
frame = requestAnimationFrame(() => {
tooltipTarget.value = [clientX, clientY];
});
}
function onSvgTouchstart({
touches
}) {
if (!touches) return;
const {
clientX,
clientY
} = touches[0];
tooltipTarget.value = [clientX, clientY];
}
function onSvgClickOutside() {
activeItemKey.value = null;
tooltipVisible.value = false;
}
return () => {
const segmentProps = pick(props, ['animation', 'gap', 'rounded', 'hideSlice', 'reveal', 'innerCut', 'hoverScale']);
const defaultTooltipTransition = {
name: 'fade-transition',
duration: 150
};
const tooltipProps = {
item: tooltipItem.value,
modelValue: tooltipVisible.value,
titleFormat: typeof props.tooltip === 'object' ? props.tooltip.titleFormat : '[title]',
subtitleFormat: typeof props.tooltip === 'object' ? props.tooltip.subtitleFormat : '[value]',
transition: typeof props.tooltip === 'object' ? props.tooltip.transition : defaultTooltipTransition,
offset: typeof props.tooltip === 'object' ? props.tooltip.offset : 16,
target: tooltipTarget.value
};
const legendDefaults = {
VChipGroup: {
direction: legendDirection.value
},
VChip: {
density: props.density
},
VAvatar: {
size: legendAvatarSize.value
}
};
const tooltipDefaults = {
VAvatar: {
size: typeof props.tooltip === 'object' ? props.tooltip.avatarSize ?? 28 : 28
}
};
const avatarSlot = ({
item
}) => _createVNode(VAvatar, {
"color": item.color,
"start": true
}, {
default: () => [item.pattern && _createElementVNode("svg", {
"height": "40",
"width": "40"
}, [_createElementVNode("rect", {
"width": "40",
"height": "40",
"fill": item.pattern
}, null)])]
});
return _createElementVNode("div", {
"class": _normalizeClass(['v-pie', `v-pie--legend-${legendMode.value}`]),
"style": {
'--v-pie-size': convertToUnit(props.size)
}
}, [slots.title?.() ?? (props.title && _createElementVNode("div", {
"class": "v-pie__title"
}, [props.title])), _createElementVNode("div", {
"class": _normalizeClass(['v-pie__content', colorClasses.value]),
"style": _normalizeStyle([{
transform: `rotate(${rotateDeg.value})`,
marginBottom: `calc(-1 * ${convertToUnit(props.size)} * ${gaugeOffset.value})`
}, textColorStyles.value])
}, [_createElementVNode("div", {
"class": _normalizeClass(['v-pie__content-underlay', colorClasses.value]),
"style": _normalizeStyle(colorStyles.value)
}, null), _withDirectives(_createElementVNode("svg", {
"xmlns": "http://www.w3.org/2000/svg",
"viewBox": "0 0 100 100",
"class": "v-pie__segments",
"onMousemove": onSvgMousemove,
"onTouchstart": onSvgTouchstart
}, [arcs.value.map((item, index) => _createVNode(VPieSegment, _mergeProps(segmentProps, {
"key": item.key,
"active": activeItemKey.value === item.key,
"color": item.color,
"value": isVisible(item) ? arcSize(item.value) : 0,
"rotate": arcOffset(index),
"pattern": item.pattern,
"onUpdate:active": val => setItemActive(item, val),
"onTouchend": () => setItemActive(item, true)
}), null))]), [[vClickOutside, {
handler: onSvgClickOutside
}]]), _createElementVNode("div", {
"class": "v-pie__center-content",
"style": {
transform: `translate(-50%, -50%)
rotate(-${rotateDeg.value})
translateY(calc(-100% * ${gaugeOffset.value}))`
}
}, [_createElementVNode("div", null, [slots.center?.({
total: total.value
})])])]), legendConfig.value.visible && _createVNode(VDefaultsProvider, {
"key": "legend",
"defaults": legendDefaults
}, {
default: () => [_createElementVNode("div", {
"class": "v-pie__legend"
}, [slots.legend?.({
isActive: isVisible,
toggle,
items: arcs.value,
total: total.value
}) ?? _createVNode(VChipGroup, {
"column": true,
"multiple": true,
"modelValue": visibleItemsKeys.value,
"onUpdate:modelValue": $event => visibleItemsKeys.value = $event
}, {
default: () => [arcs.value.map(item => _createVNode(VChip, {
"value": item.key
}, {
prepend: () => avatarSlot({
item
}),
default: () => _createElementVNode("div", {
"class": "v-pie__legend__text"
}, [slots['legend-text']?.({
item,
total: total.value
}) ?? legendTextFormatFunction.value(item)])
}))]
})])]
}), !!props.tooltip && _createVNode(VDefaultsProvider, {
"defaults": tooltipDefaults
}, {
default: () => [_createVNode(VPieTooltip, tooltipProps, {
default: slots.tooltip ? slotProps => slots.tooltip?.({
...slotProps,
total: total.value
}) : undefined,
prepend: avatarSlot
})]
})]);
};
}
});
//# sourceMappingURL=VPie.js.map
File diff suppressed because one or more lines are too long
+112
View File
@@ -0,0 +1,112 @@
@use '../../styles/tools'
@use './variables' as *
@include tools.layer('components')
.v-pie
display: grid
align-items: center
column-gap: 24px
--v-pie-size: 250px
&--legend
&-top
grid-template-areas: 'title' 'legend' 'content'
grid-template-columns: var(--v-pie-size)
&-bottom
grid-template-areas: 'title' 'content' 'legend'
grid-template-columns: var(--v-pie-size)
&-right
grid-template-areas: 'title .' 'content legend'
&-left
grid-template-areas: '. title' 'legend content'
&-hidden
grid-template-areas: 'title' 'content'
&__title
grid-area: title
text-align: center
padding-bottom: $pie-title-padding-bottom
&__content
grid-area: content
position: relative
width: var(--v-pie-size)
height: var(--v-pie-size)
@include tools.layer('overrides')
.v-overlay__scrim,
.v-overlay__content
pointer-events: none
@include tools.layer('trumps')
// expected to get bg-* class for text color
// actual background is applied to underlay
background: none
&__segments
border-radius: 50%
&__content-underlay
border-radius: 50%
position: absolute
inset: $pie-underlay-inset
pointer-events: none
z-index: -1
&__center-content
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
pointer-events: none
> div
pointer-events: auto
&__legend
grid-area: legend
padding-block: $pie-legend-padding-block
.v-avatar
border: $pie-legend-avatar-border
.v-chip__content
width: 100%
.v-chip-group .v-chip:not(.v-chip--selected)
opacity: $pie-legend-chip-disabled-opacity
&__text
font-size: $pie-legend-chip-default-font-size
white-space: nowrap
width: 100%
.v-chip--density-compact &
font-size: $pie-legend-chip-compact-font-size
.v-chip.v-chip--density-comfortable .v-avatar--start
margin-inline-start: -6px
.v-chip.v-chip--density-default .v-avatar--start
margin-inline-start: -4px
&-segment
pointer-events: none
position: absolute
inset: 0
.v-pie-segment__overlay
pointer-events: auto
opacity: 0
&__tooltip-content
.v-list-item
padding-inline: 0
min-width: $pie-tooltip-min-width
zoom: 0.88
.v-list-item-subtitle
opacity: 1
.v-avatar
border: $pie-tooltip-avatar-border
+337
View File
@@ -0,0 +1,337 @@
import { easingPatterns } from '../../util/index.js';
import type { PropType } from 'vue';
export declare const makeVPieSegmentProps: <Defaults extends {
reveal?: unknown;
active?: unknown;
rotate?: unknown;
value?: unknown;
color?: unknown;
innerCut?: unknown;
hoverScale?: unknown;
gap?: unknown;
rounded?: unknown;
animation?: unknown;
pattern?: unknown;
hideSlice?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
reveal: unknown extends Defaults["reveal"] ? {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["reveal"] ? boolean | {
duration?: number;
} : boolean | {
duration?: number;
} | Defaults["reveal"]>;
default: unknown extends Defaults["reveal"] ? boolean | {
duration?: number;
} : Defaults["reveal"] | NonNullable<boolean | {
duration?: number;
}>;
};
active: unknown extends Defaults["active"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["active"] ? boolean : boolean | Defaults["active"]>;
default: unknown extends Defaults["active"] ? boolean : boolean | Defaults["active"];
};
rotate: unknown extends Defaults["rotate"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["rotate"] ? string | number : string | number | Defaults["rotate"]>;
default: unknown extends Defaults["rotate"] ? string | number : Defaults["rotate"] | NonNullable<string | number>;
};
value: unknown extends Defaults["value"] ? {
type: NumberConstructor;
default: number;
} : Omit<{
type: NumberConstructor;
default: number;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["value"] ? number : number | Defaults["value"]>;
default: unknown extends Defaults["value"] ? number : number | Defaults["value"];
};
color: unknown extends Defaults["color"] ? StringConstructor : {
type: PropType<unknown extends Defaults["color"] ? string : string | Defaults["color"]>;
default: unknown extends Defaults["color"] ? string : string | Defaults["color"];
};
innerCut: unknown extends Defaults["innerCut"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["innerCut"] ? string | number : string | number | Defaults["innerCut"]>;
default: unknown extends Defaults["innerCut"] ? string | number : Defaults["innerCut"] | NonNullable<string | number>;
};
hoverScale: unknown extends Defaults["hoverScale"] ? {
type: (NumberConstructor | StringConstructor)[];
default: number;
} : Omit<{
type: (NumberConstructor | StringConstructor)[];
default: number;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["hoverScale"] ? string | number : string | number | Defaults["hoverScale"]>;
default: unknown extends Defaults["hoverScale"] ? string | number : Defaults["hoverScale"] | NonNullable<string | number>;
};
gap: unknown extends Defaults["gap"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["gap"] ? string | number : string | number | Defaults["gap"]>;
default: unknown extends Defaults["gap"] ? string | number : Defaults["gap"] | NonNullable<string | number>;
};
rounded: unknown extends Defaults["rounded"] ? (NumberConstructor | StringConstructor)[] : {
type: PropType<unknown extends Defaults["rounded"] ? string | number : string | number | Defaults["rounded"]>;
default: unknown extends Defaults["rounded"] ? string | number : Defaults["rounded"] | NonNullable<string | number>;
};
animation: unknown extends Defaults["animation"] ? {
type: PropType<boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
}>;
default: boolean;
} : Omit<{
type: PropType<boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
}>;
default: boolean;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["animation"] ? boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
} : boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
} | Defaults["animation"]>;
default: unknown extends Defaults["animation"] ? boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
} : Defaults["animation"] | NonNullable<boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
}>;
};
pattern: unknown extends Defaults["pattern"] ? StringConstructor : {
type: PropType<unknown extends Defaults["pattern"] ? string : string | Defaults["pattern"]>;
default: unknown extends Defaults["pattern"] ? string : string | Defaults["pattern"];
};
hideSlice: unknown extends Defaults["hideSlice"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["hideSlice"] ? boolean : boolean | Defaults["hideSlice"]>;
default: unknown extends Defaults["hideSlice"] ? boolean : boolean | Defaults["hideSlice"];
};
};
export declare const VPieSegment: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
} & {
rotate?: string | number | undefined;
color?: string | undefined;
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
pattern?: string | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((val: boolean) => any) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
'update:active': (val: boolean) => true;
}, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
}, true, {}, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
} & {
rotate?: string | number | undefined;
color?: string | undefined;
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
pattern?: string | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((val: boolean) => any) | undefined;
}, () => JSX.Element, {}, {}, {}, {
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
} & {
rotate?: string | number | undefined;
color?: string | undefined;
innerCut?: string | number | undefined;
gap?: string | number | undefined;
rounded?: string | number | undefined;
pattern?: string | undefined;
} & {
$children?: {
default?: (() => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | (() => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | (() => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | (() => import("vue").VNodeChild) | undefined;
} & {
"onUpdate:active"?: ((val: boolean) => any) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
'update:active': (val: boolean) => true;
}, string, {
reveal: boolean | {
duration?: number;
};
active: boolean;
value: number;
hoverScale: string | number;
animation: boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
};
hideSlice: boolean;
}, {}, string, import("vue").SlotsType<Partial<{
default: () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
reveal: {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
};
active: BooleanConstructor;
rotate: (NumberConstructor | StringConstructor)[];
value: {
type: NumberConstructor;
default: number;
};
color: StringConstructor;
innerCut: (NumberConstructor | StringConstructor)[];
hoverScale: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
gap: (NumberConstructor | StringConstructor)[];
rounded: (NumberConstructor | StringConstructor)[];
animation: {
type: PropType<boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
}>;
default: boolean;
};
pattern: StringConstructor;
hideSlice: BooleanConstructor;
}, import("vue").ExtractPropTypes<{
reveal: {
type: PropType<boolean | {
duration?: number;
}>;
default: boolean;
};
active: BooleanConstructor;
rotate: (NumberConstructor | StringConstructor)[];
value: {
type: NumberConstructor;
default: number;
};
color: StringConstructor;
innerCut: (NumberConstructor | StringConstructor)[];
hoverScale: {
type: (NumberConstructor | StringConstructor)[];
default: number;
};
gap: (NumberConstructor | StringConstructor)[];
rounded: (NumberConstructor | StringConstructor)[];
animation: {
type: PropType<boolean | {
duration?: number;
easing?: keyof typeof easingPatterns;
}>;
default: boolean;
};
pattern: StringConstructor;
hideSlice: BooleanConstructor;
}>>;
export type VPieSegment = InstanceType<typeof VPieSegment>;
+108
View File
@@ -0,0 +1,108 @@
import { createElementVNode as _createElementVNode } from "vue";
// Composables
import { useProxiedModel } from "../../composables/proxiedModel.js";
import { makeRevealProps, useReveal } from "../../composables/reveal.js"; // Utilities
import { computed, toRef } from 'vue';
import { useInnerSlicePath, useOuterSlicePath, usePieArc } from "./utils.js";
import { easingPatterns, genericComponent, propsFactory, useTransition } from "../../util/index.js"; // Types
export const makeVPieSegmentProps = propsFactory({
active: Boolean,
rotate: [Number, String],
value: {
type: Number,
default: 0
},
color: String,
innerCut: [Number, String],
hoverScale: {
type: [Number, String],
default: 0.05
},
gap: [Number, String],
rounded: [Number, String],
animation: {
type: [Boolean, Object],
default: false
},
pattern: String,
hideSlice: Boolean,
...makeRevealProps()
}, 'VPieSegment');
export const VPieSegment = genericComponent()({
name: 'VPieSegment',
props: makeVPieSegmentProps(),
emits: {
'update:active': val => true
},
setup(props) {
const isActive = useProxiedModel(props, 'active');
const {
state: revealState,
duration: revealDuration
} = useReveal(props);
const transitionConfig = computed(() => {
const defaultEasing = 'easeInOutCubic';
const defaultDuration = 400;
const easingName = typeof props.animation === 'object' ? props.animation.easing ?? defaultEasing : defaultEasing;
return {
duration: ['initial', 'pending'].includes(revealState.value) ? revealDuration.value : typeof props.animation === 'object' ? props.animation.duration : props.animation ? defaultDuration : 0,
transition: easingPatterns[easingName]
};
});
const {
hoverZoomRatio,
normalizedValue,
normalizedInnerCut,
outerX,
outerY,
arcWidth
} = usePieArc(props, isActive);
const arcSize = toRef(() => revealState.value === 'initial' ? 0 : normalizedValue.value);
const currentArcSize = useTransition(arcSize, transitionConfig);
const angle = toRef(() => revealState.value === 'initial' ? 0 : Number(props.rotate ?? 0) + Number(props.gap ?? 0) / 2);
const currentAngle = useTransition(angle, transitionConfig);
const arcRadius = toRef(() => 50 * (isActive.value ? 1 : 1 - hoverZoomRatio.value));
const currentArcRadius = useTransition(arcRadius, transitionConfig);
const currentArcWidth = useTransition(arcWidth, transitionConfig);
const outerSlicePath = useOuterSlicePath({
angle: currentAngle,
radius: currentArcRadius,
size: currentArcSize,
width: currentArcWidth,
rounded: () => Number(props.rounded ?? 0)
});
const innerSlicePath = useInnerSlicePath({
angle: currentAngle,
radius: () => currentArcRadius.value - currentArcWidth.value,
size: currentArcSize
});
const overlayPath = toRef(() => `M 50 0 A 50 50 0 ${normalizedValue.value > 50 ? 1 : 0} 1 ${outerX.value} ${outerY.value} L 50 50`);
return () => _createElementVNode("g", {
"class": "v-pie-segment",
"style": {
color: props.color
}
}, [_createElementVNode("path", {
"key": "outer-slice",
"fill": "currentColor",
"shape-rendering": "geometricPrecision",
"d": outerSlicePath.value
}, null), props.pattern && _createElementVNode("path", {
"key": "pattern-overlay",
"shape-rendering": "geometricPrecision",
"fill": props.pattern,
"d": outerSlicePath.value
}, null), !props.hideSlice && normalizedInnerCut.value > 0 && _createElementVNode("path", {
"key": "inner-slice",
"fill": "oklch(from currentColor l c h / calc(alpha / 2))",
"d": innerSlicePath.value
}, null), ['disabled', 'done'].includes(revealState.value) && _createElementVNode("path", {
"transform": `rotate(${currentAngle.value} 50 50)`,
"class": "v-pie-segment__overlay",
"d": overlayPath.value,
"onMouseenter": () => isActive.value = true,
"onMouseleave": () => isActive.value = false
}, null)]);
}
});
//# sourceMappingURL=VPieSegment.js.map
File diff suppressed because one or more lines are too long
+297
View File
@@ -0,0 +1,297 @@
import type { PropType } from 'vue';
import type { PieItem, TextTemplate } from './types.js';
export type VPieTooltipSlots = {
default: {
item: PieItem;
};
prepend: {
item: PieItem;
};
};
export declare const makeVPieTooltipProps: <Defaults extends {
offset?: unknown;
transition?: unknown;
modelValue?: unknown;
target?: unknown;
item?: unknown;
titleFormat?: unknown;
subtitleFormat?: unknown;
} = {}>(defaults?: Defaults | undefined) => {
offset: unknown extends Defaults["offset"] ? {
type: PropType<string | number | number[] | undefined>;
default: NonNullable<string | number | number[] | undefined>;
} : Omit<{
type: PropType<string | number | number[] | undefined>;
default: NonNullable<string | number | number[] | undefined>;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["offset"] ? string | number | number[] | undefined : string | number | number[] | Defaults["offset"] | undefined>;
default: unknown extends Defaults["offset"] ? string | number | number[] | undefined : Defaults["offset"] | NonNullable<string | number | number[] | undefined>;
};
transition: unknown extends Defaults["transition"] ? import("vue").Prop<string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null> : {
type: PropType<unknown extends Defaults["transition"] ? string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null : string | boolean | Defaults["transition"] | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null>;
default: unknown extends Defaults["transition"] ? string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null : Defaults["transition"] | NonNullable<string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null>;
};
modelValue: unknown extends Defaults["modelValue"] ? BooleanConstructor : {
type: PropType<unknown extends Defaults["modelValue"] ? boolean : boolean | Defaults["modelValue"]>;
default: unknown extends Defaults["modelValue"] ? boolean : boolean | Defaults["modelValue"];
};
target: unknown extends Defaults["target"] ? PropType<[x: number, y: number]> : {
type: PropType<unknown extends Defaults["target"] ? [x: number, y: number] : [x: number, y: number] | Defaults["target"]>;
default: unknown extends Defaults["target"] ? [x: number, y: number] : [x: number, y: number] | Defaults["target"];
};
item: unknown extends Defaults["item"] ? {
type: PropType<PieItem | null>;
default: null;
} : Omit<{
type: PropType<PieItem | null>;
default: null;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["item"] ? PieItem | null : PieItem | Defaults["item"] | null>;
default: unknown extends Defaults["item"] ? PieItem | null : PieItem | Defaults["item"];
};
titleFormat: unknown extends Defaults["titleFormat"] ? {
type: PropType<TextTemplate>;
default: string;
} : Omit<{
type: PropType<TextTemplate>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["titleFormat"] ? TextTemplate : Defaults["titleFormat"] | TextTemplate>;
default: unknown extends Defaults["titleFormat"] ? TextTemplate : Defaults["titleFormat"] | NonNullable<TextTemplate>;
};
subtitleFormat: unknown extends Defaults["subtitleFormat"] ? {
type: PropType<TextTemplate>;
default: string;
} : Omit<{
type: PropType<TextTemplate>;
default: string;
}, "default" | "type"> & {
type: PropType<unknown extends Defaults["subtitleFormat"] ? TextTemplate : Defaults["subtitleFormat"] | TextTemplate>;
default: unknown extends Defaults["subtitleFormat"] ? TextTemplate : Defaults["subtitleFormat"] | NonNullable<TextTemplate>;
};
};
export declare const VPieTooltip: {
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<{
offset: string | number | number[];
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
} & {
transition?: string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null | undefined;
target?: [x: number, y: number] | undefined;
} & {
$children?: {
default?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, {
offset: string | number | number[] | undefined;
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
}, true, {}, import("vue").SlotsType<Partial<{
default: (arg: {
item: PieItem;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
prepend: (arg: {
item: PieItem;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
P: {};
B: {};
D: {};
C: {};
M: {};
Defaults: {};
}, {
offset: string | number | number[];
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
} & {
transition?: string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null | undefined;
target?: [x: number, y: number] | undefined;
} & {
$children?: {
default?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, {}, {}, {}, {
offset: string | number | number[] | undefined;
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
}>;
__isFragment?: never;
__isTeleport?: never;
__isSuspense?: never;
} & import("vue").ComponentOptionsBase<{
offset: string | number | number[];
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
} & {
transition?: string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null | undefined;
target?: [x: number, y: number] | undefined;
} & {
$children?: {
default?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | {
$stable?: boolean;
} | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | import("vue").VNodeChild;
'v-slots'?: {
default?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
prepend?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
} | undefined;
} & {
"v-slot:default"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
"v-slot:prepend"?: false | ((arg: {
item: PieItem;
}) => import("vue").VNodeChild) | undefined;
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, Record<string, any>, string, {
offset: string | number | number[] | undefined;
modelValue: boolean;
item: PieItem | null;
titleFormat: TextTemplate;
subtitleFormat: TextTemplate;
}, {}, string, import("vue").SlotsType<Partial<{
default: (arg: {
item: PieItem;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
prepend: (arg: {
item: PieItem;
}) => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>[];
}>>, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & import("../../util/index.js").FilterPropsOptions<{
offset: {
type: PropType<string | number | number[] | undefined>;
default: NonNullable<string | number | number[] | undefined>;
};
transition: import("vue").Prop<string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null>;
modelValue: BooleanConstructor;
target: PropType<[x: number, y: number]>;
item: {
type: PropType<PieItem | null>;
default: null;
};
titleFormat: {
type: PropType<TextTemplate>;
default: string;
};
subtitleFormat: {
type: PropType<TextTemplate>;
default: string;
};
}, import("vue").ExtractPropTypes<{
offset: {
type: PropType<string | number | number[] | undefined>;
default: NonNullable<string | number | number[] | undefined>;
};
transition: import("vue").Prop<string | boolean | (import("vue").TransitionProps & {
component?: import("vue").Component;
}) | null>;
modelValue: BooleanConstructor;
target: PropType<[x: number, y: number]>;
item: {
type: PropType<PieItem | null>;
default: null;
};
titleFormat: {
type: PropType<TextTemplate>;
default: string;
};
subtitleFormat: {
type: PropType<TextTemplate>;
default: string;
};
}>>;
export type VPieTooltip = InstanceType<typeof VPieTooltip>;
+65
View File
@@ -0,0 +1,65 @@
import { createVNode as _createVNode } from "vue";
// Components
import { VListItem } from "../../components/VList/VListItem.js";
import { makeVTooltipProps, VTooltip } from "../../components/VTooltip/VTooltip.js"; // Composables
import { makeTransitionProps, MaybeTransition } from "../../composables/transition.js"; // Utilities
import { toRef } from 'vue';
import { formatTextTemplate } from "./utils.js";
import { genericComponent, pick, propsFactory } from "../../util/index.js"; // Types
export const makeVPieTooltipProps = propsFactory({
modelValue: Boolean,
target: Object,
item: {
type: Object,
default: null
},
titleFormat: {
type: [String, Function],
default: '[title]'
},
subtitleFormat: {
type: [String, Function],
default: '[value]'
},
...makeTransitionProps(),
...pick(makeVTooltipProps(), ['offset'])
}, 'VPieTooltip');
export const VPieTooltip = genericComponent()({
name: 'VPieTooltip',
props: makeVPieTooltipProps(),
setup(props, {
slots
}) {
const tooltipTitleFormatFunction = toRef(() => segment => {
return typeof props.titleFormat === 'function' ? props.titleFormat(segment) : formatTextTemplate(props.titleFormat, segment);
});
const tooltipSubtitleFormatFunction = toRef(() => segment => {
return typeof props.subtitleFormat === 'function' ? props.subtitleFormat(segment) : formatTextTemplate(props.subtitleFormat, segment);
});
return () => _createVNode(VTooltip, {
"offset": props.offset,
"modelValue": props.modelValue,
"target": props.target,
"contentClass": "v-pie__tooltip-content"
}, {
default: () => [!!props.item && (slots.default?.({
item: props.item
}) ?? _createVNode(MaybeTransition, {
"transition": props.transition,
"mode": "out-in"
}, {
default: () => [_createVNode(VListItem, {
"key": props.item.key,
"density": "compact",
"title": tooltipTitleFormatFunction.value(props.item),
"subtitle": tooltipSubtitleFormatFunction.value(props.item)
}, {
prepend: slots.prepend ? () => slots.prepend({
item: props.item
}) : undefined
})]
}))]
});
}
});
//# sourceMappingURL=VPieTooltip.js.map

Some files were not shown because too many files have changed in this diff Show More