@sparklingopinions/sparkling-ui 0.8.0 → 0.9.1
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/Icon/resolver.js +3 -2
- package/dist/components/Icon/resolver.js.map +1 -1
- package/dist/components/PropertyList/Editable.js.map +1 -1
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js +7 -1
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js +189 -139
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Select/types.d.ts +5 -0
- package/dist/components/Tag/Tag.js.map +1 -1
- package/dist/components/Tag/Tag.vue.d.ts +10 -3
- package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js +39 -26
- package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,8 +7,8 @@ function i(e) {
|
|
|
7
7
|
}
|
|
8
8
|
var a = Object.keys(t), o = /* @__PURE__ */ new Map();
|
|
9
9
|
function s(e) {
|
|
10
|
-
let t = e.viewBox.endsWith(" 8 8")
|
|
11
|
-
|
|
10
|
+
let t = e.viewBox.endsWith(" 8 8"), r = (r, { attrs: i }) => n("svg", {
|
|
11
|
+
...i,
|
|
12
12
|
width: t ? 8 : 16,
|
|
13
13
|
height: t ? 8 : 16,
|
|
14
14
|
viewBox: e.viewBox,
|
|
@@ -20,6 +20,7 @@ function s(e) {
|
|
|
20
20
|
"stroke-linejoin": e.join,
|
|
21
21
|
innerHTML: e.body
|
|
22
22
|
});
|
|
23
|
+
return r.inheritAttrs = !1, r;
|
|
23
24
|
}
|
|
24
25
|
function c(n) {
|
|
25
26
|
let i = r?.(n);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","names":["resolver","setIconResolver","next","iconNames","iconSet","builtins","draw","small","icon","h","resolveIcon","own","name","key","iconAliases"],"sources":["../../../src/components/Icon/resolver.ts"],"sourcesContent":["import { type Component, type FunctionalComponent, h } from 'vue'\n\nimport { type IconDefinition, iconAliases, iconSet } from './iconSet'\n\n/** Turns an icon name into its component, e.g. an SVG file imported as a component. */\nexport type IconResolver = (name: string) => Component | undefined\n\nlet resolver: IconResolver | undefined\n\n/** Adds or replaces icons: names the resolver returns a component for win over the library's set. */\nexport function setIconResolver(next: IconResolver) {\n resolver = next\n}\n\n/** Every name in the library's set (not counting aliases). */\nexport const iconNames = Object.keys(iconSet)\n\nconst builtins = new Map<string, FunctionalComponent>()\n\n// Inline styles, so a colour class (stroke-* fill-*) only paints the half the icon is drawn with.\nfunction draw(icon: IconDefinition): FunctionalComponent {\n const small = icon.viewBox.endsWith(' 8 8')\n
|
|
1
|
+
{"version":3,"file":"resolver.js","names":["resolver","setIconResolver","next","iconNames","iconSet","builtins","draw","small","icon","svg","h","attrs","resolveIcon","own","name","key","iconAliases"],"sources":["../../../src/components/Icon/resolver.ts"],"sourcesContent":["import { type Component, type FunctionalComponent, h } from 'vue'\n\nimport { type IconDefinition, iconAliases, iconSet } from './iconSet'\n\n/** Turns an icon name into its component, e.g. an SVG file imported as a component. */\nexport type IconResolver = (name: string) => Component | undefined\n\nlet resolver: IconResolver | undefined\n\n/** Adds or replaces icons: names the resolver returns a component for win over the library's set. */\nexport function setIconResolver(next: IconResolver) {\n resolver = next\n}\n\n/** Every name in the library's set (not counting aliases). */\nexport const iconNames = Object.keys(iconSet)\n\nconst builtins = new Map<string, FunctionalComponent>()\n\n// Inline styles, so a colour class (stroke-* fill-*) only paints the half the icon is drawn with. Attributes are passed\n// on by hand: a functional component without props only inherits class and style, so Icon's aria-hidden, role and\n// aria-label would be lost.\nfunction draw(icon: IconDefinition): FunctionalComponent {\n const small = icon.viewBox.endsWith(' 8 8')\n const svg: FunctionalComponent = (_, { attrs }) =>\n h('svg', {\n ...attrs,\n width: small ? 8 : 16,\n height: small ? 8 : 16,\n viewBox: icon.viewBox,\n fill: icon.filled ? 'currentColor' : 'none',\n stroke: icon.filled ? 'none' : 'currentColor',\n style: icon.filled ? 'stroke: none' : 'fill: none',\n 'stroke-width': icon.stroke,\n 'stroke-linecap': icon.cap,\n 'stroke-linejoin': icon.join,\n innerHTML: icon.body\n })\n svg.inheritAttrs = false\n return svg\n}\n\nexport function resolveIcon(name: string): Component | undefined {\n const own = resolver?.(name)\n if (own) return own\n const key = iconSet[name] ? name : iconAliases[name]\n if (!key) {\n console.warn(`[sparkling-ui] No icon named \"${name}\".`)\n return undefined\n }\n if (!builtins.has(key)) builtins.set(key, draw(iconSet[key]))\n return builtins.get(key)\n}\n"],"mappings":";;;AAOA,IAAIA;AAGJ,SAAgBC,EAAgB,GAAoB;CAClD,IAAWC;AACb;AAGA,IAAaC,IAAY,OAAO,KAAKC,CAAO,GAEtCC,oBAAW,IAAI,IAAiC;AAKtD,SAASC,EAAK,GAA2C;CACvD,IAAMC,IAAQC,EAAK,QAAQ,SAAS,MAAM,GACpCC,KAA4B,GAAG,EAAE,eACrCC,EAAE,OAAO;EACP,GAAGC;EACH,OAAOJ,IAAQ,IAAI;EACnB,QAAQA,IAAQ,IAAI;EACpB,SAASC,EAAK;EACd,MAAMA,EAAK,SAAS,iBAAiB;EACrC,QAAQA,EAAK,SAAS,SAAS;EAC/B,OAAOA,EAAK,SAAS,iBAAiB;EACtC,gBAAgBA,EAAK;EACrB,kBAAkBA,EAAK;EACvB,mBAAmBA,EAAK;EACxB,WAAWA,EAAK;CAClB,CAAC;CAEH,OADA,EAAI,eAAe,IACZC;AACT;AAEA,SAAgBG,EAAY,GAAqC;CAC/D,IAAMC,IAAMb,IAAWc,CAAI;CAC3B,IAAID,GAAK,OAAOA;CAChB,IAAME,IAAMX,EAAQU,KAAQA,IAAOE,EAAYF;CAC/C,IAAI,CAACC,GACH,QAAQ,KAAK,iCAAiCD,EAAK,GAAG;MAIxD,OADKT,EAAS,IAAIU,CAAG,KAAGV,EAAS,IAAIU,GAAKT,EAAKF,EAAQW,EAAI,CAAC,GACrDV,EAAS,IAAIU,CAAG;AACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editable.js","names":["_sfc_main","Editable_vue_vue_type_script_setup_true_lang_default","Editable_default"],"sources":["../../../src/components/PropertyList/Editable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, toRaw, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport Select from '../Select/Select.vue'\nimport type { SelectOption, SelectValue } from '../Select/types'\nimport Tag from '../Tag/Tag.vue'\nimport Textarea from '../Textarea/Textarea.vue'\n\nexport interface EditableProps {\n /** `text` (one line), `multiline` (Shift+Enter for a new line), `select` (an outline Tag, picked from `options`) or `title`\n * (a record's 18px name). */\n variant?: 'text' | 'multiline' | 'select' | 'title'\n placeholder?: string\n /** For `select`. */\n options?: SelectOption[]\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<EditableProps>(), {\n variant: 'text',\n placeholder: undefined,\n options: () => []\n})\n\nconst emit = defineEmits<{ blur: [] }>()\n\nconst model = defineModel<string | SelectValue | null>({ default: null })\n\n// Only the id, class and data- and aria- attributes go on the root. The name is said before the value, not as an\n// aria-label, which would hide the value from it.\nconst attrs = useAttrs()\nconst rootAttrs = computed(() =>\n Object.fromEntries(\n Object.entries(attrs).filter(\n ([key]) => key !== 'aria-label' && (key === 'id' || key === 'class' || /^(data|aria)-/.test(key))\n )\n )\n)\nconst name = computed(() => (attrs['aria-label'] as string | undefined) ?? props.placeholder)\n\nconst title = computed(() => props.variant === 'title')\nconst multiline = computed(() => props.variant === 'multiline')\n\nconst local = ref(toRaw(model.value) as string | null)\nconst original = ref(local.value)\nconst editing = ref(false)\nconst wrapper = useTemplateRef<HTMLElement>('wrapper')\n\n// Follows the value from outside (a failed save rolled back, a refetch) unless it's being edited.\nwatch(model, (value) => {\n if (editing.value) return\n local.value = toRaw(value) as string | null\n original.value = local.value\n})\n\nconst rows = computed(() => (title.value ? 1 : Math.max(1, Math.ceil((local.value?.length ?? 0) / 34))))\n\nasync function edit() {\n if (props.disabled) return\n editing.value = true\n await nextTick()\n wrapper.value?.querySelector('textarea')?.focus()\n}\n\nfunction save() {\n editing.value = false\n const value = toRaw(local.value) || null\n // Leaving it untouched isn't a change: an empty value stays as it came, '' or null.\n if (value !== (toRaw(model.value) || null)) model.value = value\n original.value = local.value\n emit('blur')\n}\n\nfunction revert(event: KeyboardEvent) {\n event.preventDefault()\n event.stopPropagation()\n local.value = original.value\n editing.value = false\n}\n\nfunction onEnter(event: KeyboardEvent) {\n if (multiline.value && event.shiftKey) return\n event.preventDefault()\n ;(event.target as HTMLElement).blur()\n}\n\nconst field =\n 'block w-full rounded-lg border border-transparent px-[0.6875rem] py-1.25 text-left text-sui-ink transition-[background-color,border-color,box-shadow] outline-hidden focus-visible:border-sui-accent focus-visible:sui-ring-field'\n</script>\n\n<!-- One value shown as plain text and edited where it is: click or Enter to edit, Enter or leaving to save, Escape to\n undo. A `select` opens its list under the value instead. -->\n<template>\n <Select\n v-if=\"variant === 'select'\"\n v-model=\"model as SelectValue | null\"\n :aria-label=\"name\"\n :disabled=\"disabled\"\n :options=\"options\"\n @update:model-value=\"emit('blur')\"\n >\n <template #trigger=\"{ open, toggle, selected }\">\n <button\n v-bind=\"rootAttrs\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :class=\"[\n field,\n 'max-w-[16rem] text-sm leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n >\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <Tag
|
|
1
|
+
{"version":3,"file":"Editable.js","names":["_sfc_main","Editable_vue_vue_type_script_setup_true_lang_default","Editable_default"],"sources":["../../../src/components/PropertyList/Editable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, toRaw, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport Select from '../Select/Select.vue'\nimport type { SelectOption, SelectValue } from '../Select/types'\nimport Tag from '../Tag/Tag.vue'\nimport Textarea from '../Textarea/Textarea.vue'\n\nexport interface EditableProps {\n /** `text` (one line), `multiline` (Shift+Enter for a new line), `select` (an outline Tag, picked from `options`) or `title`\n * (a record's 18px name). */\n variant?: 'text' | 'multiline' | 'select' | 'title'\n placeholder?: string\n /** For `select`. */\n options?: SelectOption[]\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<EditableProps>(), {\n variant: 'text',\n placeholder: undefined,\n options: () => []\n})\n\nconst emit = defineEmits<{ blur: [] }>()\n\nconst model = defineModel<string | SelectValue | null>({ default: null })\n\n// Only the id, class and data- and aria- attributes go on the root. The name is said before the value, not as an\n// aria-label, which would hide the value from it.\nconst attrs = useAttrs()\nconst rootAttrs = computed(() =>\n Object.fromEntries(\n Object.entries(attrs).filter(\n ([key]) => key !== 'aria-label' && (key === 'id' || key === 'class' || /^(data|aria)-/.test(key))\n )\n )\n)\nconst name = computed(() => (attrs['aria-label'] as string | undefined) ?? props.placeholder)\n\nconst title = computed(() => props.variant === 'title')\nconst multiline = computed(() => props.variant === 'multiline')\n\nconst local = ref(toRaw(model.value) as string | null)\nconst original = ref(local.value)\nconst editing = ref(false)\nconst wrapper = useTemplateRef<HTMLElement>('wrapper')\n\n// Follows the value from outside (a failed save rolled back, a refetch) unless it's being edited.\nwatch(model, (value) => {\n if (editing.value) return\n local.value = toRaw(value) as string | null\n original.value = local.value\n})\n\nconst rows = computed(() => (title.value ? 1 : Math.max(1, Math.ceil((local.value?.length ?? 0) / 34))))\n\nasync function edit() {\n if (props.disabled) return\n editing.value = true\n await nextTick()\n wrapper.value?.querySelector('textarea')?.focus()\n}\n\nfunction save() {\n editing.value = false\n const value = toRaw(local.value) || null\n // Leaving it untouched isn't a change: an empty value stays as it came, '' or null.\n if (value !== (toRaw(model.value) || null)) model.value = value\n original.value = local.value\n emit('blur')\n}\n\nfunction revert(event: KeyboardEvent) {\n event.preventDefault()\n event.stopPropagation()\n local.value = original.value\n editing.value = false\n}\n\nfunction onEnter(event: KeyboardEvent) {\n if (multiline.value && event.shiftKey) return\n event.preventDefault()\n ;(event.target as HTMLElement).blur()\n}\n\nconst field =\n 'block w-full rounded-lg border border-transparent px-[0.6875rem] py-1.25 text-left text-sui-ink transition-[background-color,border-color,box-shadow] outline-hidden focus-visible:border-sui-accent focus-visible:sui-ring-field'\n</script>\n\n<!-- One value shown as plain text and edited where it is: click or Enter to edit, Enter or leaving to save, Escape to\n undo. A `select` opens its list under the value instead. -->\n<template>\n <Select\n v-if=\"variant === 'select'\"\n v-model=\"model as SelectValue | null\"\n :aria-label=\"name\"\n :disabled=\"disabled\"\n :options=\"options\"\n @update:model-value=\"emit('blur')\"\n >\n <template #trigger=\"{ open, toggle, selected }\">\n <button\n v-bind=\"rootAttrs\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :class=\"[\n field,\n 'max-w-[16rem] text-sm leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n >\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <Tag\n v-if=\"selected[0]\"\n :icon=\"selected[0].icon\"\n :icon-color=\"selected[0].iconColor\"\n :name=\"selected[0].label\"\n variant=\"outline\"\n />\n <span v-else class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n </button>\n </template>\n </Select>\n <div\n v-else\n v-bind=\"rootAttrs\"\n ref=\"wrapper\"\n class=\"relative\"\n :class=\"[\n field,\n title\n ? 'min-h-10 min-w-4 text-lg leading-7 font-semibold [&_textarea]:min-h-10 [&_textarea]:text-lg [&_textarea]:leading-7 [&_textarea]:font-semibold [&_textarea]:whitespace-nowrap [&_textarea]:placeholder:text-lg [&_textarea]:placeholder:font-semibold'\n : 'max-w-[16rem] text-sm leading-5 [&_textarea]:placeholder:text-sm [&_textarea]:placeholder:leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken',\n '[&_textarea]:bg-sui-surface [&_textarea]:placeholder:text-sui-ink-muted'\n ]\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n @click=\"edit\"\n @keydown.enter=\"!editing && ($event.preventDefault(), edit())\"\n >\n <div>\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <span v-if=\"!local\" class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n <span v-else :class=\"{ 'whitespace-pre-wrap': multiline }\">{{ local }}</span>\n </div>\n <div v-if=\"editing\" class=\"absolute inset-0 z-10\" :class=\"{ '-mt-px': title }\">\n <Textarea\n v-model=\"local\"\n :aria-label=\"name\"\n :auto-resize=\"!title\"\n class=\"resize-none\"\n :class=\"title && 'h-full px-3 py-1.5'\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @blur=\"save\"\n @keydown.enter.stop=\"onEnter\"\n @keydown.esc.stop=\"revert\"\n />\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -73,9 +73,15 @@ var T = [
|
|
|
73
73
|
onClick: n
|
|
74
74
|
}), [L.value ? (m(), o("span", E, g(L.value) + ": ", 1)) : a("", !0), r[0] ? (m(), i(e, {
|
|
75
75
|
key: 1,
|
|
76
|
+
icon: r[0].icon,
|
|
77
|
+
"icon-color": r[0].iconColor,
|
|
76
78
|
name: r[0].label,
|
|
77
79
|
variant: "outline"
|
|
78
|
-
}, null, 8, [
|
|
80
|
+
}, null, 8, [
|
|
81
|
+
"icon",
|
|
82
|
+
"icon-color",
|
|
83
|
+
"name"
|
|
84
|
+
])) : (m(), o("span", D, g(l.placeholder), 1))], 16, T)]),
|
|
79
85
|
_: 1
|
|
80
86
|
}, 8, [
|
|
81
87
|
"modelValue",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Editable.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","Select","_withCtx","_createElementVNode","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","Tag","_withKeys","_normalizeClass","_createVNode","Textarea","_withModifiers","Tag_default","Select_default","Textarea_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","field","Editable_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__emit","props","__props","emit","model","useModel","attrs","useAttrs","rootAttrs","computed","key","name","title","multiline","local","ref","toRaw","original","editing","wrapper","useTemplateRef","value","rows","edit","nextTick","save","revert","event","onEnter","openBlock","createBlock","_cache","$event","withCtx","createElementVNode","mergeProps","open","toggle","createElementBlock","toDisplayString","createCommentVNode","selected","withKeys","normalizeClass","createVNode","withModifiers"],"sources":["../../../src/components/PropertyList/Editable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, toRaw, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport Select from '../Select/Select.vue'\nimport type { SelectOption, SelectValue } from '../Select/types'\nimport Tag from '../Tag/Tag.vue'\nimport Textarea from '../Textarea/Textarea.vue'\n\nexport interface EditableProps {\n /** `text` (one line), `multiline` (Shift+Enter for a new line), `select` (an outline Tag, picked from `options`) or `title`\n * (a record's 18px name). */\n variant?: 'text' | 'multiline' | 'select' | 'title'\n placeholder?: string\n /** For `select`. */\n options?: SelectOption[]\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<EditableProps>(), {\n variant: 'text',\n placeholder: undefined,\n options: () => []\n})\n\nconst emit = defineEmits<{ blur: [] }>()\n\nconst model = defineModel<string | SelectValue | null>({ default: null })\n\n// Only the id, class and data- and aria- attributes go on the root. The name is said before the value, not as an\n// aria-label, which would hide the value from it.\nconst attrs = useAttrs()\nconst rootAttrs = computed(() =>\n Object.fromEntries(\n Object.entries(attrs).filter(\n ([key]) => key !== 'aria-label' && (key === 'id' || key === 'class' || /^(data|aria)-/.test(key))\n )\n )\n)\nconst name = computed(() => (attrs['aria-label'] as string | undefined) ?? props.placeholder)\n\nconst title = computed(() => props.variant === 'title')\nconst multiline = computed(() => props.variant === 'multiline')\n\nconst local = ref(toRaw(model.value) as string | null)\nconst original = ref(local.value)\nconst editing = ref(false)\nconst wrapper = useTemplateRef<HTMLElement>('wrapper')\n\n// Follows the value from outside (a failed save rolled back, a refetch) unless it's being edited.\nwatch(model, (value) => {\n if (editing.value) return\n local.value = toRaw(value) as string | null\n original.value = local.value\n})\n\nconst rows = computed(() => (title.value ? 1 : Math.max(1, Math.ceil((local.value?.length ?? 0) / 34))))\n\nasync function edit() {\n if (props.disabled) return\n editing.value = true\n await nextTick()\n wrapper.value?.querySelector('textarea')?.focus()\n}\n\nfunction save() {\n editing.value = false\n const value = toRaw(local.value) || null\n // Leaving it untouched isn't a change: an empty value stays as it came, '' or null.\n if (value !== (toRaw(model.value) || null)) model.value = value\n original.value = local.value\n emit('blur')\n}\n\nfunction revert(event: KeyboardEvent) {\n event.preventDefault()\n event.stopPropagation()\n local.value = original.value\n editing.value = false\n}\n\nfunction onEnter(event: KeyboardEvent) {\n if (multiline.value && event.shiftKey) return\n event.preventDefault()\n ;(event.target as HTMLElement).blur()\n}\n\nconst field =\n 'block w-full rounded-lg border border-transparent px-[0.6875rem] py-1.25 text-left text-sui-ink transition-[background-color,border-color,box-shadow] outline-hidden focus-visible:border-sui-accent focus-visible:sui-ring-field'\n</script>\n\n<!-- One value shown as plain text and edited where it is: click or Enter to edit, Enter or leaving to save, Escape to\n undo. A `select` opens its list under the value instead. -->\n<template>\n <Select\n v-if=\"variant === 'select'\"\n v-model=\"model as SelectValue | null\"\n :aria-label=\"name\"\n :disabled=\"disabled\"\n :options=\"options\"\n @update:model-value=\"emit('blur')\"\n >\n <template #trigger=\"{ open, toggle, selected }\">\n <button\n v-bind=\"rootAttrs\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :class=\"[\n field,\n 'max-w-[16rem] text-sm leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n >\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <Tag v-if=\"selected[0]\" :name=\"selected[0].label\" variant=\"outline\" />\n <span v-else class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n </button>\n </template>\n </Select>\n <div\n v-else\n v-bind=\"rootAttrs\"\n ref=\"wrapper\"\n class=\"relative\"\n :class=\"[\n field,\n title\n ? 'min-h-10 min-w-4 text-lg leading-7 font-semibold [&_textarea]:min-h-10 [&_textarea]:text-lg [&_textarea]:leading-7 [&_textarea]:font-semibold [&_textarea]:whitespace-nowrap [&_textarea]:placeholder:text-lg [&_textarea]:placeholder:font-semibold'\n : 'max-w-[16rem] text-sm leading-5 [&_textarea]:placeholder:text-sm [&_textarea]:placeholder:leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken',\n '[&_textarea]:bg-sui-surface [&_textarea]:placeholder:text-sui-ink-muted'\n ]\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n @click=\"edit\"\n @keydown.enter=\"!editing && ($event.preventDefault(), edit())\"\n >\n <div>\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <span v-if=\"!local\" class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n <span v-else :class=\"{ 'whitespace-pre-wrap': multiline }\">{{ local }}</span>\n </div>\n <div v-if=\"editing\" class=\"absolute inset-0 z-10\" :class=\"{ '-mt-px': title }\">\n <Textarea\n v-model=\"local\"\n :aria-label=\"name\"\n :auto-resize=\"!title\"\n class=\"resize-none\"\n :class=\"title && 'h-full px-3 py-1.5'\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @blur=\"save\"\n @keydown.enter.stop=\"onEnter\"\n @keydown.esc.stop=\"revert\"\n />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Editable.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","Select","_withCtx","_createElementVNode","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","Tag","_withKeys","_normalizeClass","_createVNode","Textarea","_withModifiers","Tag_default","Select_default","Textarea_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","field","Editable_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__emit","props","__props","emit","model","useModel","attrs","useAttrs","rootAttrs","computed","key","name","title","multiline","local","ref","toRaw","original","editing","wrapper","useTemplateRef","value","rows","edit","nextTick","save","revert","event","onEnter","openBlock","createBlock","_cache","$event","withCtx","createElementVNode","mergeProps","open","toggle","createElementBlock","toDisplayString","createCommentVNode","selected","withKeys","normalizeClass","createVNode","withModifiers"],"sources":["../../../src/components/PropertyList/Editable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, toRaw, useAttrs, useTemplateRef, watch } from 'vue'\n\nimport Select from '../Select/Select.vue'\nimport type { SelectOption, SelectValue } from '../Select/types'\nimport Tag from '../Tag/Tag.vue'\nimport Textarea from '../Textarea/Textarea.vue'\n\nexport interface EditableProps {\n /** `text` (one line), `multiline` (Shift+Enter for a new line), `select` (an outline Tag, picked from `options`) or `title`\n * (a record's 18px name). */\n variant?: 'text' | 'multiline' | 'select' | 'title'\n placeholder?: string\n /** For `select`. */\n options?: SelectOption[]\n disabled?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<EditableProps>(), {\n variant: 'text',\n placeholder: undefined,\n options: () => []\n})\n\nconst emit = defineEmits<{ blur: [] }>()\n\nconst model = defineModel<string | SelectValue | null>({ default: null })\n\n// Only the id, class and data- and aria- attributes go on the root. The name is said before the value, not as an\n// aria-label, which would hide the value from it.\nconst attrs = useAttrs()\nconst rootAttrs = computed(() =>\n Object.fromEntries(\n Object.entries(attrs).filter(\n ([key]) => key !== 'aria-label' && (key === 'id' || key === 'class' || /^(data|aria)-/.test(key))\n )\n )\n)\nconst name = computed(() => (attrs['aria-label'] as string | undefined) ?? props.placeholder)\n\nconst title = computed(() => props.variant === 'title')\nconst multiline = computed(() => props.variant === 'multiline')\n\nconst local = ref(toRaw(model.value) as string | null)\nconst original = ref(local.value)\nconst editing = ref(false)\nconst wrapper = useTemplateRef<HTMLElement>('wrapper')\n\n// Follows the value from outside (a failed save rolled back, a refetch) unless it's being edited.\nwatch(model, (value) => {\n if (editing.value) return\n local.value = toRaw(value) as string | null\n original.value = local.value\n})\n\nconst rows = computed(() => (title.value ? 1 : Math.max(1, Math.ceil((local.value?.length ?? 0) / 34))))\n\nasync function edit() {\n if (props.disabled) return\n editing.value = true\n await nextTick()\n wrapper.value?.querySelector('textarea')?.focus()\n}\n\nfunction save() {\n editing.value = false\n const value = toRaw(local.value) || null\n // Leaving it untouched isn't a change: an empty value stays as it came, '' or null.\n if (value !== (toRaw(model.value) || null)) model.value = value\n original.value = local.value\n emit('blur')\n}\n\nfunction revert(event: KeyboardEvent) {\n event.preventDefault()\n event.stopPropagation()\n local.value = original.value\n editing.value = false\n}\n\nfunction onEnter(event: KeyboardEvent) {\n if (multiline.value && event.shiftKey) return\n event.preventDefault()\n ;(event.target as HTMLElement).blur()\n}\n\nconst field =\n 'block w-full rounded-lg border border-transparent px-[0.6875rem] py-1.25 text-left text-sui-ink transition-[background-color,border-color,box-shadow] outline-hidden focus-visible:border-sui-accent focus-visible:sui-ring-field'\n</script>\n\n<!-- One value shown as plain text and edited where it is: click or Enter to edit, Enter or leaving to save, Escape to\n undo. A `select` opens its list under the value instead. -->\n<template>\n <Select\n v-if=\"variant === 'select'\"\n v-model=\"model as SelectValue | null\"\n :aria-label=\"name\"\n :disabled=\"disabled\"\n :options=\"options\"\n @update:model-value=\"emit('blur')\"\n >\n <template #trigger=\"{ open, toggle, selected }\">\n <button\n v-bind=\"rootAttrs\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :class=\"[\n field,\n 'max-w-[16rem] text-sm leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n >\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <Tag\n v-if=\"selected[0]\"\n :icon=\"selected[0].icon\"\n :icon-color=\"selected[0].iconColor\"\n :name=\"selected[0].label\"\n variant=\"outline\"\n />\n <span v-else class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n </button>\n </template>\n </Select>\n <div\n v-else\n v-bind=\"rootAttrs\"\n ref=\"wrapper\"\n class=\"relative\"\n :class=\"[\n field,\n title\n ? 'min-h-10 min-w-4 text-lg leading-7 font-semibold [&_textarea]:min-h-10 [&_textarea]:text-lg [&_textarea]:leading-7 [&_textarea]:font-semibold [&_textarea]:whitespace-nowrap [&_textarea]:placeholder:text-lg [&_textarea]:placeholder:font-semibold'\n : 'max-w-[16rem] text-sm leading-5 [&_textarea]:placeholder:text-sm [&_textarea]:placeholder:leading-5',\n disabled ? 'cursor-auto' : 'cursor-pointer hover:bg-sui-sunken',\n '[&_textarea]:bg-sui-surface [&_textarea]:placeholder:text-sui-ink-muted'\n ]\"\n role=\"button\"\n :tabindex=\"disabled ? -1 : 0\"\n @click=\"edit\"\n @keydown.enter=\"!editing && ($event.preventDefault(), edit())\"\n >\n <div>\n <span v-if=\"name\" class=\"sr-only\">{{ name }}: </span>\n <span v-if=\"!local\" class=\"text-sui-ink-muted\">{{ placeholder }}</span>\n <span v-else :class=\"{ 'whitespace-pre-wrap': multiline }\">{{ local }}</span>\n </div>\n <div v-if=\"editing\" class=\"absolute inset-0 z-10\" :class=\"{ '-mt-px': title }\">\n <Textarea\n v-model=\"local\"\n :aria-label=\"name\"\n :auto-resize=\"!title\"\n class=\"resize-none\"\n :class=\"title && 'h-full px-3 py-1.5'\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n @blur=\"save\"\n @keydown.enter.stop=\"onEnter\"\n @keydown.esc.stop=\"revert\"\n />\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","names":["_sfc_main","Select_vue_vue_type_script_setup_true_lang_default","Select_default"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, shallowRef, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n /** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search\n * results. Options picked from the list are remembered without it. */\n selectedOptions?: SelectOption<T>[]\n multiple?: boolean\n /** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */\n clearable?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results\n * for the query, such as from a server search through `query-change`. */\n filter?: boolean | ((option: SelectOption<T>, query: string) => boolean)\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n selectedOptions: () => [],\n searchable: undefined,\n filter: true,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string]; 'create-error': [error: unknown] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\n// Options picked from earlier results, which `options` may no longer hold.\nconst picked = shallowRef<SelectOption<T>[]>([])\nconst selected = computed(() =>\n values.value\n .map((value) =>\n [props.options, props.selectedOptions, picked.value]\n .map((list) => list.find((option) => option.value === value))\n .find((option) => !!option)\n )\n .filter((option) => !!option)\n)\nconst search = computed(\n () =>\n props.searchable ??\n (props.multiple || props.canCreate || props.clearable || props.filter === false || props.options.length > 8)\n)\nconst removable = computed(() => props.multiple || props.clearable)\nconst matches = (option: SelectOption<T>, text: string) =>\n typeof props.filter === 'function'\n ? props.filter(option, text)\n : option.label.toLowerCase().includes(text.toLowerCase())\nconst listed = computed(() => {\n const text = query.value.trim()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || props.filter === false || matches(option, text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n picked.value = [option, ...picked.value.filter((known) => known.value !== option.value)]\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nconst creating = ref(false)\n\n// A failed createOption keeps what was typed, so it can be tried again.\nasync function create() {\n if (creating.value) return\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n creating.value = true\n try {\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n } catch (error) {\n emit('create-error', error)\n } finally {\n creating.value = false\n }\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = props.multiple ? values.value.filter((chosen) => chosen !== value) : null\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && removable.value && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\n\n// Nor does Reka know a press on it isn't outside: closing there would let the click reopen the list.\nfunction onPointerDownOutside(event: CustomEvent<{ originalEvent: PointerEvent }>) {\n const target = event.detail.originalEvent.target\n if (target instanceof Node && anchor.value?.$el?.contains(target)) event.preventDefault()\n}\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). For a server search, set `:filter=\"false\"` and replace `options` on `query-change`. The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-strong hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n />\n </span>\n <Tag v-else-if=\"selected[0]!.color\" :color=\"selected[0]!.color\" :name=\"selected[0]!.label\" />\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @pointer-down-outside=\"onPointerDownOutside\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"removable\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"removable && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Select.js","names":["_sfc_main","Select_vue_vue_type_script_setup_true_lang_default","Select_default"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, shallowRef, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Icon from '../Icon/Icon.vue'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n /** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search\n * results. Options picked from the list are remembered without it. */\n selectedOptions?: SelectOption<T>[]\n multiple?: boolean\n /** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */\n clearable?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results\n * for the query, such as from a server search through `query-change`. */\n filter?: boolean | ((option: SelectOption<T>, query: string) => boolean)\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n selectedOptions: () => [],\n searchable: undefined,\n filter: true,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string]; 'create-error': [error: unknown] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\n// Options picked from earlier results, which `options` may no longer hold.\nconst picked = shallowRef<SelectOption<T>[]>([])\nconst selected = computed(() =>\n values.value\n .map((value) =>\n [props.options, props.selectedOptions, picked.value]\n .map((list) => list.find((option) => option.value === value))\n .find((option) => !!option)\n )\n .filter((option) => !!option)\n)\nconst search = computed(\n () =>\n props.searchable ??\n (props.multiple || props.canCreate || props.clearable || props.filter === false || props.options.length > 8)\n)\nconst removable = computed(() => props.multiple || props.clearable)\nconst matches = (option: SelectOption<T>, text: string) =>\n typeof props.filter === 'function'\n ? props.filter(option, text)\n : option.label.toLowerCase().includes(text.toLowerCase())\nconst listed = computed(() => {\n const text = query.value.trim()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || props.filter === false || matches(option, text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n picked.value = [option, ...picked.value.filter((known) => known.value !== option.value)]\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nconst creating = ref(false)\n\n// A failed createOption keeps what was typed, so it can be tried again.\nasync function create() {\n if (creating.value) return\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n creating.value = true\n try {\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n } catch (error) {\n emit('create-error', error)\n } finally {\n creating.value = false\n }\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = props.multiple ? values.value.filter((chosen) => chosen !== value) : null\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && removable.value && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\n\n// Nor does Reka know a press on it isn't outside: closing there would let the click reopen the list.\nfunction onPointerDownOutside(event: CustomEvent<{ originalEvent: PointerEvent }>) {\n const target = event.detail.originalEvent.target\n if (target instanceof Node && anchor.value?.$el?.contains(target)) event.preventDefault()\n}\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). For a server search, set `:filter=\"false\"` and replace `options` on `query-change`. The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-strong hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n </span>\n <Tag\n v-else-if=\"selected[0]!.color\"\n :color=\"selected[0]!.color\"\n :icon=\"selected[0]!.icon\"\n :icon-color=\"selected[0]!.iconColor\"\n :name=\"selected[0]!.label\"\n />\n <span v-else-if=\"selected[0]!.icon\" class=\"inline-flex min-w-0 items-center gap-1.5\">\n <Icon class=\"shrink-0\" :color=\"selected[0]!.iconColor ?? 'ink-subtle'\" :name=\"selected[0]!.icon\" />\n <span class=\"truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n </span>\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @pointer-down-outside=\"onPointerDownOutside\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"removable\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"removable && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag\n v-if=\"option.color\"\n :color=\"option.color\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n <template v-else>\n <Icon\n v-if=\"option.icon\"\n class=\"shrink-0\"\n :color=\"option.iconColor ?? 'ink-subtle'\"\n :name=\"option.icon\"\n />{{ option.label }}\n </template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag\n v-if=\"option.color\"\n :color=\"option.color\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n <template v-else>\n <Icon\n v-if=\"option.icon\"\n class=\"shrink-0\"\n :color=\"option.iconColor ?? 'ink-subtle'\"\n :name=\"option.icon\"\n />{{ option.label }}\n </template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import t from "../
|
|
3
|
-
import n from "../
|
|
4
|
-
import r from "../
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
1
|
+
import e from "../Icon/Icon.js";
|
|
2
|
+
import { useFieldControl as t } from "../FormField/context.js";
|
|
3
|
+
import n from "../internal/CheckIcon.js";
|
|
4
|
+
import r from "../Spinner/Spinner.js";
|
|
5
|
+
import i from "../Tag/Tag.js";
|
|
6
|
+
import { Fragment as a, computed as o, createBlock as s, createCommentVNode as c, createElementBlock as l, createElementVNode as u, createTextVNode as d, createVNode as f, defineComponent as p, mergeModels as m, mergeProps as ee, nextTick as h, normalizeClass as g, normalizeStyle as te, openBlock as _, ref as v, renderList as y, renderSlot as b, shallowRef as ne, toDisplayString as x, unref as S, useAttrs as re, useModel as ie, withCtx as C } from "vue";
|
|
7
|
+
import { ListboxContent as ae, ListboxFilter as oe, ListboxItem as w, ListboxRoot as se, ListboxVirtualizer as ce, PopoverAnchor as le, PopoverContent as ue, PopoverPortal as de, PopoverRoot as fe } from "reka-ui";
|
|
7
8
|
//#region src/components/Select/Select.vue?vue&type=script&setup=true&lang.ts
|
|
8
9
|
var pe = [
|
|
9
10
|
"aria-expanded",
|
|
@@ -20,23 +21,26 @@ var pe = [
|
|
|
20
21
|
class: "flex flex-wrap gap-1 py-1.25"
|
|
21
22
|
}, _e = {
|
|
22
23
|
key: 4,
|
|
24
|
+
class: "inline-flex min-w-0 items-center gap-1.5"
|
|
25
|
+
}, ve = { class: "truncate whitespace-nowrap" }, ye = {
|
|
26
|
+
key: 5,
|
|
23
27
|
class: "min-w-0 truncate whitespace-nowrap"
|
|
24
|
-
},
|
|
28
|
+
}, be = {
|
|
25
29
|
key: 0,
|
|
26
30
|
class: "flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5"
|
|
27
|
-
},
|
|
31
|
+
}, xe = {
|
|
28
32
|
key: 1,
|
|
29
33
|
class: "flex justify-center py-6"
|
|
30
|
-
},
|
|
34
|
+
}, Se = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, Ce = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, we = { class: "inline-flex min-w-0 items-center gap-1.5" }, Te = {
|
|
31
35
|
key: 1,
|
|
32
36
|
class: "font-medium"
|
|
33
|
-
},
|
|
37
|
+
}, Ee = {
|
|
34
38
|
key: 3,
|
|
35
39
|
class: "px-3.5 py-3 text-13 text-sui-ink-muted"
|
|
36
|
-
},
|
|
40
|
+
}, T = "flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset", E = /*@__PURE__*/ p({
|
|
37
41
|
inheritAttrs: !1,
|
|
38
42
|
__name: "Select",
|
|
39
|
-
props: /*@__PURE__*/
|
|
43
|
+
props: /*@__PURE__*/ m({
|
|
40
44
|
options: {},
|
|
41
45
|
selectedOptions: { default: () => [] },
|
|
42
46
|
multiple: { type: Boolean },
|
|
@@ -74,19 +78,19 @@ var pe = [
|
|
|
74
78
|
modelValue: { default: null },
|
|
75
79
|
modelModifiers: {}
|
|
76
80
|
}),
|
|
77
|
-
emits: /*@__PURE__*/
|
|
78
|
-
setup(
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
].map((t) => t.find((t) => t.value === e)).find((e) => !!e)).filter((e) => !!e)),
|
|
84
|
-
let e =
|
|
85
|
-
return
|
|
86
|
-
}),
|
|
87
|
-
let e =
|
|
88
|
-
return
|
|
89
|
-
}),
|
|
81
|
+
emits: /*@__PURE__*/ m(["query-change", "create-error"], ["update:modelValue"]),
|
|
82
|
+
setup(p, { emit: m }) {
|
|
83
|
+
let E = p, D = m, O = re(), k = o(() => O["aria-label"]), De = o(() => Object.fromEntries(Object.entries(O).filter(([e]) => e !== "aria-label"))), A = ie(p, "modelValue"), j = v(!1), M = v(""), N = v(!1), P = o(() => Array.isArray(A.value) ? A.value : A.value === null || A.value === void 0 ? [] : [A.value]), F = ne([]), I = o(() => P.value.map((e) => [
|
|
84
|
+
E.options,
|
|
85
|
+
E.selectedOptions,
|
|
86
|
+
F.value
|
|
87
|
+
].map((t) => t.find((t) => t.value === e)).find((e) => !!e)).filter((e) => !!e)), L = o(() => E.searchable ?? (E.multiple || E.canCreate || E.clearable || E.filter === !1 || E.options.length > 8)), R = o(() => E.multiple || E.clearable), Oe = (e, t) => typeof E.filter == "function" ? E.filter(e, t) : e.label.toLowerCase().includes(t.toLowerCase()), z = o(() => {
|
|
88
|
+
let e = M.value.trim();
|
|
89
|
+
return E.options.filter((e) => !(E.multiple && L.value && P.value.includes(e.value))).filter((t) => !e || E.filter === !1 || Oe(t, e));
|
|
90
|
+
}), B = o(() => {
|
|
91
|
+
let e = M.value.trim().toLowerCase();
|
|
92
|
+
return E.canCreate && !!e && !E.options.some((t) => t.label.toLowerCase() === e);
|
|
93
|
+
}), ke = o(() => z.value.length > 20), V = [
|
|
90
94
|
"gray",
|
|
91
95
|
"purple",
|
|
92
96
|
"red",
|
|
@@ -95,192 +99,226 @@ var pe = [
|
|
|
95
99
|
"pink",
|
|
96
100
|
"blue",
|
|
97
101
|
"slate"
|
|
98
|
-
],
|
|
99
|
-
function W(e) {
|
|
100
|
-
j.value = e, E("query-change", e);
|
|
101
|
-
}
|
|
102
|
+
], H = o(() => V[M.value.length % V.length]), U = { create: !0 }, { attrs: Ae, invalid: W } = t(() => void 0, () => E.invalid);
|
|
102
103
|
function G(e) {
|
|
103
|
-
|
|
104
|
+
M.value = e, D("query-change", e);
|
|
104
105
|
}
|
|
105
|
-
function K() {
|
|
106
|
-
|
|
106
|
+
function K(e) {
|
|
107
|
+
E.disabled || (N.value = e, j.value = !0);
|
|
107
108
|
}
|
|
108
|
-
function q(
|
|
109
|
-
|
|
109
|
+
function q() {
|
|
110
|
+
j.value = !1, M.value && G("");
|
|
110
111
|
}
|
|
111
112
|
function J(e) {
|
|
112
|
-
|
|
113
|
+
j.value ? q() : K(!!e && e.detail === 0);
|
|
114
|
+
}
|
|
115
|
+
function je(e) {
|
|
116
|
+
F.value = [e, ...F.value.filter((t) => t.value !== e.value)], E.multiple ? (A.value = P.value.includes(e.value) ? P.value.filter((t) => t !== e.value) : [...P.value, e.value], G("")) : (A.value = e.value, q());
|
|
113
117
|
}
|
|
114
|
-
let Y =
|
|
115
|
-
async function
|
|
118
|
+
let Y = v(!1);
|
|
119
|
+
async function Me() {
|
|
116
120
|
if (Y.value) return;
|
|
117
|
-
let e =
|
|
121
|
+
let e = M.value.trim(), t = E.createColored ? {
|
|
118
122
|
label: e,
|
|
119
|
-
color:
|
|
123
|
+
color: H.value
|
|
120
124
|
} : { label: e };
|
|
121
125
|
Y.value = !0;
|
|
122
126
|
try {
|
|
123
|
-
|
|
127
|
+
je(E.createOption ? await E.createOption(t) : {
|
|
124
128
|
value: e,
|
|
125
129
|
label: e,
|
|
126
130
|
color: t.color
|
|
127
131
|
});
|
|
128
132
|
} catch (e) {
|
|
129
|
-
|
|
133
|
+
D("create-error", e);
|
|
130
134
|
} finally {
|
|
131
135
|
Y.value = !1;
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
function X(e, t) {
|
|
135
|
-
e.preventDefault(), "create" in t ?
|
|
139
|
+
e.preventDefault(), "create" in t ? Me() : t.disabled || je(t);
|
|
136
140
|
}
|
|
137
141
|
function Z(e) {
|
|
138
|
-
|
|
142
|
+
A.value = E.multiple ? P.value.filter((t) => t !== e) : null;
|
|
139
143
|
}
|
|
140
|
-
function
|
|
144
|
+
function Ne(e) {
|
|
141
145
|
[
|
|
142
146
|
"ArrowUp",
|
|
143
147
|
"ArrowDown",
|
|
144
148
|
"Home",
|
|
145
149
|
"End"
|
|
146
|
-
].includes(e.key) && (
|
|
150
|
+
].includes(e.key) && (N.value = !0), e.key === "Backspace" && R.value && !M.value && P.value.length && Z(P.value[P.value.length - 1]);
|
|
147
151
|
}
|
|
148
|
-
function
|
|
149
|
-
!
|
|
152
|
+
function Pe(e) {
|
|
153
|
+
!j.value && ["ArrowDown", "ArrowUp"].includes(e.key) && (e.preventDefault(), K(!0));
|
|
150
154
|
}
|
|
151
|
-
function
|
|
152
|
-
e.preventDefault(),
|
|
155
|
+
function Fe(e) {
|
|
156
|
+
e.preventDefault(), h(() => e.target?.querySelector("[data-select-filter], [role=\"listbox\"]")?.focus());
|
|
153
157
|
}
|
|
154
|
-
let Q =
|
|
155
|
-
function
|
|
158
|
+
let Q = v();
|
|
159
|
+
function Ie(e) {
|
|
156
160
|
let t = e.detail.originalEvent.target;
|
|
157
161
|
t instanceof Node && Q.value?.$el?.contains(t) && e.preventDefault();
|
|
158
162
|
}
|
|
159
|
-
function
|
|
163
|
+
function Le(e) {
|
|
160
164
|
e.preventDefault(), Q.value?.$el?.focus();
|
|
161
165
|
}
|
|
162
|
-
let $ =
|
|
163
|
-
function
|
|
164
|
-
!
|
|
166
|
+
let $ = v();
|
|
167
|
+
function Re(e) {
|
|
168
|
+
!N.value && e && (P.value.includes(e.value) || h(() => $.value && !N.value && ($.value.highlightedElement = null)));
|
|
165
169
|
}
|
|
166
|
-
let
|
|
170
|
+
let ze = {
|
|
167
171
|
md: "min-h-9 pr-2.5 pl-3 text-sm",
|
|
168
172
|
sm: "h-7 pr-1.5 pl-2 text-13"
|
|
169
173
|
};
|
|
170
|
-
return (
|
|
171
|
-
open:
|
|
172
|
-
"onUpdate:open":
|
|
174
|
+
return (t, o) => (_(), s(S(fe), {
|
|
175
|
+
open: j.value,
|
|
176
|
+
"onUpdate:open": o[2] ||= (e) => e ? K(!1) : q()
|
|
173
177
|
}, {
|
|
174
|
-
default:
|
|
178
|
+
default: C(() => [f(S(le), {
|
|
175
179
|
ref_key: "anchor",
|
|
176
180
|
ref: Q,
|
|
177
181
|
"as-child": ""
|
|
178
182
|
}, {
|
|
179
|
-
default:
|
|
180
|
-
open:
|
|
181
|
-
selected:
|
|
182
|
-
toggle:
|
|
183
|
-
}, () => [
|
|
184
|
-
...
|
|
185
|
-
...
|
|
183
|
+
default: C(() => [b(t.$slots, "trigger", {
|
|
184
|
+
open: j.value,
|
|
185
|
+
selected: I.value,
|
|
186
|
+
toggle: J
|
|
187
|
+
}, () => [u("button", ee({
|
|
188
|
+
...S(Ae),
|
|
189
|
+
...De.value
|
|
186
190
|
}, {
|
|
187
|
-
"aria-expanded":
|
|
191
|
+
"aria-expanded": j.value,
|
|
188
192
|
"aria-haspopup": "listbox",
|
|
189
|
-
"aria-invalid":
|
|
190
|
-
class: ["group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60", [
|
|
191
|
-
disabled:
|
|
193
|
+
"aria-invalid": S(W) ? "true" : void 0,
|
|
194
|
+
class: ["group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60", [ze[p.size], S(W) ? "border-sui-danger-mid" : "border-sui-line-strong hover:border-sui-ink-muted"]],
|
|
195
|
+
disabled: p.disabled,
|
|
192
196
|
type: "button",
|
|
193
|
-
onClick:
|
|
194
|
-
onKeydown:
|
|
197
|
+
onClick: J,
|
|
198
|
+
onKeydown: Pe
|
|
195
199
|
}), [
|
|
196
|
-
|
|
197
|
-
|
|
200
|
+
k.value ? (_(), l("span", me, x(k.value) + ": ", 1)) : c("", !0),
|
|
201
|
+
I.value.length ? p.multiple ? (_(), l("span", ge, [(_(!0), l(a, null, y(I.value, (e) => (_(), s(i, {
|
|
198
202
|
key: e.value,
|
|
199
203
|
color: e.color ?? void 0,
|
|
204
|
+
icon: e.icon,
|
|
205
|
+
"icon-color": e.iconColor,
|
|
200
206
|
name: e.label
|
|
201
|
-
}, null, 8, [
|
|
207
|
+
}, null, 8, [
|
|
208
|
+
"color",
|
|
209
|
+
"icon",
|
|
210
|
+
"icon-color",
|
|
211
|
+
"name"
|
|
212
|
+
]))), 128))])) : I.value[0].color ? (_(), s(i, {
|
|
202
213
|
key: 3,
|
|
203
|
-
color:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
color: I.value[0].color,
|
|
215
|
+
icon: I.value[0].icon,
|
|
216
|
+
"icon-color": I.value[0].iconColor,
|
|
217
|
+
name: I.value[0].label
|
|
218
|
+
}, null, 8, [
|
|
219
|
+
"color",
|
|
220
|
+
"icon",
|
|
221
|
+
"icon-color",
|
|
222
|
+
"name"
|
|
223
|
+
])) : I.value[0].icon ? (_(), l("span", _e, [f(e, {
|
|
224
|
+
class: "shrink-0",
|
|
225
|
+
color: I.value[0].iconColor ?? "ink-subtle",
|
|
226
|
+
name: I.value[0].icon
|
|
227
|
+
}, null, 8, ["color", "name"]), u("span", ve, x(I.value[0].label), 1)])) : (_(), l("span", ye, x(I.value[0].label), 1)) : (_(), l("span", he, x(p.placeholder), 1)),
|
|
228
|
+
o[3] ||= u("svg", {
|
|
207
229
|
"aria-hidden": "true",
|
|
208
230
|
class: "size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180",
|
|
209
231
|
"stroke-linecap": "round",
|
|
210
232
|
"stroke-linejoin": "round",
|
|
211
233
|
"stroke-width": "1.5",
|
|
212
234
|
viewBox: "0 0 16 16"
|
|
213
|
-
}, [
|
|
235
|
+
}, [u("path", { d: "M4.5 6.5 8 10l3.5-3.5" })], -1)
|
|
214
236
|
], 16, pe)])]),
|
|
215
237
|
_: 3
|
|
216
|
-
}, 512),
|
|
217
|
-
default:
|
|
218
|
-
align:
|
|
219
|
-
"aria-label":
|
|
220
|
-
class:
|
|
221
|
-
side:
|
|
238
|
+
}, 512), f(S(de), null, {
|
|
239
|
+
default: C(() => [f(S(ue), {
|
|
240
|
+
align: p.align,
|
|
241
|
+
"aria-label": k.value ?? p.placeholder,
|
|
242
|
+
class: g(["z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in", L.value ? "min-w-[max(var(--reka-popover-trigger-width),15rem)]" : "min-w-(--reka-popover-trigger-width)"]),
|
|
243
|
+
side: p.side,
|
|
222
244
|
"side-offset": 4,
|
|
223
|
-
style: te(
|
|
224
|
-
onCloseAutoFocus:
|
|
225
|
-
onOpenAutoFocus:
|
|
226
|
-
onPointerDownOutside:
|
|
245
|
+
style: te(p.panelWidth ? { width: p.panelWidth } : void 0),
|
|
246
|
+
onCloseAutoFocus: Le,
|
|
247
|
+
onOpenAutoFocus: Fe,
|
|
248
|
+
onPointerDownOutside: Ie
|
|
227
249
|
}, {
|
|
228
|
-
default:
|
|
250
|
+
default: C(() => [f(S(se), {
|
|
229
251
|
ref_key: "listbox",
|
|
230
252
|
ref: $,
|
|
231
253
|
"highlight-on-hover": !0,
|
|
232
|
-
"model-value":
|
|
233
|
-
multiple:
|
|
234
|
-
onHighlight:
|
|
235
|
-
onKeydown:
|
|
236
|
-
onPointermove:
|
|
254
|
+
"model-value": p.multiple ? P.value : P.value[0] ?? void 0,
|
|
255
|
+
multiple: p.multiple,
|
|
256
|
+
onHighlight: Re,
|
|
257
|
+
onKeydown: Ne,
|
|
258
|
+
onPointermove: o[1] ||= (e) => N.value = !1
|
|
237
259
|
}, {
|
|
238
|
-
default:
|
|
260
|
+
default: C(() => [L.value ? (_(), l("div", be, [R.value ? (_(!0), l(a, { key: 0 }, y(I.value, (e) => (_(), s(i, {
|
|
239
261
|
key: e.value,
|
|
240
262
|
color: e.color ?? void 0,
|
|
263
|
+
icon: e.icon,
|
|
264
|
+
"icon-color": e.iconColor,
|
|
241
265
|
name: e.label,
|
|
242
266
|
removable: "",
|
|
243
|
-
"remove-label":
|
|
267
|
+
"remove-label": p.removeLabel,
|
|
244
268
|
onRemove: (t) => Z(e.value)
|
|
245
269
|
}, null, 8, [
|
|
246
270
|
"color",
|
|
271
|
+
"icon",
|
|
272
|
+
"icon-color",
|
|
247
273
|
"name",
|
|
248
274
|
"remove-label",
|
|
249
275
|
"onRemove"
|
|
250
|
-
]))), 128)) :
|
|
251
|
-
"aria-label":
|
|
276
|
+
]))), 128)) : c("", !0), f(S(oe), {
|
|
277
|
+
"aria-label": k.value ?? p.placeholder,
|
|
252
278
|
class: "h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden",
|
|
253
279
|
"data-select-filter": "",
|
|
254
|
-
"model-value":
|
|
255
|
-
placeholder:
|
|
256
|
-
"onUpdate:modelValue":
|
|
280
|
+
"model-value": M.value,
|
|
281
|
+
placeholder: R.value && I.value.length ? "" : p.searchPlaceholder,
|
|
282
|
+
"onUpdate:modelValue": G
|
|
257
283
|
}, null, 8, [
|
|
258
284
|
"aria-label",
|
|
259
285
|
"model-value",
|
|
260
286
|
"placeholder"
|
|
261
|
-
])])) :
|
|
287
|
+
])])) : c("", !0), p.loading ? (_(), l("div", xe, [f(r, { size: "sm" })])) : z.value.length || B.value ? (_(), s(S(ae), {
|
|
262
288
|
key: 2,
|
|
263
|
-
"aria-label":
|
|
289
|
+
"aria-label": k.value ?? p.placeholder,
|
|
264
290
|
class: "group/list max-h-60 overflow-auto p-1 outline-hidden",
|
|
265
|
-
"data-keyboard":
|
|
291
|
+
"data-keyboard": N.value || void 0
|
|
266
292
|
}, {
|
|
267
|
-
default:
|
|
293
|
+
default: C(() => [ke.value ? (_(), s(S(ce), {
|
|
268
294
|
key: 0,
|
|
269
295
|
"estimate-size": 32,
|
|
270
|
-
options:
|
|
296
|
+
options: z.value,
|
|
271
297
|
"text-content": (e) => e.label
|
|
272
298
|
}, {
|
|
273
|
-
default:
|
|
274
|
-
class:
|
|
275
|
-
disabled:
|
|
276
|
-
value:
|
|
277
|
-
onSelect: (e) => X(e,
|
|
299
|
+
default: C(({ option: r }) => [f(S(w), {
|
|
300
|
+
class: g(T),
|
|
301
|
+
disabled: r.disabled,
|
|
302
|
+
value: r.value,
|
|
303
|
+
onSelect: (e) => X(e, r)
|
|
278
304
|
}, {
|
|
279
|
-
default:
|
|
305
|
+
default: C(() => [u("span", Se, [b(t.$slots, "option", { option: r }, () => [r.color ? (_(), s(i, {
|
|
306
|
+
key: 0,
|
|
307
|
+
color: r.color,
|
|
308
|
+
icon: r.icon,
|
|
309
|
+
"icon-color": r.iconColor,
|
|
310
|
+
name: r.label
|
|
311
|
+
}, null, 8, [
|
|
312
|
+
"color",
|
|
313
|
+
"icon",
|
|
314
|
+
"icon-color",
|
|
315
|
+
"name"
|
|
316
|
+
])) : (_(), l(a, { key: 1 }, [r.icon ? (_(), s(e, {
|
|
280
317
|
key: 0,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
318
|
+
class: "shrink-0",
|
|
319
|
+
color: r.iconColor ?? "ink-subtle",
|
|
320
|
+
name: r.icon
|
|
321
|
+
}, null, 8, ["color", "name"])) : c("", !0), d(x(r.label), 1)], 64))])]), P.value.includes(r.value) ? (_(), s(n, { key: 0 })) : c("", !0)]),
|
|
284
322
|
_: 2
|
|
285
323
|
}, 1032, [
|
|
286
324
|
"disabled",
|
|
@@ -288,38 +326,50 @@ var pe = [
|
|
|
288
326
|
"onSelect"
|
|
289
327
|
])]),
|
|
290
328
|
_: 3
|
|
291
|
-
}, 8, ["options", "text-content"])) : (
|
|
292
|
-
key:
|
|
293
|
-
class:
|
|
294
|
-
disabled:
|
|
295
|
-
value:
|
|
296
|
-
onSelect: (e) => X(e,
|
|
329
|
+
}, 8, ["options", "text-content"])) : (_(!0), l(a, { key: 1 }, y(z.value, (r) => (_(), s(S(w), {
|
|
330
|
+
key: r.value,
|
|
331
|
+
class: g(T),
|
|
332
|
+
disabled: r.disabled,
|
|
333
|
+
value: r.value,
|
|
334
|
+
onSelect: (e) => X(e, r)
|
|
297
335
|
}, {
|
|
298
|
-
default:
|
|
336
|
+
default: C(() => [u("span", Ce, [b(t.$slots, "option", { option: r }, () => [r.color ? (_(), s(i, {
|
|
337
|
+
key: 0,
|
|
338
|
+
color: r.color,
|
|
339
|
+
icon: r.icon,
|
|
340
|
+
"icon-color": r.iconColor,
|
|
341
|
+
name: r.label
|
|
342
|
+
}, null, 8, [
|
|
343
|
+
"color",
|
|
344
|
+
"icon",
|
|
345
|
+
"icon-color",
|
|
346
|
+
"name"
|
|
347
|
+
])) : (_(), l(a, { key: 1 }, [r.icon ? (_(), s(e, {
|
|
299
348
|
key: 0,
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
349
|
+
class: "shrink-0",
|
|
350
|
+
color: r.iconColor ?? "ink-subtle",
|
|
351
|
+
name: r.icon
|
|
352
|
+
}, null, 8, ["color", "name"])) : c("", !0), d(x(r.label), 1)], 64))])]), P.value.includes(r.value) ? (_(), s(n, { key: 0 })) : c("", !0)]),
|
|
303
353
|
_: 2
|
|
304
354
|
}, 1032, [
|
|
305
355
|
"disabled",
|
|
306
356
|
"value",
|
|
307
357
|
"onSelect"
|
|
308
|
-
]))), 128)),
|
|
358
|
+
]))), 128)), B.value ? (_(), s(S(w), {
|
|
309
359
|
key: 2,
|
|
310
|
-
class:
|
|
311
|
-
value:
|
|
312
|
-
onSelect:
|
|
360
|
+
class: g(T),
|
|
361
|
+
value: U,
|
|
362
|
+
onSelect: o[0] ||= (e) => X(e, U)
|
|
313
363
|
}, {
|
|
314
|
-
default:
|
|
364
|
+
default: C(() => [u("span", we, [d(x(p.createLabel) + " ", 1), p.createColored ? (_(), s(i, {
|
|
315
365
|
key: 0,
|
|
316
|
-
color:
|
|
317
|
-
name:
|
|
318
|
-
}, null, 8, ["color", "name"])) : (
|
|
366
|
+
color: H.value,
|
|
367
|
+
name: M.value.trim()
|
|
368
|
+
}, null, 8, ["color", "name"])) : (_(), l("strong", Te, "“" + x(M.value.trim()) + "”", 1))])]),
|
|
319
369
|
_: 1
|
|
320
|
-
})) :
|
|
370
|
+
})) : c("", !0)]),
|
|
321
371
|
_: 3
|
|
322
|
-
}, 8, ["aria-label", "data-keyboard"])) : (
|
|
372
|
+
}, 8, ["aria-label", "data-keyboard"])) : (_(), l("div", Ee, x(p.emptyLabel), 1))]),
|
|
323
373
|
_: 3
|
|
324
374
|
}, 8, ["model-value", "multiple"])]),
|
|
325
375
|
_: 3
|
|
@@ -337,6 +387,6 @@ var pe = [
|
|
|
337
387
|
}
|
|
338
388
|
});
|
|
339
389
|
//#endregion
|
|
340
|
-
export {
|
|
390
|
+
export { E as default };
|
|
341
391
|
|
|
342
392
|
//# sourceMappingURL=Select.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","_unref","_withCtx","_createVNode","_renderSlot","_createElementVNode","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","_Fragment","_renderList","Tag","_normalizeClass","_normalizeStyle","Spinner","_createTextVNode","CheckIcon","CheckIcon_default","Spinner_default","Tag_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","optionClass","Select_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__emit","props","__props","emit","attrs","useAttrs","label","computed","triggerAttrs","key","model","useModel","open","ref","query","keyboard","values","picked","shallowRef","selected","list","option","value","search","removable","matches","text","listed","showCreate","virtual","createHues","newHue","CREATE","fieldAttrs","isInvalid","useFieldControl","setQuery","show","viaKeyboard","close","toggle","event","choose","known","creating","create","draft","error","onSelect","row","remove","chosen","onKeydown","onTriggerKeydown","onOpenAutoFocus","nextTick","anchor","onPointerDownOutside","target","returnFocus","listbox","onHighlight","payload","triggerSizes","openBlock","createBlock","unref","PopoverRoot","_cache","withCtx","createVNode","PopoverAnchor","renderSlot","_ctx","createElementVNode","mergeProps","createElementBlock","toDisplayString","createCommentVNode","Fragment","renderList","PopoverPortal","PopoverContent","normalizeClass","normalizeStyle","ListboxRoot","ListboxFilter","ListboxContent","ListboxVirtualizer","ListboxItem","$event","createTextVNode"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, shallowRef, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n /** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search\n * results. Options picked from the list are remembered without it. */\n selectedOptions?: SelectOption<T>[]\n multiple?: boolean\n /** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */\n clearable?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results\n * for the query, such as from a server search through `query-change`. */\n filter?: boolean | ((option: SelectOption<T>, query: string) => boolean)\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n selectedOptions: () => [],\n searchable: undefined,\n filter: true,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string]; 'create-error': [error: unknown] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\n// Options picked from earlier results, which `options` may no longer hold.\nconst picked = shallowRef<SelectOption<T>[]>([])\nconst selected = computed(() =>\n values.value\n .map((value) =>\n [props.options, props.selectedOptions, picked.value]\n .map((list) => list.find((option) => option.value === value))\n .find((option) => !!option)\n )\n .filter((option) => !!option)\n)\nconst search = computed(\n () =>\n props.searchable ??\n (props.multiple || props.canCreate || props.clearable || props.filter === false || props.options.length > 8)\n)\nconst removable = computed(() => props.multiple || props.clearable)\nconst matches = (option: SelectOption<T>, text: string) =>\n typeof props.filter === 'function'\n ? props.filter(option, text)\n : option.label.toLowerCase().includes(text.toLowerCase())\nconst listed = computed(() => {\n const text = query.value.trim()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || props.filter === false || matches(option, text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n picked.value = [option, ...picked.value.filter((known) => known.value !== option.value)]\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nconst creating = ref(false)\n\n// A failed createOption keeps what was typed, so it can be tried again.\nasync function create() {\n if (creating.value) return\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n creating.value = true\n try {\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n } catch (error) {\n emit('create-error', error)\n } finally {\n creating.value = false\n }\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = props.multiple ? values.value.filter((chosen) => chosen !== value) : null\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && removable.value && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\n\n// Nor does Reka know a press on it isn't outside: closing there would let the click reopen the list.\nfunction onPointerDownOutside(event: CustomEvent<{ originalEvent: PointerEvent }>) {\n const target = event.detail.originalEvent.target\n if (target instanceof Node && anchor.value?.$el?.contains(target)) event.preventDefault()\n}\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). For a server search, set `:filter=\"false\"` and replace `options` on `query-change`. The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-strong hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n />\n </span>\n <Tag v-else-if=\"selected[0]!.color\" :color=\"selected[0]!.color\" :name=\"selected[0]!.label\" />\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @pointer-down-outside=\"onPointerDownOutside\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"removable\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"removable && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Select.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","_unref","_withCtx","_createVNode","_renderSlot","_createElementVNode","_mergeProps","_createElementBlock","_toDisplayString","_createCommentVNode","_Fragment","_renderList","Tag","Icon","_normalizeClass","_normalizeStyle","Spinner","_createTextVNode","CheckIcon","Icon_default","CheckIcon_default","Spinner_default","Tag_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","_hoisted_13","_hoisted_14","optionClass","Select_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","__emit","props","__props","emit","attrs","useAttrs","label","computed","triggerAttrs","key","model","useModel","open","ref","query","keyboard","values","picked","shallowRef","selected","list","option","value","search","removable","matches","text","listed","showCreate","virtual","createHues","newHue","CREATE","fieldAttrs","isInvalid","useFieldControl","setQuery","show","viaKeyboard","close","toggle","event","choose","known","creating","create","draft","error","onSelect","row","remove","chosen","onKeydown","onTriggerKeydown","onOpenAutoFocus","nextTick","anchor","onPointerDownOutside","target","returnFocus","listbox","onHighlight","payload","triggerSizes","openBlock","createBlock","unref","PopoverRoot","_cache","withCtx","createVNode","PopoverAnchor","renderSlot","_ctx","createElementVNode","mergeProps","createElementBlock","toDisplayString","createCommentVNode","Fragment","renderList","PopoverPortal","PopoverContent","normalizeClass","normalizeStyle","ListboxRoot","ListboxFilter","ListboxContent","ListboxVirtualizer","ListboxItem","$event","createTextVNode"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, shallowRef, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Icon from '../Icon/Icon.vue'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n /** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search\n * results. Options picked from the list are remembered without it. */\n selectedOptions?: SelectOption<T>[]\n multiple?: boolean\n /** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */\n clearable?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results\n * for the query, such as from a server search through `query-change`. */\n filter?: boolean | ((option: SelectOption<T>, query: string) => boolean)\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n selectedOptions: () => [],\n searchable: undefined,\n filter: true,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string]; 'create-error': [error: unknown] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\n// Options picked from earlier results, which `options` may no longer hold.\nconst picked = shallowRef<SelectOption<T>[]>([])\nconst selected = computed(() =>\n values.value\n .map((value) =>\n [props.options, props.selectedOptions, picked.value]\n .map((list) => list.find((option) => option.value === value))\n .find((option) => !!option)\n )\n .filter((option) => !!option)\n)\nconst search = computed(\n () =>\n props.searchable ??\n (props.multiple || props.canCreate || props.clearable || props.filter === false || props.options.length > 8)\n)\nconst removable = computed(() => props.multiple || props.clearable)\nconst matches = (option: SelectOption<T>, text: string) =>\n typeof props.filter === 'function'\n ? props.filter(option, text)\n : option.label.toLowerCase().includes(text.toLowerCase())\nconst listed = computed(() => {\n const text = query.value.trim()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || props.filter === false || matches(option, text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n picked.value = [option, ...picked.value.filter((known) => known.value !== option.value)]\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nconst creating = ref(false)\n\n// A failed createOption keeps what was typed, so it can be tried again.\nasync function create() {\n if (creating.value) return\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n creating.value = true\n try {\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n } catch (error) {\n emit('create-error', error)\n } finally {\n creating.value = false\n }\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = props.multiple ? values.value.filter((chosen) => chosen !== value) : null\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && removable.value && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\n\n// Nor does Reka know a press on it isn't outside: closing there would let the click reopen the list.\nfunction onPointerDownOutside(event: CustomEvent<{ originalEvent: PointerEvent }>) {\n const target = event.detail.originalEvent.target\n if (target instanceof Node && anchor.value?.$el?.contains(target)) event.preventDefault()\n}\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). For a server search, set `:filter=\"false\"` and replace `options` on `query-change`. The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-strong hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n </span>\n <Tag\n v-else-if=\"selected[0]!.color\"\n :color=\"selected[0]!.color\"\n :icon=\"selected[0]!.icon\"\n :icon-color=\"selected[0]!.iconColor\"\n :name=\"selected[0]!.label\"\n />\n <span v-else-if=\"selected[0]!.icon\" class=\"inline-flex min-w-0 items-center gap-1.5\">\n <Icon class=\"shrink-0\" :color=\"selected[0]!.iconColor ?? 'ink-subtle'\" :name=\"selected[0]!.icon\" />\n <span class=\"truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n </span>\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @pointer-down-outside=\"onPointerDownOutside\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"removable\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"removable && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag\n v-if=\"option.color\"\n :color=\"option.color\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n <template v-else>\n <Icon\n v-if=\"option.icon\"\n class=\"shrink-0\"\n :color=\"option.iconColor ?? 'ink-subtle'\"\n :name=\"option.icon\"\n />{{ option.label }}\n </template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag\n v-if=\"option.color\"\n :color=\"option.color\"\n :icon=\"option.icon\"\n :icon-color=\"option.iconColor\"\n :name=\"option.label\"\n />\n <template v-else>\n <Icon\n v-if=\"option.icon\"\n class=\"shrink-0\"\n :color=\"option.iconColor ?? 'ink-subtle'\"\n :name=\"option.icon\"\n />{{ option.label }}\n </template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { Hue } from '../../utils/hues';
|
|
2
|
+
import type { IconColor } from '../Icon/colors';
|
|
2
3
|
export type SelectValue = string | number;
|
|
3
4
|
export interface SelectOption<T extends SelectValue = SelectValue> {
|
|
4
5
|
value: T;
|
|
5
6
|
label: string;
|
|
6
7
|
/** Shows the option as a Tag of this hue. */
|
|
7
8
|
color?: Hue | null;
|
|
9
|
+
/** An icon before the label, through the app's icon resolver: in the list, the trigger and its tags. */
|
|
10
|
+
icon?: string;
|
|
11
|
+
/** The icon's colour; ink-subtle by default. */
|
|
12
|
+
iconColor?: IconColor;
|
|
8
13
|
disabled?: boolean;
|
|
9
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.js","names":["_sfc_main","Tag_vue_vue_type_script_setup_true_lang_default","Tag_default"],"sources":["../../../src/components/Tag/Tag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Hue } from '../../utils/hues'\nimport CloseSmallIcon from '../internal/CloseSmallIcon.vue'\n\nexport interface TagProps {\n /** The text; or use the default slot. */\n name?: string\n /** soft: a label people create, with an optional hue dot, removable. outline: a value from a fixed set (a stage, a status). */\n variant?: 'soft' | 'outline'\n /** soft only: a dot of the tag's own hue, on a neutral chip. */\n color?: Hue\n /** outline only: the quieter, superseded side, e.g. the \"from\" of a stage change. */\n muted?: boolean\n /** soft only. */\n removable?: boolean\n /** The remove button's accessible name. */\n removeLabel?: string\n /** A toggle button for quick choices, bound to `v-model:pressed`. Group several in a `role=\"group\"` with a label. */\n pressable?: boolean\n /** pressable only. */\n disabled?: boolean\n /** pressable only: md is 28px, sm 22px for a dense row of filters. */\n size?: 'sm' | 'md'\n}\n\nwithDefaults(defineProps<TagProps>(), {\n name: undefined,\n variant: 'soft',\n color: undefined,\n removeLabel: 'Remove tag',\n size: 'md'\n})\n\ndefineEmits<{ remove: [] }>()\n\nconst pressed = defineModel<boolean>('pressed', { default: false })\n\nconst dots: Record<Hue, string> = {\n blue: 'bg-sui-hue-blue-ink',\n green: 'bg-sui-hue-green-ink',\n orange: 'bg-sui-hue-orange-ink',\n pink: 'bg-sui-hue-pink-ink',\n purple: 'bg-sui-hue-purple-ink',\n slate: 'bg-sui-hue-slate-ink',\n red: 'bg-sui-hue-red-ink',\n amber: 'bg-sui-hue-amber-ink',\n gray: 'bg-sui-ink'\n}\n</script>\n\n<!-- A label: soft for the ones people create, outline for a value from a fixed set; or, pressable, a toggle chip. -->\n<template>\n <button\n v-if=\"pressable\"\n :aria-pressed=\"pressed\"\n class=\"inline-flex cursor-pointer items-center rounded-md border leading-none font-medium whitespace-nowrap transition-colors duration-150 outline-hidden focus-visible:sui-ring disabled:cursor-default disabled:opacity-50\"\n :class=\"[\n size === 'sm' ? 'h-5.5 gap-1 px-2 text-xs' : 'h-7 gap-1.5 px-3 text-13',\n pressed\n ? 'border-sui-accent-line bg-sui-accent-wash text-sui-accent enabled:hover:border-sui-accent-bright enabled:hover:bg-sui-accent-soft enabled:active:border-sui-accent-solid enabled:active:bg-sui-accent-soft'\n : 'border-sui-line bg-sui-surface text-sui-ink-body enabled:hover:border-sui-line-strong enabled:hover:bg-sui-canvas enabled:hover:text-sui-ink enabled:active:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"pressed = !pressed\"\n >\n <svg\n v-if=\"pressed\"\n aria-hidden=\"true\"\n class=\"-ml-0.5 size-3 shrink-0 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3.5 8.25l3 3 6-6.5\" />\n </svg>\n <slot>{{ name }}</slot>\n </button>\n <span\n v-else-if=\"variant === 'outline'\"\n class=\"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap\"\n :class=\"\n muted ? 'border-sui-line-subtle text-sui-ink-muted' : 'border-sui-line bg-sui-sunken font-medium text-sui-ink'\n \"\n >\n <slot>{{ name }}</slot>\n </span>\n <div\n v-else\n class=\"inline-flex items-center gap-1.5 rounded-md bg-sui-sunken px-2 py-0.5 text-xs font-medium text-sui-ink-body\"\n >\n <span v-if=\"color\" class=\"h-1.5 w-1.5 shrink-0 rounded-full\" :class=\"dots[color]\" />\n <span class=\"truncate\"\n ><slot>{{ name }}</slot></span\n >\n <button\n v-if=\"removable\"\n :aria-label=\"removeLabel\"\n class=\"-mr-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-sui-line focus-visible:sui-ring\"\n type=\"button\"\n @click=\"$emit('remove')\"\n >\n <CloseSmallIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </button>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Tag.js","names":["_sfc_main","Tag_vue_vue_type_script_setup_true_lang_default","Tag_default"],"sources":["../../../src/components/Tag/Tag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Hue } from '../../utils/hues'\nimport Icon from '../Icon/Icon.vue'\nimport type { IconColor } from '../Icon/colors'\nimport CloseSmallIcon from '../internal/CloseSmallIcon.vue'\n\nexport interface TagProps {\n /** The text; or use the default slot. */\n name?: string\n /** soft: a label people create, with an optional hue dot, removable. outline: a value from a fixed set (a stage, a status). */\n variant?: 'soft' | 'outline'\n /** soft only: a dot of the tag's own hue, on a neutral chip. */\n color?: Hue\n /** soft and outline: a 14px icon before the name, through the app's icon resolver, in place of the dot. */\n icon?: string\n /** The icon's colour; ink-subtle by default. */\n iconColor?: IconColor\n /** outline only: the quieter, superseded side, e.g. the \"from\" of a stage change. */\n muted?: boolean\n /** soft only. */\n removable?: boolean\n /** The remove button's accessible name. */\n removeLabel?: string\n /** A toggle button for quick choices, bound to `v-model:pressed`. Group several in a `role=\"group\"` with a label. */\n pressable?: boolean\n /** pressable only. */\n disabled?: boolean\n /** pressable only: md is 28px, sm 22px for a dense row of filters. */\n size?: 'sm' | 'md'\n}\n\nwithDefaults(defineProps<TagProps>(), {\n name: undefined,\n variant: 'soft',\n color: undefined,\n icon: undefined,\n iconColor: 'ink-subtle',\n removeLabel: 'Remove tag',\n size: 'md'\n})\n\ndefineEmits<{ remove: [] }>()\n\nconst pressed = defineModel<boolean>('pressed', { default: false })\n\nconst dots: Record<Hue, string> = {\n blue: 'bg-sui-hue-blue-ink',\n green: 'bg-sui-hue-green-ink',\n orange: 'bg-sui-hue-orange-ink',\n pink: 'bg-sui-hue-pink-ink',\n purple: 'bg-sui-hue-purple-ink',\n slate: 'bg-sui-hue-slate-ink',\n red: 'bg-sui-hue-red-ink',\n amber: 'bg-sui-hue-amber-ink',\n gray: 'bg-sui-ink'\n}\n</script>\n\n<!-- A label: soft for the ones people create, outline for a value from a fixed set; or, pressable, a toggle chip. -->\n<template>\n <button\n v-if=\"pressable\"\n :aria-pressed=\"pressed\"\n class=\"inline-flex cursor-pointer items-center rounded-md border leading-none font-medium whitespace-nowrap transition-colors duration-150 outline-hidden focus-visible:sui-ring disabled:cursor-default disabled:opacity-50\"\n :class=\"[\n size === 'sm' ? 'h-5.5 gap-1 px-2 text-xs' : 'h-7 gap-1.5 px-3 text-13',\n pressed\n ? 'border-sui-accent-line bg-sui-accent-wash text-sui-accent enabled:hover:border-sui-accent-bright enabled:hover:bg-sui-accent-soft enabled:active:border-sui-accent-solid enabled:active:bg-sui-accent-soft'\n : 'border-sui-line bg-sui-surface text-sui-ink-body enabled:hover:border-sui-line-strong enabled:hover:bg-sui-canvas enabled:hover:text-sui-ink enabled:active:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"pressed = !pressed\"\n >\n <svg\n v-if=\"pressed\"\n aria-hidden=\"true\"\n class=\"-ml-0.5 size-3 shrink-0 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3.5 8.25l3 3 6-6.5\" />\n </svg>\n <slot>{{ name }}</slot>\n </button>\n <span\n v-else-if=\"variant === 'outline'\"\n class=\"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap\"\n :class=\"\n muted ? 'border-sui-line-subtle text-sui-ink-muted' : 'border-sui-line bg-sui-sunken font-medium text-sui-ink'\n \"\n >\n <Icon v-if=\"icon\" class=\"size-3.5 shrink-0\" :color=\"iconColor\" :name=\"icon\" />\n <slot>{{ name }}</slot>\n </span>\n <div\n v-else\n class=\"inline-flex items-center gap-1.5 rounded-md bg-sui-sunken px-2 py-0.5 text-xs font-medium text-sui-ink-body\"\n >\n <Icon v-if=\"icon\" class=\"size-3.5 shrink-0\" :color=\"iconColor\" :name=\"icon\" />\n <span v-else-if=\"color\" class=\"h-1.5 w-1.5 shrink-0 rounded-full\" :class=\"dots[color]\" />\n <span class=\"truncate\"\n ><slot>{{ name }}</slot></span\n >\n <button\n v-if=\"removable\"\n :aria-label=\"removeLabel\"\n class=\"-mr-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-sui-line focus-visible:sui-ring\"\n type=\"button\"\n @click=\"$emit('remove')\"\n >\n <CloseSmallIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </button>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Hue } from '../../utils/hues';
|
|
2
|
+
import type { IconColor } from '../Icon/colors';
|
|
2
3
|
export interface TagProps {
|
|
3
4
|
/** The text; or use the default slot. */
|
|
4
5
|
name?: string;
|
|
@@ -6,6 +7,10 @@ export interface TagProps {
|
|
|
6
7
|
variant?: 'soft' | 'outline';
|
|
7
8
|
/** soft only: a dot of the tag's own hue, on a neutral chip. */
|
|
8
9
|
color?: Hue;
|
|
10
|
+
/** soft and outline: a 14px icon before the name, through the app's icon resolver, in place of the dot. */
|
|
11
|
+
icon?: string;
|
|
12
|
+
/** The icon's colour; ink-subtle by default. */
|
|
13
|
+
iconColor?: IconColor;
|
|
9
14
|
/** outline only: the quieter, superseded side, e.g. the "from" of a stage change. */
|
|
10
15
|
muted?: boolean;
|
|
11
16
|
/** soft only. */
|
|
@@ -24,13 +29,13 @@ type __VLS_ModelProps = {
|
|
|
24
29
|
'pressed'?: boolean;
|
|
25
30
|
};
|
|
26
31
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
27
|
-
declare var __VLS_1: {},
|
|
32
|
+
declare var __VLS_1: {}, __VLS_8: {}, __VLS_15: {};
|
|
28
33
|
type __VLS_Slots = {} & {
|
|
29
34
|
default?: (props: typeof __VLS_1) => any;
|
|
30
35
|
} & {
|
|
31
|
-
default?: (props: typeof
|
|
36
|
+
default?: (props: typeof __VLS_8) => any;
|
|
32
37
|
} & {
|
|
33
|
-
default?: (props: typeof
|
|
38
|
+
default?: (props: typeof __VLS_15) => any;
|
|
34
39
|
};
|
|
35
40
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
36
41
|
remove: () => any;
|
|
@@ -43,7 +48,9 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
43
48
|
size: "sm" | "md";
|
|
44
49
|
name: string;
|
|
45
50
|
color: Hue;
|
|
51
|
+
icon: string;
|
|
46
52
|
removeLabel: string;
|
|
53
|
+
iconColor: "danger" | "warning" | "success" | "ink" | "ink-emphasis" | "ink-body" | "ink-subtle" | "ink-muted" | "ink-faint" | "mark-faint" | "accent" | "accent-solid" | "on-accent" | "on-contrast" | "on-danger" | "on-inverse" | "success-mid" | "warning-mid" | "danger-mid" | "info-mid" | "info";
|
|
47
54
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
48
55
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
49
56
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import e from "../
|
|
2
|
-
import
|
|
1
|
+
import e from "../Icon/Icon.js";
|
|
2
|
+
import t from "../internal/CloseSmallIcon.js";
|
|
3
|
+
import { createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeModels as l, normalizeClass as u, openBlock as d, renderSlot as f, toDisplayString as p, useModel as m } from "vue";
|
|
3
4
|
//#region src/components/Tag/Tag.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
-
var
|
|
5
|
+
var h = ["aria-pressed", "disabled"], g = {
|
|
5
6
|
key: 0,
|
|
6
7
|
"aria-hidden": "true",
|
|
7
8
|
class: "-ml-0.5 size-3 shrink-0 fill-none stroke-current",
|
|
@@ -9,15 +10,17 @@ var p = ["aria-pressed", "disabled"], m = {
|
|
|
9
10
|
"stroke-linejoin": "round",
|
|
10
11
|
"stroke-width": "1.75",
|
|
11
12
|
viewBox: "0 0 16 16"
|
|
12
|
-
},
|
|
13
|
+
}, _ = {
|
|
13
14
|
key: 2,
|
|
14
15
|
class: "inline-flex items-center gap-1.5 rounded-md bg-sui-sunken px-2 py-0.5 text-xs font-medium text-sui-ink-body"
|
|
15
|
-
},
|
|
16
|
+
}, v = { class: "truncate" }, y = ["aria-label"], b = /*@__PURE__*/ c({
|
|
16
17
|
__name: "Tag",
|
|
17
|
-
props: /*@__PURE__*/
|
|
18
|
+
props: /*@__PURE__*/ l({
|
|
18
19
|
name: { default: void 0 },
|
|
19
20
|
variant: { default: "soft" },
|
|
20
21
|
color: { default: void 0 },
|
|
22
|
+
icon: { default: void 0 },
|
|
23
|
+
iconColor: { default: "ink-subtle" },
|
|
21
24
|
muted: { type: Boolean },
|
|
22
25
|
removable: { type: Boolean },
|
|
23
26
|
removeLabel: { default: "Remove tag" },
|
|
@@ -31,9 +34,9 @@ var p = ["aria-pressed", "disabled"], m = {
|
|
|
31
34
|
},
|
|
32
35
|
pressedModifiers: {}
|
|
33
36
|
}),
|
|
34
|
-
emits: /*@__PURE__*/
|
|
35
|
-
setup(
|
|
36
|
-
let
|
|
37
|
+
emits: /*@__PURE__*/ l(["remove"], ["update:pressed"]),
|
|
38
|
+
setup(c) {
|
|
39
|
+
let l = m(c, "pressed"), b = {
|
|
37
40
|
blue: "bg-sui-hue-blue-ink",
|
|
38
41
|
green: "bg-sui-hue-green-ink",
|
|
39
42
|
orange: "bg-sui-hue-orange-ink",
|
|
@@ -44,33 +47,43 @@ var p = ["aria-pressed", "disabled"], m = {
|
|
|
44
47
|
amber: "bg-sui-hue-amber-ink",
|
|
45
48
|
gray: "bg-sui-ink"
|
|
46
49
|
};
|
|
47
|
-
return (
|
|
50
|
+
return (m, x) => c.pressable ? (d(), i("button", {
|
|
48
51
|
key: 0,
|
|
49
|
-
"aria-pressed":
|
|
50
|
-
class:
|
|
51
|
-
disabled:
|
|
52
|
+
"aria-pressed": l.value,
|
|
53
|
+
class: u(["inline-flex cursor-pointer items-center rounded-md border leading-none font-medium whitespace-nowrap transition-colors duration-150 outline-hidden focus-visible:sui-ring disabled:cursor-default disabled:opacity-50", [c.size === "sm" ? "h-5.5 gap-1 px-2 text-xs" : "h-7 gap-1.5 px-3 text-13", l.value ? "border-sui-accent-line bg-sui-accent-wash text-sui-accent enabled:hover:border-sui-accent-bright enabled:hover:bg-sui-accent-soft enabled:active:border-sui-accent-solid enabled:active:bg-sui-accent-soft" : "border-sui-line bg-sui-surface text-sui-ink-body enabled:hover:border-sui-line-strong enabled:hover:bg-sui-canvas enabled:hover:text-sui-ink enabled:active:bg-sui-sunken"]]),
|
|
54
|
+
disabled: c.disabled,
|
|
52
55
|
type: "button",
|
|
53
|
-
onClick:
|
|
54
|
-
}, [
|
|
56
|
+
onClick: x[0] ||= (e) => l.value = !l.value
|
|
57
|
+
}, [l.value ? (d(), i("svg", g, [...x[2] ||= [a("path", { d: "M3.5 8.25l3 3 6-6.5" }, null, -1)]])) : r("", !0), f(m.$slots, "default", {}, () => [o(p(c.name), 1)])], 10, h)) : c.variant === "outline" ? (d(), i("span", {
|
|
55
58
|
key: 1,
|
|
56
|
-
class:
|
|
57
|
-
}, [
|
|
58
|
-
|
|
59
|
+
class: u(["inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap", c.muted ? "border-sui-line-subtle text-sui-ink-muted" : "border-sui-line bg-sui-sunken font-medium text-sui-ink"])
|
|
60
|
+
}, [c.icon ? (d(), n(e, {
|
|
61
|
+
key: 0,
|
|
62
|
+
class: "size-3.5 shrink-0",
|
|
63
|
+
color: c.iconColor,
|
|
64
|
+
name: c.icon
|
|
65
|
+
}, null, 8, ["color", "name"])) : r("", !0), f(m.$slots, "default", {}, () => [o(p(c.name), 1)])], 2)) : (d(), i("div", _, [
|
|
66
|
+
c.icon ? (d(), n(e, {
|
|
59
67
|
key: 0,
|
|
60
|
-
class:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
class: "size-3.5 shrink-0",
|
|
69
|
+
color: c.iconColor,
|
|
70
|
+
name: c.icon
|
|
71
|
+
}, null, 8, ["color", "name"])) : c.color ? (d(), i("span", {
|
|
64
72
|
key: 1,
|
|
65
|
-
"
|
|
73
|
+
class: u(["h-1.5 w-1.5 shrink-0 rounded-full", b[c.color]])
|
|
74
|
+
}, null, 2)) : r("", !0),
|
|
75
|
+
a("span", v, [f(m.$slots, "default", {}, () => [o(p(c.name), 1)])]),
|
|
76
|
+
c.removable ? (d(), i("button", {
|
|
77
|
+
key: 2,
|
|
78
|
+
"aria-label": c.removeLabel,
|
|
66
79
|
class: "-mr-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-sui-line focus-visible:sui-ring",
|
|
67
80
|
type: "button",
|
|
68
|
-
onClick:
|
|
69
|
-
}, [
|
|
81
|
+
onClick: x[1] ||= (e) => m.$emit("remove")
|
|
82
|
+
}, [s(t, { class: "fill-sui-ink-muted stroke-sui-ink-muted" })], 8, y)) : r("", !0)
|
|
70
83
|
]));
|
|
71
84
|
}
|
|
72
85
|
});
|
|
73
86
|
//#endregion
|
|
74
|
-
export {
|
|
87
|
+
export { b as default };
|
|
75
88
|
|
|
76
89
|
//# sourceMappingURL=Tag.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tag.vue_vue_type_script_setup_true_lang.js","names":["$emit","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_createVNode","CloseSmallIcon","CloseSmallIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","Tag_vue_vue_type_script_setup_true_lang_default","\"pressed\"","\"pressedModifiers\"","pressed","useModel","__props","dots","openBlock","createElementBlock","normalizeClass","_cache","createElementVNode","createCommentVNode","renderSlot","_ctx","createTextVNode","toDisplayString","createVNode"],"sources":["../../../src/components/Tag/Tag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Hue } from '../../utils/hues'\nimport CloseSmallIcon from '../internal/CloseSmallIcon.vue'\n\nexport interface TagProps {\n /** The text; or use the default slot. */\n name?: string\n /** soft: a label people create, with an optional hue dot, removable. outline: a value from a fixed set (a stage, a status). */\n variant?: 'soft' | 'outline'\n /** soft only: a dot of the tag's own hue, on a neutral chip. */\n color?: Hue\n /** outline only: the quieter, superseded side, e.g. the \"from\" of a stage change. */\n muted?: boolean\n /** soft only. */\n removable?: boolean\n /** The remove button's accessible name. */\n removeLabel?: string\n /** A toggle button for quick choices, bound to `v-model:pressed`. Group several in a `role=\"group\"` with a label. */\n pressable?: boolean\n /** pressable only. */\n disabled?: boolean\n /** pressable only: md is 28px, sm 22px for a dense row of filters. */\n size?: 'sm' | 'md'\n}\n\nwithDefaults(defineProps<TagProps>(), {\n name: undefined,\n variant: 'soft',\n color: undefined,\n removeLabel: 'Remove tag',\n size: 'md'\n})\n\ndefineEmits<{ remove: [] }>()\n\nconst pressed = defineModel<boolean>('pressed', { default: false })\n\nconst dots: Record<Hue, string> = {\n blue: 'bg-sui-hue-blue-ink',\n green: 'bg-sui-hue-green-ink',\n orange: 'bg-sui-hue-orange-ink',\n pink: 'bg-sui-hue-pink-ink',\n purple: 'bg-sui-hue-purple-ink',\n slate: 'bg-sui-hue-slate-ink',\n red: 'bg-sui-hue-red-ink',\n amber: 'bg-sui-hue-amber-ink',\n gray: 'bg-sui-ink'\n}\n</script>\n\n<!-- A label: soft for the ones people create, outline for a value from a fixed set; or, pressable, a toggle chip. -->\n<template>\n <button\n v-if=\"pressable\"\n :aria-pressed=\"pressed\"\n class=\"inline-flex cursor-pointer items-center rounded-md border leading-none font-medium whitespace-nowrap transition-colors duration-150 outline-hidden focus-visible:sui-ring disabled:cursor-default disabled:opacity-50\"\n :class=\"[\n size === 'sm' ? 'h-5.5 gap-1 px-2 text-xs' : 'h-7 gap-1.5 px-3 text-13',\n pressed\n ? 'border-sui-accent-line bg-sui-accent-wash text-sui-accent enabled:hover:border-sui-accent-bright enabled:hover:bg-sui-accent-soft enabled:active:border-sui-accent-solid enabled:active:bg-sui-accent-soft'\n : 'border-sui-line bg-sui-surface text-sui-ink-body enabled:hover:border-sui-line-strong enabled:hover:bg-sui-canvas enabled:hover:text-sui-ink enabled:active:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"pressed = !pressed\"\n >\n <svg\n v-if=\"pressed\"\n aria-hidden=\"true\"\n class=\"-ml-0.5 size-3 shrink-0 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3.5 8.25l3 3 6-6.5\" />\n </svg>\n <slot>{{ name }}</slot>\n </button>\n <span\n v-else-if=\"variant === 'outline'\"\n class=\"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap\"\n :class=\"\n muted ? 'border-sui-line-subtle text-sui-ink-muted' : 'border-sui-line bg-sui-sunken font-medium text-sui-ink'\n \"\n >\n <slot>{{ name }}</slot>\n </span>\n <div\n v-else\n class=\"inline-flex items-center gap-1.5 rounded-md bg-sui-sunken px-2 py-0.5 text-xs font-medium text-sui-ink-body\"\n >\n <span v-if=\"color\" class=\"h-1.5 w-1.5 shrink-0 rounded-full\" :class=\"dots[color]\" />\n <span class=\"truncate\"\n ><slot>{{ name }}</slot></span\n >\n <button\n v-if=\"removable\"\n :aria-label=\"removeLabel\"\n class=\"-mr-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-sui-line focus-visible:sui-ring\"\n type=\"button\"\n @click=\"$emit('remove')\"\n >\n <CloseSmallIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </button>\n </div>\n</template>\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Tag.vue_vue_type_script_setup_true_lang.js","names":["$emit","_useModel","_openBlock","_createElementBlock","_normalizeClass","_createElementVNode","_createCommentVNode","_renderSlot","_createTextVNode","_toDisplayString","_createBlock","Icon","_createVNode","CloseSmallIcon","Icon_default","CloseSmallIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","Tag_vue_vue_type_script_setup_true_lang_default","\"pressed\"","\"pressedModifiers\"","pressed","useModel","__props","dots","openBlock","createElementBlock","normalizeClass","_cache","createElementVNode","createCommentVNode","renderSlot","_ctx","createTextVNode","toDisplayString","createBlock","createVNode"],"sources":["../../../src/components/Tag/Tag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Hue } from '../../utils/hues'\nimport Icon from '../Icon/Icon.vue'\nimport type { IconColor } from '../Icon/colors'\nimport CloseSmallIcon from '../internal/CloseSmallIcon.vue'\n\nexport interface TagProps {\n /** The text; or use the default slot. */\n name?: string\n /** soft: a label people create, with an optional hue dot, removable. outline: a value from a fixed set (a stage, a status). */\n variant?: 'soft' | 'outline'\n /** soft only: a dot of the tag's own hue, on a neutral chip. */\n color?: Hue\n /** soft and outline: a 14px icon before the name, through the app's icon resolver, in place of the dot. */\n icon?: string\n /** The icon's colour; ink-subtle by default. */\n iconColor?: IconColor\n /** outline only: the quieter, superseded side, e.g. the \"from\" of a stage change. */\n muted?: boolean\n /** soft only. */\n removable?: boolean\n /** The remove button's accessible name. */\n removeLabel?: string\n /** A toggle button for quick choices, bound to `v-model:pressed`. Group several in a `role=\"group\"` with a label. */\n pressable?: boolean\n /** pressable only. */\n disabled?: boolean\n /** pressable only: md is 28px, sm 22px for a dense row of filters. */\n size?: 'sm' | 'md'\n}\n\nwithDefaults(defineProps<TagProps>(), {\n name: undefined,\n variant: 'soft',\n color: undefined,\n icon: undefined,\n iconColor: 'ink-subtle',\n removeLabel: 'Remove tag',\n size: 'md'\n})\n\ndefineEmits<{ remove: [] }>()\n\nconst pressed = defineModel<boolean>('pressed', { default: false })\n\nconst dots: Record<Hue, string> = {\n blue: 'bg-sui-hue-blue-ink',\n green: 'bg-sui-hue-green-ink',\n orange: 'bg-sui-hue-orange-ink',\n pink: 'bg-sui-hue-pink-ink',\n purple: 'bg-sui-hue-purple-ink',\n slate: 'bg-sui-hue-slate-ink',\n red: 'bg-sui-hue-red-ink',\n amber: 'bg-sui-hue-amber-ink',\n gray: 'bg-sui-ink'\n}\n</script>\n\n<!-- A label: soft for the ones people create, outline for a value from a fixed set; or, pressable, a toggle chip. -->\n<template>\n <button\n v-if=\"pressable\"\n :aria-pressed=\"pressed\"\n class=\"inline-flex cursor-pointer items-center rounded-md border leading-none font-medium whitespace-nowrap transition-colors duration-150 outline-hidden focus-visible:sui-ring disabled:cursor-default disabled:opacity-50\"\n :class=\"[\n size === 'sm' ? 'h-5.5 gap-1 px-2 text-xs' : 'h-7 gap-1.5 px-3 text-13',\n pressed\n ? 'border-sui-accent-line bg-sui-accent-wash text-sui-accent enabled:hover:border-sui-accent-bright enabled:hover:bg-sui-accent-soft enabled:active:border-sui-accent-solid enabled:active:bg-sui-accent-soft'\n : 'border-sui-line bg-sui-surface text-sui-ink-body enabled:hover:border-sui-line-strong enabled:hover:bg-sui-canvas enabled:hover:text-sui-ink enabled:active:bg-sui-sunken'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"pressed = !pressed\"\n >\n <svg\n v-if=\"pressed\"\n aria-hidden=\"true\"\n class=\"-ml-0.5 size-3 shrink-0 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M3.5 8.25l3 3 6-6.5\" />\n </svg>\n <slot>{{ name }}</slot>\n </button>\n <span\n v-else-if=\"variant === 'outline'\"\n class=\"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap\"\n :class=\"\n muted ? 'border-sui-line-subtle text-sui-ink-muted' : 'border-sui-line bg-sui-sunken font-medium text-sui-ink'\n \"\n >\n <Icon v-if=\"icon\" class=\"size-3.5 shrink-0\" :color=\"iconColor\" :name=\"icon\" />\n <slot>{{ name }}</slot>\n </span>\n <div\n v-else\n class=\"inline-flex items-center gap-1.5 rounded-md bg-sui-sunken px-2 py-0.5 text-xs font-medium text-sui-ink-body\"\n >\n <Icon v-if=\"icon\" class=\"size-3.5 shrink-0\" :color=\"iconColor\" :name=\"icon\" />\n <span v-else-if=\"color\" class=\"h-1.5 w-1.5 shrink-0 rounded-full\" :class=\"dots[color]\" />\n <span class=\"truncate\"\n ><slot>{{ name }}</slot></span\n >\n <button\n v-if=\"removable\"\n :aria-label=\"removeLabel\"\n class=\"-mr-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full transition-colors hover:bg-sui-line focus-visible:sui-ring\"\n type=\"button\"\n @click=\"$emit('remove')\"\n >\n <CloseSmallIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </button>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CA,IAAMwB,IAAUvB,EAAoByB,GAAC,SAA6B,GAE5DC,IAA4B;GAChC,MAAM;GACN,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;GACR,OAAO;GACP,KAAK;GACL,OAAO;GACP,MAAM;EACR;E,iBAMUD,EAAA,aADRxB,EAAA,GAAAC,EA0BS,UAAA;;GAxBN,gBAAcqB,EAAA;GACf,OAAKpB,EAAA,CAAC,yNAAuN,CAC7MsB,EAAA,SAAI,OAAA,6BAAA,4BAA2EF,EAAA,Q;GAM9F,UAAUE,EAAA;GACX,MAAK;GACJ,SAAK,AAAAK,EAAA,QAAA,MAAE,EAAA,QAAO,CAAIP,EAAA;E,IAGXA,EAAA,SADRtB,EAAA,GAAAC,EAUM,OAVNc,GAUM,CAAA,GAAA,AAAAc,EAAA,OAAA,CADJ1B,EAAgC,QAAA,EAA1B,GAAE,sBAAqB,GAAA,MAAA,EAAA,C,kBAE/BE,EAAuB4B,EAAA,QAAA,WAAA,CAAA,SAAA,CAAd3B,EAAAC,EAAAiB,EAAA,IAAI,GAAA,CAAA,C,eAGFA,EAAA,YAAO,aADpBxB,EAAA,GAAAC,EASO,QAAA;;GAPL,OAAKC,EAAA,CAAC,0FACSsB,EAAA,QAAK,8CAAA,wD;E,IAIRA,EAAA,QAAZxB,EAAA,GAAAQ,EAA8EC,GAAA;;GAA5D,OAAM;GAAqB,OAAOe,EAAA;GAAY,MAAMA,EAAA;E,6CACtEnB,EAAuB4B,EAAA,QAAA,WAAA,CAAA,SAAA,CAAd3B,EAAAC,EAAAiB,EAAA,IAAI,GAAA,CAAA,C,YAEfxB,EAAA,GAAAC,EAkBM,OAlBNe,GAkBM;GAdQQ,EAAA,QAAZxB,EAAA,GAAAQ,EAA8EC,GAAA;;IAA5D,OAAM;IAAqB,OAAOe,EAAA;IAAY,MAAMA,EAAA;G,kCACrDA,EAAA,SAAjBxB,EAAA,GAAAC,EAAyF,QAAA;;IAAjE,OAAKC,EAAA,CAAC,qCAA4CuB,EAAKD,EAAA,MAAK,CAAA;;GACpFrB,EAEC,QAFDc,GAEC,CADEZ,EAAuB4B,EAAA,QAAA,WAAA,CAAA,SAAA,CAAd3B,EAAAC,EAAAiB,EAAA,IAAI,GAAA,CAAA,C;GAGRA,EAAA,aADRxB,EAAA,GAAAC,EAQS,UAAA;;IANN,cAAYuB,EAAA;IACb,OAAM;IACN,MAAK;IACJ,SAAK,AAAAK,EAAA,QAAA,MAAE/B,EAAAA,MAAK,QAAA;G,IAEbY,EAAkEC,GAAA,EAAlD,OAAM,0CAAyC,CAAA,C"}
|