vectis-ui 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/VAccordion/VAccordion.css +1 -1
- package/dist/components/VAccordion/VAccordion.d.ts +0 -6
- package/dist/components/VAccordion/VAccordion.js.map +1 -1
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
- package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAccordion/context.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.d.ts +0 -3
- package/dist/components/VAvatar/VAvatar.js.map +1 -1
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
- package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
- package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
- package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VAvatar/context.d.ts +0 -1
- package/dist/components/VAvatar/context.js.map +1 -1
- package/dist/components/VButton/VButton.d.ts +0 -6
- package/dist/components/VButton/VButton.js.map +1 -1
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
- package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.d.ts +0 -6
- package/dist/components/VButton/VButtonGroup.js.map +1 -1
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VButton/context.d.ts +0 -2
- package/dist/components/VButton/context.js.map +1 -1
- package/dist/components/VChip/VChip.d.ts +0 -3
- package/dist/components/VChip/VChip.js.map +1 -1
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.d.ts +0 -3
- package/dist/components/VColorInput/VColorInput.js.map +1 -1
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +60 -66
- package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.d.ts +0 -3
- package/dist/components/VCombobox/VCombobox.js.map +1 -1
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +127 -138
- package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
- package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.css +1 -1
- package/dist/components/VDataTable/VDataTable.d.ts +0 -2
- package/dist/components/VDataTable/VDataTable.js.map +1 -1
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -84
- package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.d.ts +0 -3
- package/dist/components/VDateInput/VDateInput.js.map +1 -1
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +32 -37
- package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.d.ts +0 -5
- package/dist/components/VFileInput/VFileInput.js.map +1 -1
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +75 -86
- package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
- package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.d.ts +0 -3
- package/dist/components/VIconButton/VIconButton.js.map +1 -1
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInput.css +1 -1
- package/dist/components/VInput/VInput.d.ts +0 -3
- package/dist/components/VInput/VInput.js.map +1 -1
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +18 -24
- package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/VInputGroup.d.ts +0 -3
- package/dist/components/VInput/VInputGroup.js.map +1 -1
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
- package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VInput/context.d.ts +0 -2
- package/dist/components/VInput/context.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
- package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
- package/dist/components/VListbox/VListboxPanel.js.map +1 -1
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.js.map +1 -1
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
- package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenu.d.ts +0 -3
- package/dist/components/VMenu/VMenu.js.map +1 -1
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
- package/dist/components/VMenu/VMenuPanel.js.map +1 -1
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
- package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VPagination/VPagination.d.ts +0 -3
- package/dist/components/VPagination/VPagination.js.map +1 -1
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSelect/VSelect.d.ts +0 -3
- package/dist/components/VSelect/VSelect.js.map +1 -1
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +114 -123
- package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.d.ts +0 -6
- package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +1 -10
- package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSideNavigation/context.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
- package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSlider/VSlider.js.map +1 -1
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +115 -120
- package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
- package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTab.js.map +1 -1
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
- package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/VTabs.css +1 -1
- package/dist/components/VTabs/VTabs.d.ts +0 -3
- package/dist/components/VTabs/VTabs.js.map +1 -1
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
- package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTabs/context.d.ts +0 -2
- package/dist/components/VTabs/context.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.d.ts +0 -6
- package/dist/components/VTextarea/VTextarea.js.map +1 -1
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
- package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +211 -218
- package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggle.d.ts +0 -3
- package/dist/components/VToggle/VToggle.js.map +1 -1
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +0 -6
- package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useControlShape.d.ts +1 -5
- package/dist/composables/useControlShape.js +0 -1
- package/dist/composables/useControlShape.js.map +1 -1
- package/dist/composables/useListbox.d.ts +0 -1
- package/dist/composables/useListbox.js +1 -1
- package/dist/composables/useListbox.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/semantic.d.ts +3 -0
- package/dist/tokens/semantic.js +6 -5
- package/dist/tokens/semantic.js.map +1 -1
- package/dist/tokens.json +12 -5
- package/dist/types.d.ts +0 -2
- package/dist/utils/chip.d.ts +4 -6
- package/dist/utils/chip.js +8 -5
- package/dist/utils/chip.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VChip.js","names":["_sfc_main","VChip_vue_vue_type_script_setup_true_lang_default","VChip_default"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Takes 4px off the height, leaving the padding, the text and the icons as they are. */\n compact?: boolean\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n compact: false,\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VChip.js","names":["_sfc_main","VChip_vue_vue_type_script_setup_true_lang_default","VChip_default"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -16,7 +16,6 @@ var M = [
|
|
|
16
16
|
"data-custom",
|
|
17
17
|
"data-shape",
|
|
18
18
|
"data-size",
|
|
19
|
-
"data-compact",
|
|
20
19
|
"data-selected",
|
|
21
20
|
"data-disabled",
|
|
22
21
|
"data-icon-only"
|
|
@@ -29,10 +28,6 @@ var M = [
|
|
|
29
28
|
color: { default: void 0 },
|
|
30
29
|
shape: { default: "chip" },
|
|
31
30
|
size: { default: "xs" },
|
|
32
|
-
compact: {
|
|
33
|
-
type: Boolean,
|
|
34
|
-
default: !1
|
|
35
|
-
},
|
|
36
31
|
clickable: {
|
|
37
32
|
type: Boolean,
|
|
38
33
|
default: !1
|
|
@@ -97,7 +92,6 @@ var M = [
|
|
|
97
92
|
"data-custom": c.color === void 0 ? void 0 : "",
|
|
98
93
|
"data-shape": c.shape,
|
|
99
94
|
"data-size": c.size,
|
|
100
|
-
"data-compact": c.compact ? "" : void 0,
|
|
101
95
|
"data-selected": c.selectable && F.value ? "" : void 0,
|
|
102
96
|
"data-disabled": c.disabled ? "" : void 0,
|
|
103
97
|
"data-icon-only": J() ? "" : void 0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VChip.vue_vue_type_script_setup_true_lang.js","names":["$emit","checkMark","closeIcon","_useModel","_useSlots","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createBlock","_resolveDynamicComponent","_mergeProps","_withCtx","VIcon","_normalizeProps","_renderSlot","_createCommentVNode","_createVNode","_guardReactiveProps","VIcon_default","_hoisted_1","_hoisted_2","VChip_vue_vue_type_script_setup_true_lang_default","check","close","\"selected\"","\"selectedModifiers\"","__expose","props","__props","m","useMessages","resolvedDismissLabel","computed","selected","useModel","slots","useSlots","consumerStyle","useRootAttrs","actionAttrs","useInertLink","forwardedAttrs","actionTag","isLink","rootStyle","customColorStyle","actionEl","ref","showCheck","iconOnly","flattenSlot","isDev","attrs","useAttrs","options","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","createBlock","resolveDynamicComponent","mergeProps","isInertLink","linkHref","_cache","withCtx","normalizeProps","iconProps","renderSlot","_ctx","createCommentVNode","createVNode","guardReactiveProps"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Takes 4px off the height, leaving the padding, the text and the icons as they are. */\n compact?: boolean\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n compact: false,\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-compact=\"compact ? '' : undefined\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VChip.vue_vue_type_script_setup_true_lang.js","names":["$emit","checkMark","closeIcon","_useModel","_useSlots","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createBlock","_resolveDynamicComponent","_mergeProps","_withCtx","VIcon","_normalizeProps","_renderSlot","_createCommentVNode","_createVNode","_guardReactiveProps","VIcon_default","_hoisted_1","_hoisted_2","VChip_vue_vue_type_script_setup_true_lang_default","check","close","\"selected\"","\"selectedModifiers\"","__expose","props","__props","m","useMessages","resolvedDismissLabel","computed","selected","useModel","slots","useSlots","consumerStyle","useRootAttrs","actionAttrs","useInertLink","forwardedAttrs","actionTag","isLink","rootStyle","customColorStyle","actionEl","ref","showCheck","iconOnly","flattenSlot","isDev","attrs","useAttrs","options","openBlock","createElementBlock","normalizeClass","unref","rootClass","normalizeStyle","createBlock","resolveDynamicComponent","mergeProps","isInertLink","linkHref","_cache","withCtx","normalizeProps","iconProps","renderSlot","_ctx","createCommentVNode","createVNode","guardReactiveProps"],"sources":["../../../src/components/VChip/VChip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Choose native links or buttons for chip actions. Dismissal is a sibling button because nested\n * buttons are invalid and inaccessible; inert links need a JS bridge.\n */\n\nimport { computed, onMounted, ref, useAttrs } from 'vue'\nimport type { StyleValue, VNode } from 'vue'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\n// Neither import may be named after a prop (`check`, `checkIcon`): every top-level binding of a\n// `<script setup>` reaches the template, where it shadows the prop of that name.\nimport { check as checkMark } from '../VIcon/icons/check'\nimport { close as closeIcon } from '../VIcon/icons/close'\nimport type { IconSource } from '../VIcon/types'\nimport { useMessages } from '../../i18n/state'\n\nimport { useInertLink } from '../../composables/useInertLink'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { customColorStyle } from '../../utils/css'\nimport { isDev } from '../../utils/env'\nimport { flattenSlot } from '../../utils/vnode'\n\n/** How much visual weight the chip carries. */\nexport type ChipVariant = 'soft' | 'solid' | 'outline'\n\n/** What the chip means, in colour. */\nexport type ChipTone = 'neutral' | 'accent' | 'danger' | 'success' | 'warning'\n\n/** The silhouette: softly rounded, or a full pill. */\nexport type ChipShape = 'chip' | 'pill'\n\n/** The height of the chip: 24 or 32 pixels. */\nexport type ChipSize = 'xs' | 'sm'\n\ninterface ChipProps {\n /**\n * How strongly the chip is painted: a tinted background, the full colour, or a\n * border alone.\n */\n variant?: ChipVariant\n /**\n * What the chip means, expressed as a colour: `neutral` by default, then `accent` for\n * something singled out and `danger`/ `success`/ `warning` for a state being reported.\n */\n tone?: ChipTone\n /** A colour of your own (hex, CSS name or `oklch()`), which REPLACES the tone. */\n color?: string\n /** The silhouette: softly rounded corners, or a full pill. */\n shape?: ChipShape\n /** The height of the chip. */\n size?: ChipSize\n /** Makes the chip a button that reacts to clicks, without holding a state. */\n clickable?: boolean\n /**\n * Where the chip leads, which makes it a link. A disabled link is made inert by\n * hand.\n */\n href?: string\n /**\n * Makes the chip something that stays chosen, bound to `v-model:selected`. It wins\n * over `href` and `clickable`.\n */\n selectable?: boolean\n /**\n * Shows a tick before the label while the chip is selected. It REPLACES whatever\n * start icon was given, so the two are never shown together.\n */\n check?: boolean\n /** The icon of that tick, a built-in check mark by default. `iconFilled` does not reach it. */\n checkIcon?: IconSource\n /** An icon before the label. The `#start` slot replaces it. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis). It has\n * no effect on the `#start`/`#end` slots, whose icons the consumer builds, nor on the tick\n * or the removal cross.\n */\n iconFilled?: boolean\n /**\n * Adds a button that asks for the chip to be removed. It only EMITS that request:\n * actually taking the chip away is the consumer's decision.\n */\n dismissible?: boolean\n /** The icon of that removal button. */\n dismissIcon?: IconSource\n /**\n * What the removal button does, in words. It falls back to the design system\n * dictionary.\n */\n dismissLabel?: string\n /** Makes the chip unusable, greyed out through the colour tokens. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ChipProps>(), {\n variant: 'soft',\n tone: 'neutral',\n color: undefined,\n shape: 'chip',\n size: 'xs',\n clickable: false,\n href: undefined,\n selectable: false,\n check: false,\n checkIcon: () => checkMark,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n dismissible: false,\n dismissIcon: () => closeIcon,\n dismissLabel: undefined,\n disabled: false,\n})\n\nconst m = useMessages()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? m.value.common.dismiss)\n\n/**\n * Whether the chip is selected, which is also what makes it selectable at all: binding this\n * turns it into a toggle button and takes precedence over `href` and `clickable`. It starts\n * unselected.\n */\nconst selected = defineModel<boolean>('selected', { default: false })\n\ndefineEmits<{\n /** The removal button was pressed. The chip is still there: removing it is up to you. */\n dismiss: []\n}>()\n\nconst slots = defineSlots<{\n /** The label. It may be left out entirely, which gives a chip made of icons alone. */\n default?(): unknown\n /** Content before the label, which takes the place of `iconStart`. */\n start?(): unknown\n /** Content after the label, which takes the place of `iconEnd`. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\n// The wrapper-root split: the pill is the box a consumer positions, and the ACTION is the\n// element that is focused, named and clicked, so everything else goes there.\nconst { rootClass, rootStyle: consumerStyle, forwardedAttrs } = useRootAttrs()\n\n/* A selectable chip is therefore never a link, whatever `href` says. */\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: actionAttrs,\n} = useInertLink({\n href: () => (props.selectable ? undefined : props.href),\n inert: () => props.disabled,\n attrs: () => forwardedAttrs.value,\n})\nconst actionTag = computed(() =>\n props.selectable ? 'button' : isLink.value ? 'a' : props.clickable ? 'button' : 'span',\n)\n\nconst rootStyle = computed<StyleValue>(() => [customColorStyle(props.color), consumerStyle.value])\n\nconst actionEl = ref<HTMLElement | null>(null)\n\nconst showCheck = computed(() => props.check && props.selectable && selected.value)\n\n// A function read by the template, never a `computed`: `slots` is not reactive, so a computed\n// would keep its first answer while a slot behind a `v-if` comes and goes.\n/**\n * An icon and no label at all: the chip becomes a square, as wide as it is tall. The default\n * slot is asked what it RENDERS, not whether it was passed: a wrapper forwarding an empty\n * label still hands one down.\n */\nfunction iconOnly() {\n return (\n flattenSlot(slots.default?.() as VNode[] | undefined).length === 0 &&\n !!(slots.start || slots.end || props.iconStart || props.iconEnd)\n )\n}\n\n// @devwarn\n// A chip drawn with an icon alone has no accessible name: the icon is decorative, so a\n// clickable, selectable or linked one is announced as a bare button or link. Nothing fails\n// visibly, hence the message.\nif (isDev) {\n const attrs = useAttrs()\n onMounted(() => {\n if (\n actionTag.value !== 'span' &&\n actionEl.value?.parentElement?.hasAttribute('data-icon-only') &&\n attrs['aria-label'] === undefined &&\n attrs['aria-labelledby'] === undefined\n ) {\n console.warn(\n '[VChip] a chip made of an icon alone has no accessible name: set aria-label or aria-labelledby on it.',\n )\n }\n })\n}\n\n// A template ref on the chip reaches the pill, a layout box; the element that is focused,\n// named and clicked is the action inside it.\ndefineExpose({\n /** Moves the focus to the chip's action: its button or link. */\n focus: (options?: FocusOptions) => actionEl.value?.focus(options),\n /** The action element, which is also where the consumer's attributes land. */\n el: actionEl,\n})\n</script>\n\n<template>\n <span\n :class=\"['v-chip v-control v-tone v-variant', rootClass]\"\n :style=\"rootStyle\"\n :data-variant=\"variant\"\n :data-tone=\"tone\"\n :data-custom=\"color !== undefined ? '' : undefined\"\n :data-shape=\"shape\"\n :data-size=\"size\"\n :data-selected=\"selectable && selected ? '' : undefined\"\n :data-disabled=\"disabled ? '' : undefined\"\n :data-icon-only=\"iconOnly() ? '' : undefined\"\n >\n <component\n :is=\"actionTag\"\n ref=\"actionEl\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-pressed=\"selectable ? selected : undefined\"\n v-bind=\"actionAttrs\"\n class=\"v-chip-action\"\n :type=\"actionTag === 'button' ? 'button' : undefined\"\n :href=\"linkHref\"\n :disabled=\"actionTag === 'button' ? disabled : undefined\"\n @click=\"selectable && !disabled && (selected = !selected)\"\n >\n <VIcon v-if=\"showCheck\" v-bind=\"iconProps(checkIcon)\" />\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n <button\n v-if=\"dismissible\"\n type=\"button\"\n class=\"v-chip-dismiss\"\n :aria-label=\"resolvedDismissLabel\"\n :disabled=\"disabled\"\n @click=\"$emit('dismiss')\"\n >\n <VIcon v-bind=\"iconProps(dismissIcon)\" />\n </button>\n </span>\n</template>\n\n<style>\n@layer vectis.components {\n /* The height comes from the shared size class set on this element. The scale itself\n holds five steps; the component's type restricts it to the two smallest, a chip\n larger than that being a button. */\n .v-chip {\n display: inline-flex;\n align-items: center;\n block-size: var(--control-height);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-chip);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-chip[data-shape='pill'] {\n border-radius: var(--vectis-radius-pill);\n }\n\n /* The tone table itself lives in styles/tones.css, in a layer below the components.\n All five tones are taken exactly as they come; the only values redefined here are\n those of a custom colour, and they win over the shared table by LAYER rather than\n by order, so no bundling can change the outcome. */\n\n /* A custom colour replaces the tone entirely. The shared table derives the four values\n every adopter reads (styles/tones.css, `.v-tone[data-custom]`); the chip adds the\n three only it paints, its hover and active steps and its outline, from the same colour\n mixed with the theme's own text and surface. */\n .v-chip[data-custom] {\n --tone-bg-solid-hover: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 8%);\n --tone-bg-solid-active: color-mix(in oklab, var(--custom-color), var(--vectis-color-text) 14%);\n --tone-border-soft: color-mix(in oklab, var(--custom-color), var(--vectis-color-surface) 60%);\n }\n\n /*\n * It COMPOUNDS `.v-variant`: the variant table weighs (0,2,0) like this rule and lives in\n * another sheet, so at equal specificity the winner would be whichever one the consumer's\n * bundler put last. At (0,3,0) it wins by construction.\n */\n .v-variant.v-chip[data-selected] {\n background: var(--tone-bg-solid);\n color: var(--tone-text-solid);\n border-color: transparent;\n }\n\n /* Hovering and pressing are conditioned on the ACTION being a real control, which\n buys two things at once: a chip that merely displays a value never lights up, and\n hovering the removal button does not repaint the whole chip as though it were\n about to be activated. */\n /* The same steps as VButton's, rule for rule: an outline sits on the soft surface while\n hovered and one tint further when pressed, and that tint is where a soft chip starts\n on hover. Held in each sheet rather than in `variants.css`, which every consumer\n downloads whether or not it renders a single button or chip. */\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: var(--tone-bg-soft);\n }\n\n .v-chip[data-variant='outline']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ),\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:hover\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-chip[data-variant='soft']:not([data-disabled], [data-selected]):has(\n :is(button, a).v-chip-action:active\n ) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:hover),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:hover) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-chip[data-variant='solid']:not([data-disabled]):has(:is(button, a).v-chip-action:active),\n .v-chip[data-selected]:not([data-disabled]):has(:is(button, a).v-chip-action:active) {\n background: var(--tone-bg-solid-active);\n }\n\n .v-chip-action {\n display: inline-flex;\n align-items: center;\n gap: var(--control-gap);\n block-size: 100%;\n padding-block: 0;\n padding-inline: var(--control-padding-inline);\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n border-radius: inherit;\n text-decoration: none;\n cursor: default;\n }\n\n /* With an icon and no label the action becomes square, its width taken from its own\n height through the ratio rather than from a new measurement to keep in step. */\n .v-chip[data-icon-only] .v-chip-action {\n aspect-ratio: 1;\n justify-content: center;\n padding-inline: 0;\n }\n\n :is(button, a).v-chip-action {\n cursor: pointer;\n }\n\n :is(button, a).v-chip-action:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /*\n * The cross inherits the chip's text colour rather than a muted one: drawn from the icon font,\n * it is real text, to which the text contrast rule applies.\n */\n .v-chip-dismiss {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n inline-size: var(--control-action-size);\n block-size: var(--control-action-size);\n margin-inline: calc(var(--vectis-space-1) * -1) var(--vectis-space-1);\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n border-radius: var(--vectis-radius-pill);\n cursor: pointer;\n flex: none;\n }\n\n .v-chip-dismiss:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: calc(-1 * var(--vectis-focus-ring-width));\n }\n\n /*\n * A disabled chip greys out through the colour tokens, which makes it follow the dark theme\n * without a rule of its own.\n */\n .v-variant.v-chip[data-disabled] {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n border-color: transparent;\n }\n\n .v-variant.v-chip[data-disabled][data-variant='outline'] {\n background: transparent;\n border-color: var(--vectis-color-border);\n }\n\n .v-chip[data-disabled] :is(:is(button, a).v-chip-action, .v-chip-dismiss) {\n cursor: not-allowed;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-chip {\n transition: none;\n }\n }\n\n /*\n * The class is repeated to reach (0,6,0). The variant, hover and active rules reach (0,5,0),\n * and with the forcing turned off any of them that still won would paint its tone over the\n * selection, with HighlightText on top of it.\n */\n @media (forced-colors: active) {\n .v-chip.v-chip.v-chip.v-chip[data-selected]:not([data-disabled]) {\n forced-color-adjust: none;\n background-color: Highlight;\n color: HighlightText;\n border-color: Highlight;\n outline-color: CanvasText;\n }\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -38,8 +38,6 @@ interface ColorInputProps {
|
|
|
38
38
|
placeholder?: string;
|
|
39
39
|
/** The height of the field: 32, 40 or 48 pixels. */
|
|
40
40
|
size?: ColorInputSize;
|
|
41
|
-
/** Takes 4px off the height. */
|
|
42
|
-
compact?: boolean;
|
|
43
41
|
/** Makes the field unusable. */
|
|
44
42
|
disabled?: boolean;
|
|
45
43
|
/** Shows the colour without letting it change: nothing can be typed, and there is no picker. */
|
|
@@ -90,7 +88,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
|
|
|
90
88
|
pickerLabel: string;
|
|
91
89
|
error: string;
|
|
92
90
|
size: ColorInputSize;
|
|
93
|
-
compact: boolean;
|
|
94
91
|
disabled: boolean;
|
|
95
92
|
required: boolean;
|
|
96
93
|
placement: ColorInputPlacement;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VColorInput.js","names":["_sfc_main","VColorInput_vue_vue_type_script_setup_true_lang_default","VColorInput_default"],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'top'\n | 'top-start'\n | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Takes 4px off the height. */\n compact?: boolean\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n compact: false,\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :compact=\"resolvedCompact\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"VColorInput.js","names":["_sfc_main","VColorInput_vue_vue_type_script_setup_true_lang_default","VColorInput_default"],"sources":["../../../src/components/VColorInput/VColorInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y @core\n/**\n * Compose VInput with VColorPicker. The colour is typed into the field in any format, and the\n * swatch at its start opens the picker in a panel; a text input cannot use popovertarget, so the\n * manual panel bridge handles opening, dismissal and focus transfer into the picker.\n */\nimport { computed, inject, ref, watch } from 'vue'\n\nimport VColorPicker from '../VColorPicker/VColorPicker.vue'\nimport type { ColorFormat, ColorSwatch } from '../VColorPicker/VColorPicker.vue'\nimport { formatColor, parseColor } from '../VColorPicker/color'\nimport type { FieldLabelPosition } from '../VField/VField.vue'\nimport VInput from '../VInput/VInput.vue'\nimport { inputGroupKey } from '../VInput/context'\nimport VPopover from '../VPopover/VPopover.vue'\nimport { canClear } from '../../composables/useClearable'\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useFieldPanel } from '../../composables/useFieldPanel'\nimport { useRootAttrs } from '../../composables/useRootAttrs'\nimport { useMessages } from '../../i18n/state'\n\n/** The height of the field: 32, 40 or 48 pixels. */\nexport type ColorInputSize = 'sm' | 'md' | 'lg'\n\n/** Where the picker opens relative to the field. */\nexport type ColorInputPlacement =\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'top'\n | 'top-start'\n | 'top-end'\n\ninterface ColorInputProps {\n /**\n * How the value is written: `#rrggbb`, `rgb()`, `hsl()` or `oklch()`. The field accepts all\n * four, and rewrites what was typed in this one when the reader leaves it.\n */\n format?: ColorFormat\n /** Offers an opacity track, and writes the alpha into the value when it is below 1. */\n alpha?: boolean\n /** Preset colours, offered as swatches in the picker. */\n swatches?: ColorSwatch[]\n /** Hides the picker's eyedropper button. */\n hideEyeDropper?: boolean\n /** The label above the field. */\n label?: string\n /** A line of help under the field. */\n hint?: string\n /**\n * A message saying what is wrong with the value, shown in place of the hint. It marks the\n * field invalid, is read along with it, and is announced when it appears or changes.\n */\n error?: string\n /** Marks the field as required: an asterisk follows the label, and the field is `required`. */\n required?: boolean\n /** Hides the label visually. It still names the field for assistive technology. */\n hideLabel?: boolean\n /**\n * Where the label sits: above the field (`top`, the default), or at its start, in a column of\n * its own, moving back above when there is not room for both.\n */\n labelPosition?: FieldLabelPosition\n /** What the field says while empty. */\n placeholder?: string\n /** The height of the field: 32, 40 or 48 pixels. */\n size?: ColorInputSize\n /** Makes the field unusable. */\n disabled?: boolean\n /** Shows the colour without letting it change: nothing can be typed, and there is no picker. */\n readonly?: boolean\n /** Marks the field as invalid, for a rule of your own. */\n invalid?: boolean\n /** Offers a cross that empties the value. */\n clearable?: boolean\n /** What that cross does, in words. It falls back to the design system dictionary. */\n clearLabel?: string\n /**\n * What the swatch at the start of the field does, in words. It falls back to the design\n * system dictionary.\n */\n pickerLabel?: string\n /** Where the picker opens relative to the field. */\n placement?: ColorInputPlacement\n}\n\nconst props = withDefaults(defineProps<ColorInputProps>(), {\n format: 'hex',\n alpha: false,\n swatches: undefined,\n hideEyeDropper: false,\n label: undefined,\n hint: undefined,\n error: undefined,\n required: false,\n hideLabel: false,\n labelPosition: 'top',\n placeholder: undefined,\n size: 'md',\n disabled: false,\n readonly: false,\n invalid: false,\n clearable: false,\n clearLabel: undefined,\n pickerLabel: undefined,\n placement: 'bottom-start',\n})\n\n/** The colour, written in `format`, or `null` when the field is empty. */\nconst model = defineModel<string | null>({ default: null })\n\nconst emit = defineEmits<{\n /** The clear cross emptied the field. The value has already been reset. */\n clear: []\n}>()\n\ndefineSlots<{\n /** Controls of your own inside the field, placed before the clear cross. */\n 'value-end'?(): unknown\n}>()\n\n// `class` and `style` stay on the wrapper; everything else goes down to the text field, which a\n// consumer's label points at and what assistive technology deals with.\ndefineOptions({ inheritAttrs: false })\nconst { rootClass, rootStyle, forwardedAttrs } = useRootAttrs()\n\nconst m = useMessages()\n\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelRef = ref<InstanceType<typeof VPopover> | null>(null)\nconst inputRef = ref<InstanceType<typeof VInput> | null>(null)\nconst pickerRef = ref<InstanceType<typeof VColorPicker> | null>(null)\n\n// A VInputGroup joins several controls into one object, so the shape of this field is the\n// row's decision rather than its own (VInput/context.ts).\nconst group = inject(inputGroupKey, null)\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape(props, group)\n\nconst hasPanel = computed(() => !props.readonly)\n\nconst {\n open,\n panelId,\n popupAttrs,\n closeAndFocus,\n focusField,\n toggleFromIcon,\n onFocusout,\n onKeydown,\n onPanelMousedown,\n} = useFieldPanel({\n rootEl,\n panelRef,\n field: inputRef,\n disabled: () => resolvedDisabled.value || !hasPanel.value,\n focusInPanel: () => pickerRef.value?.focus(),\n})\n\nconst inputAttrs = popupAttrs(\n () => forwardedAttrs.value,\n () => hasPanel.value,\n)\n\n/** What the field shows: the value, or what is being typed until the reader leaves. */\nconst fieldText = ref('')\nwatch(model, (value) => (fieldText.value = value ?? ''), { immediate: true })\n\nconst parsed = computed(() => parseColor(model.value))\n\n/** The colour the swatch shows, with the opacity it has. */\nconst swatchColor = computed(() =>\n parsed.value ? formatColor(parsed.value, 'rgb', props.alpha) : undefined,\n)\n\n/**\n * Takes what was typed, in any of the four formats, and rewrites it in `format`. Anything that\n * is not a colour puts the field back as it was.\n */\nfunction commitField() {\n const text = fieldText.value.trim()\n if (!text) {\n model.value = null\n return\n }\n const color = parseColor(text)\n if (!color) {\n fieldText.value = model.value ?? ''\n return\n }\n const next = formatColor(color, props.format, props.alpha)\n model.value = next\n // The same value written again changes nothing, so the watcher would leave the text as typed.\n fieldText.value = next\n}\n\n// @keyboard\n// Enter takes the value and stays in the field; without it, the panel bridge would open the\n// picker. The down arrow remains the way into the picker.\nfunction onFieldKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter') return\n event.preventDefault()\n commitField()\n}\n\n// @keyboard\n// Enter on a track or a swatch is done with choosing: the panel closes and the focus returns to\n// the field. A button's Enter is left alone, being its click.\nfunction onPickerKeydown(event: KeyboardEvent) {\n if (event.key !== 'Enter' || !(event.target as HTMLElement).matches('input')) return\n event.preventDefault()\n closeAndFocus()\n}\n\nconst clearVisible = computed(() =>\n canClear(props, resolvedDisabled.value, !!model.value || !!fieldText.value),\n)\n\nfunction clearValue() {\n model.value = null\n fieldText.value = ''\n focusField()\n emit('clear')\n}\n\ndefineExpose({\n /** Moves the focus to the text field. */\n focus: (options?: FocusOptions) => inputRef.value?.focus(options),\n /** Selects what the field is showing. */\n select: () => inputRef.value?.select(),\n /** The real `<input>` behind the field. */\n el: computed(() => inputRef.value?.el ?? null),\n})\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n class=\"v-color-input\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n :data-open=\"open ? '' : undefined\"\n :data-disabled=\"resolvedDisabled ? '' : undefined\"\n @focusout=\"onFocusout\"\n @keydown=\"onKeydown\"\n >\n <VInput\n ref=\"inputRef\"\n v-model=\"fieldText\"\n class=\"v-color-input-field\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n v-bind=\"inputAttrs\"\n :readonly=\"readonly\"\n :label=\"label\"\n :hint=\"hint\"\n :error=\"error\"\n :required=\"required\"\n :hide-label=\"hideLabel\"\n :label-position=\"labelPosition\"\n :placeholder=\"placeholder\"\n :size=\"resolvedSize\"\n :disabled=\"resolvedDisabled\"\n :invalid=\"invalid\"\n :clearable=\"clearable\"\n :clear-visible=\"clearVisible\"\n :clear-label=\"clearLabel ?? m.colorInput.clear\"\n @clear=\"clearValue\"\n @change=\"commitField\"\n @keydown=\"onFieldKeydown\"\n >\n <template #start>\n <button\n v-if=\"hasPanel\"\n type=\"button\"\n class=\"v-field-action v-color-input-swatch\"\n :aria-label=\"pickerLabel ?? m.colorInput.openPicker\"\n :disabled=\"resolvedDisabled\"\n @click=\"toggleFromIcon\"\n >\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </button>\n <span v-else class=\"v-color-input-swatch\" aria-hidden=\"true\">\n <span class=\"v-color-input-chip\" :style=\"{ '--color-input-chip': swatchColor }\" />\n </span>\n </template>\n <template v-if=\"$slots['value-end']\" #value-end><slot name=\"value-end\" /></template>\n </VInput>\n\n <VPopover\n v-if=\"hasPanel\"\n :id=\"panelId\"\n ref=\"panelRef\"\n v-model:open=\"open\"\n mode=\"manual\"\n anchor=\"--color-input-anchor\"\n :placement=\"placement\"\n role=\"dialog\"\n :aria-label=\"label ?? m.colorInput.pickerLabel\"\n class=\"v-color-input-panel\"\n @mousedown=\"onPanelMousedown\"\n >\n <VColorPicker\n ref=\"pickerRef\"\n v-model=\"model\"\n :format=\"format\"\n :alpha=\"alpha\"\n :swatches=\"swatches\"\n :hide-eye-dropper=\"hideEyeDropper\"\n hide-input\n @keydown=\"onPickerKeydown\"\n />\n </VPopover>\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n .v-color-input {\n anchor-scope: --color-input-anchor;\n display: block;\n inline-size: 100%;\n font-family: var(--vectis-text-family);\n }\n\n /*\n * Anchored to the field's box rather than the wrapper, which also holds the label and the\n * hint: the panel then covers whichever of the two it lands on.\n */\n .v-color-input .v-input-field {\n anchor-name: --color-input-anchor;\n }\n\n /* A colour is written left to right in every language, as in VColorPicker's own field. */\n .v-color-input .v-input-control {\n direction: ltr;\n font-variant-numeric: tabular-nums;\n }\n\n .v-color-input .v-input-control:dir(rtl) {\n text-align: right;\n }\n\n /* The read-only swatch, which is no button and takes none of `.v-field-action`. */\n span.v-color-input-swatch {\n display: inline-flex;\n flex: none;\n }\n\n /*\n * The checkerboard shows through a translucent colour, drawn in the theme's own surface and\n * border colours; an empty field shows it alone. Forced colours leave the chip untouched: it\n * is the value, not the theme's paint. As on VColorPicker's swatches, the edge is a\n * translucent border shading the colour that runs under it, the checkerboard stopping inside:\n * an opaque ring would leave a pale fringe on the antialiased pixels.\n */\n .v-color-input-chip {\n inline-size: var(--vectis-icon-size);\n block-size: var(--vectis-icon-size);\n border: 1px solid var(--vectis-color-border-on-fill);\n border-radius: var(--vectis-radius-interactive);\n background:\n linear-gradient(var(--color-input-chip, transparent), var(--color-input-chip, transparent))\n border-box,\n repeating-conic-gradient(var(--vectis-color-border) 0 25%, var(--vectis-color-surface) 0 50%)\n 0 0 / var(--vectis-control-size-color-picker-checker)\n var(--vectis-control-size-color-picker-checker) padding-box;\n forced-color-adjust: none;\n }\n\n .v-color-input[data-disabled] .v-color-input-chip {\n opacity: 0.5;\n }\n\n /*\n * `position-anchor` and the chrome come from VPopover; the panel's own padding is cancelled,\n * VColorPicker padding itself as the date and time pickers do.\n */\n .v-popover-panel.v-color-input-panel {\n inline-size: max-content;\n padding: 0;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -8,16 +8,16 @@ import ee from "../VInput/VInput.js";
|
|
|
8
8
|
import { useFieldPanel as te } from "../../composables/useFieldPanel.js";
|
|
9
9
|
import { formatColor as o, parseColor as s } from "../VColorPicker/color.js";
|
|
10
10
|
import c from "../VColorPicker/VColorPicker.js";
|
|
11
|
-
import { computed as l, createBlock as u, createCommentVNode as
|
|
11
|
+
import { computed as l, createBlock as u, createCommentVNode as d, createElementBlock as f, createElementVNode as p, createSlots as m, createVNode as h, defineComponent as g, inject as _, isRef as v, mergeModels as y, mergeProps as b, normalizeClass as x, normalizeStyle as S, openBlock as C, ref as w, renderSlot as ne, unref as T, useModel as re, watch as ie, withCtx as E } from "vue";
|
|
12
12
|
//#region src/components/VColorInput/VColorInput.vue?vue&type=script&setup=true&lang.ts
|
|
13
13
|
var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
14
14
|
key: 1,
|
|
15
15
|
class: "v-color-input-swatch",
|
|
16
16
|
"aria-hidden": "true"
|
|
17
|
-
}, D = /*@__PURE__*/
|
|
17
|
+
}, D = /*@__PURE__*/ g({
|
|
18
18
|
inheritAttrs: !1,
|
|
19
19
|
__name: "VColorInput",
|
|
20
|
-
props: /*@__PURE__*/
|
|
20
|
+
props: /*@__PURE__*/ y({
|
|
21
21
|
format: { default: "hex" },
|
|
22
22
|
alpha: {
|
|
23
23
|
type: Boolean,
|
|
@@ -42,10 +42,6 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
42
42
|
labelPosition: { default: "top" },
|
|
43
43
|
placeholder: { default: void 0 },
|
|
44
44
|
size: { default: "md" },
|
|
45
|
-
compact: {
|
|
46
|
-
type: Boolean,
|
|
47
|
-
default: !1
|
|
48
|
-
},
|
|
49
45
|
disabled: {
|
|
50
46
|
type: Boolean,
|
|
51
47
|
default: !1
|
|
@@ -69,15 +65,15 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
69
65
|
modelValue: { default: null },
|
|
70
66
|
modelModifiers: {}
|
|
71
67
|
}),
|
|
72
|
-
emits: /*@__PURE__*/
|
|
73
|
-
setup(
|
|
74
|
-
let O =
|
|
68
|
+
emits: /*@__PURE__*/ y(["clear"], ["update:modelValue"]),
|
|
69
|
+
setup(g, { expose: y, emit: D }) {
|
|
70
|
+
let O = g, k = re(g, "modelValue"), A = D, { rootClass: j, rootStyle: M, forwardedAttrs: N } = r(), P = e(), F = w(null), I = w(null), L = w(null), R = w(null), z = _(a, null), { size: B, disabled: V } = n(O, z), H = l(() => !O.readonly), { open: U, panelId: W, popupAttrs: G, closeAndFocus: K, focusField: ce, toggleFromIcon: q, onFocusout: J, onKeydown: Y, onPanelMousedown: le } = te({
|
|
75
71
|
rootEl: F,
|
|
76
72
|
panelRef: I,
|
|
77
73
|
field: L,
|
|
78
|
-
disabled: () =>
|
|
74
|
+
disabled: () => V.value || !H.value,
|
|
79
75
|
focusInPanel: () => R.value?.focus()
|
|
80
|
-
}),
|
|
76
|
+
}), ue = G(() => N.value, () => H.value), X = w("");
|
|
81
77
|
ie(k, (e) => X.value = e ?? "", { immediate: !0 });
|
|
82
78
|
let Z = l(() => s(k.value)), Q = l(() => Z.value ? o(Z.value, "rgb", O.alpha) : void 0);
|
|
83
79
|
function $() {
|
|
@@ -94,30 +90,30 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
94
90
|
let n = o(t, O.format, O.alpha);
|
|
95
91
|
k.value = n, X.value = n;
|
|
96
92
|
}
|
|
97
|
-
function
|
|
93
|
+
function de(e) {
|
|
98
94
|
e.key === "Enter" && (e.preventDefault(), $());
|
|
99
95
|
}
|
|
100
|
-
function
|
|
101
|
-
e.key === "Enter" && e.target.matches("input") && (e.preventDefault(),
|
|
96
|
+
function fe(e) {
|
|
97
|
+
e.key === "Enter" && e.target.matches("input") && (e.preventDefault(), K());
|
|
102
98
|
}
|
|
103
|
-
let
|
|
104
|
-
function
|
|
105
|
-
k.value = null, X.value = "",
|
|
99
|
+
let pe = l(() => i(O, V.value, !!k.value || !!X.value));
|
|
100
|
+
function me() {
|
|
101
|
+
k.value = null, X.value = "", ce(), A("clear");
|
|
106
102
|
}
|
|
107
|
-
return
|
|
103
|
+
return y({
|
|
108
104
|
focus: (e) => L.value?.focus(e),
|
|
109
105
|
select: () => L.value?.select(),
|
|
110
106
|
el: l(() => L.value?.el ?? null)
|
|
111
|
-
}), (e, n) => (
|
|
107
|
+
}), (e, n) => (C(), f("div", {
|
|
112
108
|
ref_key: "rootEl",
|
|
113
109
|
ref: F,
|
|
114
|
-
class:
|
|
115
|
-
style:
|
|
116
|
-
"data-open": T(
|
|
117
|
-
"data-disabled": T(
|
|
110
|
+
class: x(["v-color-input", T(j)]),
|
|
111
|
+
style: S(T(M)),
|
|
112
|
+
"data-open": T(U) ? "" : void 0,
|
|
113
|
+
"data-disabled": T(V) ? "" : void 0,
|
|
118
114
|
onFocusout: n[4] ||= (...e) => T(J) && T(J)(...e),
|
|
119
115
|
onKeydown: n[5] ||= (...e) => T(Y) && T(Y)(...e)
|
|
120
|
-
}, [
|
|
116
|
+
}, [h(ee, b({
|
|
121
117
|
ref_key: "inputRef",
|
|
122
118
|
ref: L,
|
|
123
119
|
modelValue: X.value,
|
|
@@ -125,44 +121,43 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
125
121
|
class: "v-color-input-field",
|
|
126
122
|
autocomplete: "off",
|
|
127
123
|
spellcheck: "false"
|
|
128
|
-
}, T(
|
|
129
|
-
readonly:
|
|
130
|
-
label:
|
|
131
|
-
hint:
|
|
132
|
-
error:
|
|
133
|
-
required:
|
|
134
|
-
"hide-label":
|
|
135
|
-
"label-position":
|
|
136
|
-
placeholder:
|
|
124
|
+
}, T(ue), {
|
|
125
|
+
readonly: g.readonly,
|
|
126
|
+
label: g.label,
|
|
127
|
+
hint: g.hint,
|
|
128
|
+
error: g.error,
|
|
129
|
+
required: g.required,
|
|
130
|
+
"hide-label": g.hideLabel,
|
|
131
|
+
"label-position": g.labelPosition,
|
|
132
|
+
placeholder: g.placeholder,
|
|
137
133
|
size: T(B),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"clear-
|
|
143
|
-
|
|
144
|
-
onClear: he,
|
|
134
|
+
disabled: T(V),
|
|
135
|
+
invalid: g.invalid,
|
|
136
|
+
clearable: g.clearable,
|
|
137
|
+
"clear-visible": pe.value,
|
|
138
|
+
"clear-label": g.clearLabel ?? T(P).colorInput.clear,
|
|
139
|
+
onClear: me,
|
|
145
140
|
onChange: $,
|
|
146
|
-
onKeydown:
|
|
147
|
-
}),
|
|
148
|
-
start: E(() => [
|
|
141
|
+
onKeydown: de
|
|
142
|
+
}), m({
|
|
143
|
+
start: E(() => [H.value ? (C(), f("button", {
|
|
149
144
|
key: 0,
|
|
150
145
|
type: "button",
|
|
151
146
|
class: "v-field-action v-color-input-swatch",
|
|
152
|
-
"aria-label":
|
|
153
|
-
disabled: T(
|
|
147
|
+
"aria-label": g.pickerLabel ?? T(P).colorInput.openPicker,
|
|
148
|
+
disabled: T(V),
|
|
154
149
|
onClick: n[0] ||= (...e) => T(q) && T(q)(...e)
|
|
155
|
-
}, [
|
|
150
|
+
}, [p("span", {
|
|
156
151
|
class: "v-color-input-chip",
|
|
157
|
-
style:
|
|
158
|
-
}, null, 4)], 8, oe)) : (
|
|
152
|
+
style: S({ "--color-input-chip": Q.value })
|
|
153
|
+
}, null, 4)], 8, oe)) : (C(), f("span", se, [p("span", {
|
|
159
154
|
class: "v-color-input-chip",
|
|
160
|
-
style:
|
|
155
|
+
style: S({ "--color-input-chip": Q.value })
|
|
161
156
|
}, null, 4)]))]),
|
|
162
157
|
_: 2
|
|
163
158
|
}, [e.$slots["value-end"] ? {
|
|
164
159
|
name: "value-end",
|
|
165
|
-
fn: E(() => [
|
|
160
|
+
fn: E(() => [ne(e.$slots, "value-end")]),
|
|
166
161
|
key: "0"
|
|
167
162
|
} : void 0]), 1040, [
|
|
168
163
|
"modelValue",
|
|
@@ -175,38 +170,37 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
175
170
|
"label-position",
|
|
176
171
|
"placeholder",
|
|
177
172
|
"size",
|
|
178
|
-
"compact",
|
|
179
173
|
"disabled",
|
|
180
174
|
"invalid",
|
|
181
175
|
"clearable",
|
|
182
176
|
"clear-visible",
|
|
183
177
|
"clear-label"
|
|
184
|
-
]),
|
|
178
|
+
]), H.value ? (C(), u(t, {
|
|
185
179
|
key: 0,
|
|
186
|
-
id: T(
|
|
180
|
+
id: T(W),
|
|
187
181
|
ref_key: "panelRef",
|
|
188
182
|
ref: I,
|
|
189
|
-
open: T(
|
|
190
|
-
"onUpdate:open": n[3] ||= (e) =>
|
|
183
|
+
open: T(U),
|
|
184
|
+
"onUpdate:open": n[3] ||= (e) => v(U) ? U.value = e : null,
|
|
191
185
|
mode: "manual",
|
|
192
186
|
anchor: "--color-input-anchor",
|
|
193
|
-
placement:
|
|
187
|
+
placement: g.placement,
|
|
194
188
|
role: "dialog",
|
|
195
|
-
"aria-label":
|
|
189
|
+
"aria-label": g.label ?? T(P).colorInput.pickerLabel,
|
|
196
190
|
class: "v-color-input-panel",
|
|
197
|
-
onMousedown: T(
|
|
191
|
+
onMousedown: T(le)
|
|
198
192
|
}, {
|
|
199
|
-
default: E(() => [
|
|
193
|
+
default: E(() => [h(c, {
|
|
200
194
|
ref_key: "pickerRef",
|
|
201
195
|
ref: R,
|
|
202
196
|
modelValue: k.value,
|
|
203
197
|
"onUpdate:modelValue": n[2] ||= (e) => k.value = e,
|
|
204
|
-
format:
|
|
205
|
-
alpha:
|
|
206
|
-
swatches:
|
|
207
|
-
"hide-eye-dropper":
|
|
198
|
+
format: g.format,
|
|
199
|
+
alpha: g.alpha,
|
|
200
|
+
swatches: g.swatches,
|
|
201
|
+
"hide-eye-dropper": g.hideEyeDropper,
|
|
208
202
|
"hide-input": "",
|
|
209
|
-
onKeydown:
|
|
203
|
+
onKeydown: fe
|
|
210
204
|
}, null, 8, [
|
|
211
205
|
"modelValue",
|
|
212
206
|
"format",
|
|
@@ -221,7 +215,7 @@ var ae = ["data-open", "data-disabled"], oe = ["aria-label", "disabled"], se = {
|
|
|
221
215
|
"placement",
|
|
222
216
|
"aria-label",
|
|
223
217
|
"onMousedown"
|
|
224
|
-
])) :
|
|
218
|
+
])) : d("", !0)], 46, ae));
|
|
225
219
|
}
|
|
226
220
|
});
|
|
227
221
|
//#endregion
|