@sparklingopinions/sparkling-ui 0.8.0 → 0.9.2

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.
@@ -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
- return () => n("svg", {
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 return () =>\n h('svg', {\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}\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;AAGtD,SAASC,EAAK,GAA2C;CACvD,IAAMC,IAAQC,EAAK,QAAQ,SAAS,MAAM;CAC1C,aACEC,EAAE,OAAO;EACP,OAAOF,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;AACL;AAEA,SAAgBE,EAAY,GAAqC;CAC/D,IAAMC,IAAMX,IAAWY,CAAI;CAC3B,IAAID,GAAK,OAAOA;CAChB,IAAME,IAAMT,EAAQQ,KAAQA,IAAOE,EAAYF;CAC/C,IAAI,CAACC,GACH,QAAQ,KAAK,iCAAiCD,EAAK,GAAG;MAIxD,OADKP,EAAS,IAAIQ,CAAG,KAAGR,EAAS,IAAIQ,GAAKP,EAAKF,EAAQS,EAAI,CAAC,GACrDR,EAAS,IAAIQ,CAAG;AACzB"}
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 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":""}
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, ["name"])) : (m(), o("span", D, g(l.placeholder), 1))], 16, T)]),
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":";;;;;;;;;;;;;;;;;;;;;A,GAwFMyB,IACJ,qO;;;;;;;;;;;;;;EArEF,IAAMK,IAAQC,GAMRC,IAAOH,GAEPI,IAAQjC,EAAwC+B,GAAA,YAAkB,GAIlEI,IAAQC,EAAS,GACjBC,IAAYC,QAChB,OAAO,YACL,OAAO,QAAQH,CAAK,CAAC,CAAC,QACnB,CAACI,OAASA,MAAQ,iBAAiBA,MAAQ,QAAQA,MAAQ,WAAW,gBAAgB,KAAKA,CAAG,EACjG,CACF,CACF,GACMC,IAAOF,QAAgBH,EAAM,iBAAwCL,EAAM,WAAW,GAEtFW,IAAQH,QAAeR,EAAM,YAAY,OAAO,GAChDY,IAAYJ,QAAeR,EAAM,YAAY,WAAW,GAExDa,IAAQC,EAAIC,EAAMZ,EAAM,KAAK,CAAkB,GAC/Ca,IAAWF,EAAID,EAAM,KAAK,GAC1BI,IAAUH,EAAI,EAAK,GACnBI,IAAUC,EAA4B,SAAS;EAGrD,EAAMhB,IAAQ,MAAU;GACtB,AAAIc,EAAQ,UACZ,EAAM,QAAQF,EAAMK,CAAK,GACzB,EAAS,QAAQP,EAAM;EACzB,CAAC;EAED,IAAMQ,IAAOb,QAAgBG,EAAM,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK,MAAME,EAAM,OAAO,UAAU,KAAK,EAAE,CAAC,CAAE;EAEvG,eAAeS,IAAO;GACpB,AAAItB,EAAM,aACV,EAAQ,QAAQ,IAChB,MAAMuB,EAAS,GACfL,EAAQ,OAAO,cAAc,UAAU,CAAC,EAAE,MAAM;EAClD;EAEA,SAASM,IAAO;GACd,EAAQ,QAAQ;GAChB,IAAMJ,IAAQL,EAAMF,EAAM,KAAK,KAAK;GAIpC,AAFIO,OAAWL,EAAMZ,EAAM,KAAK,KAAK,UAAO,EAAM,QAAQiB,IAC1D,EAAS,QAAQP,EAAM,OACvBX,EAAK,MAAM;EACb;EAEA,SAASuB,EAAO,GAAsB;GAIpC,AAHAC,EAAM,eAAe,GACrBA,EAAM,gBAAgB,GACtB,EAAM,QAAQV,EAAS,OACvB,EAAQ,QAAQ;EAClB;EAEA,SAASW,EAAQ,GAAsB;GACrC,AAAIf,EAAU,SAASc,EAAM,aAC7BA,EAAM,eAAe,GACpBA,EAAO,OAAuB,KAAK;EACtC;E,iBAUUzB,EAAA,YAAO,YADf9B,EAAA,GAAAC,EAiCSC,GAAA;;GA/BE,YAAA8B,EAAA;G,wBAAA,AAAA2B,EAAA,QAAA,MAAA,EAAA,QAAKC,GAIO,AAAAD,EAAA,QAAA,MAAA5B,EAAI,MAAA,C;GAHxB,cAAYQ,EAAA;GACZ,UAAUT,EAAA;GACV,SAASA,EAAA;;GAGC,SAAO3B,GAuBP,EAvBW,SAAM,WAAQ,kBAAQ,CAC1CC,EAsBS,UAtBTC,EACU+B,EAqBD,OArBU;IAChB,iBAAe4B;IAChB,iBAAc;IACb,OAAK;KAAcxC;;KAA8DM,EAAA,WAAQ,gBAAA;;IAKzF,UAAUA,EAAA;IACX,MAAK;IACJ,SAAOmC;G,KAEI1B,EAAA,SAAZvC,EAAA,GAAAM,EAAqD,QAArDa,GAAqDZ,EAAhBgC,EAAA,KAAI,IAAG,MAAE,CAAA,K,WAEtC8B,EAAQ,MADhBrE,EAAA,GAAAC,EAMEQ,GAAA;;IAJC,MAAM4D,EAAQ,EAAA,CAAI;IAClB,cAAYA,EAAQ,EAAA,CAAI;IACxB,MAAMA,EAAQ,EAAA,CAAI;IACnB,SAAQ;;;;;G,OAEVrE,EAAA,GAAAM,EAAgE,QAAhEc,GAAgEb,EAArBuB,EAAA,WAAW,GAAA,CAAA,E;;;;;;;E,OAI5D9B,EAAA,GAAAM,EAqCM,OArCND,EAqCM,EAAA,KAAA,EAAA,GAnCI+B,EAAA,OAAS;GACb,SAAA;GAAJ,KAAIW;GACJ,OAAK,CAAC,YAAU;IACAvB;IAAagB,EAAA,Q;IAA8XV,EAAA,WAAQ,gBAAA;;;GAQna,MAAK;GACJ,UAAUA,EAAA,WAAQ,KAAA;GAClB,SAAOqB;GACP,WAAO,AAAAQ,EAAA,OAAAjD,GAAA,MAAA,CAASoC,EAAA,UAAYc,EAAO,eAAc,GAAIT,EAAI,IAAA,CAAA,OAAA,CAAA;E,KAE1D/C,EAIM,OAAA,MAAA,CAHQmC,EAAA,SAAZvC,EAAA,GAAAM,EAAqD,QAArDgB,GAAqDf,EAAhBgC,EAAA,KAAI,IAAG,MAAE,CAAA,K,WACjCG,EAAA,SACb1C,EAAA,GAAAM,EAA6E,QAAA;;GAA/D,OAAKK,EAAA,EAAA,uBAA2B8B,EAAA,MAAS,CAAA;EAAO,GAAAlC,EAAAmC,EAAA,KAAK,GAAA,CAAA,MADnE1C,EAAA,GAAAM,EAAuE,QAAvEiB,GAAuEhB,EAArBuB,EAAA,WAAW,GAAA,CAAA,E,IAGpDgB,EAAA,SAAX9C,EAAA,GAAAM,EAaM,OAAA;;GAbc,OAAKK,EAAA,CAAC,yBAAuB,EAAA,UAAqB6B,EAAA,MAAK,CAAA,CAAA;E,IACzE5B,EAWEC,GAAA;GAVS,YAAA6B,EAAA;GAAA,uBAAA,AAAAiB,EAAA,QAAA,MAAA,EAAK,QAAAC;GACb,cAAYrB,EAAA;GACZ,eAAW,CAAGC,EAAA;GACf,OAAK7B,EAAA,CAAC,eACE6B,EAAA,SAAK,oBAAA,CAAA;GACZ,aAAaV,EAAA;GACb,MAAMoB,EAAA;GACN,QAAMG;GACN,WAAO,CAAa3C,EAAAI,EAAA0C,GAAO,CAAA,MAAA,CAAA,GAAA,CAAA,OAAA,CAAA,GACT9C,EAAAI,EAAAwC,GAAM,CAAA,MAAA,CAAA,GAAA,CAAA,KAAA,CAAA,C"}
@@ -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)\nconst pointer = 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 pointer.value = false\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, the keyboard or the pointer should.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || pointer.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && !pointer.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.capture=\"((keyboard = false), (pointer = true))\"\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 { useFieldControl as e } from "../FormField/context.js";
2
- import t from "../internal/CheckIcon.js";
3
- import n from "../Spinner/Spinner.js";
4
- import r from "../Tag/Tag.js";
5
- import { Fragment as i, computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, createTextVNode as u, createVNode as d, defineComponent as f, mergeModels as p, mergeProps as ee, nextTick as m, normalizeClass as h, normalizeStyle as te, openBlock as g, ref as _, renderList as v, renderSlot as y, shallowRef as ne, toDisplayString as b, unref as x, useAttrs as re, useModel as ie, withCtx as S } from "vue";
6
- import { ListboxContent as ae, ListboxFilter as oe, ListboxItem as C, ListboxRoot as se, ListboxVirtualizer as ce, PopoverAnchor as le, PopoverContent as ue, PopoverPortal as de, PopoverRoot as fe } from "reka-ui";
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
- }, ve = {
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
- }, ye = {
31
+ }, xe = {
28
32
  key: 1,
29
33
  class: "flex justify-center py-6"
30
- }, be = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, xe = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, Se = { class: "inline-flex min-w-0 items-center gap-1.5" }, Ce = {
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
- }, we = {
37
+ }, Ee = {
34
38
  key: 3,
35
39
  class: "px-3.5 py-3 text-13 text-sui-ink-muted"
36
- }, w = "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", T = /*@__PURE__*/ f({
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__*/ p({
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__*/ p(["query-change", "create-error"], ["update:modelValue"]),
78
- setup(f, { emit: p }) {
79
- let T = f, E = p, D = re(), O = a(() => D["aria-label"]), Te = a(() => Object.fromEntries(Object.entries(D).filter(([e]) => e !== "aria-label"))), k = ie(f, "modelValue"), A = _(!1), j = _(""), M = _(!1), N = a(() => Array.isArray(k.value) ? k.value : k.value === null || k.value === void 0 ? [] : [k.value]), P = ne([]), F = a(() => N.value.map((e) => [
80
- T.options,
81
- T.selectedOptions,
82
- P.value
83
- ].map((t) => t.find((t) => t.value === e)).find((e) => !!e)).filter((e) => !!e)), I = a(() => T.searchable ?? (T.multiple || T.canCreate || T.clearable || T.filter === !1 || T.options.length > 8)), L = a(() => T.multiple || T.clearable), Ee = (e, t) => typeof T.filter == "function" ? T.filter(e, t) : e.label.toLowerCase().includes(t.toLowerCase()), R = a(() => {
84
- let e = j.value.trim();
85
- return T.options.filter((e) => !(T.multiple && I.value && N.value.includes(e.value))).filter((t) => !e || T.filter === !1 || Ee(t, e));
86
- }), z = a(() => {
87
- let e = j.value.trim().toLowerCase();
88
- return T.canCreate && !!e && !T.options.some((t) => t.label.toLowerCase() === e);
89
- }), De = a(() => R.value.length > 20), B = [
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 = v(!1), F = o(() => Array.isArray(A.value) ? A.value : A.value === null || A.value === void 0 ? [] : [A.value]), I = ne([]), L = o(() => F.value.map((e) => [
84
+ E.options,
85
+ E.selectedOptions,
86
+ I.value
87
+ ].map((t) => t.find((t) => t.value === e)).find((e) => !!e)).filter((e) => !!e)), R = o(() => E.searchable ?? (E.multiple || E.canCreate || E.clearable || E.filter === !1 || E.options.length > 8)), z = o(() => E.multiple || E.clearable), Oe = (e, t) => typeof E.filter == "function" ? E.filter(e, t) : e.label.toLowerCase().includes(t.toLowerCase()), B = o(() => {
88
+ let e = M.value.trim();
89
+ return E.options.filter((e) => !(E.multiple && R.value && F.value.includes(e.value))).filter((t) => !e || E.filter === !1 || Oe(t, e));
90
+ }), V = o(() => {
91
+ let e = M.value.trim().toLowerCase();
92
+ return E.canCreate && !!e && !E.options.some((t) => t.label.toLowerCase() === e);
93
+ }), ke = o(() => B.value.length > 20), H = [
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
- ], V = a(() => B[j.value.length % B.length]), H = { create: !0 }, { attrs: Oe, invalid: U } = e(() => void 0, () => T.invalid);
99
- function W(e) {
100
- j.value = e, E("query-change", e);
102
+ ], U = o(() => H[M.value.length % H.length]), W = { create: !0 }, { attrs: Ae, invalid: G } = t(() => void 0, () => E.invalid);
103
+ function K(e) {
104
+ M.value = e, D("query-change", e);
101
105
  }
102
- function G(e) {
103
- T.disabled || (M.value = e, A.value = !0);
106
+ function q(e) {
107
+ E.disabled || (N.value = e, P.value = !1, j.value = !0);
104
108
  }
105
- function K() {
106
- A.value = !1, j.value && W("");
109
+ function J() {
110
+ j.value = !1, M.value && K("");
107
111
  }
108
- function q(e) {
109
- A.value ? K() : G(!!e && e.detail === 0);
112
+ function Y(e) {
113
+ j.value ? J() : q(!!e && e.detail === 0);
110
114
  }
111
- function J(e) {
112
- P.value = [e, ...P.value.filter((t) => t.value !== e.value)], T.multiple ? (k.value = N.value.includes(e.value) ? N.value.filter((t) => t !== e.value) : [...N.value, e.value], W("")) : (k.value = e.value, K());
115
+ function je(e) {
116
+ I.value = [e, ...I.value.filter((t) => t.value !== e.value)], E.multiple ? (A.value = F.value.includes(e.value) ? F.value.filter((t) => t !== e.value) : [...F.value, e.value], K("")) : (A.value = e.value, J());
113
117
  }
114
- let Y = _(!1);
115
- async function ke() {
116
- if (Y.value) return;
117
- let e = j.value.trim(), t = T.createColored ? {
118
+ let X = v(!1);
119
+ async function Me() {
120
+ if (X.value) return;
121
+ let e = M.value.trim(), t = E.createColored ? {
118
122
  label: e,
119
- color: V.value
123
+ color: U.value
120
124
  } : { label: e };
121
- Y.value = !0;
125
+ X.value = !0;
122
126
  try {
123
- J(T.createOption ? await T.createOption(t) : {
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
- E("create-error", e);
133
+ D("create-error", e);
130
134
  } finally {
131
- Y.value = !1;
135
+ X.value = !1;
132
136
  }
133
137
  }
134
- function X(e, t) {
135
- e.preventDefault(), "create" in t ? ke() : t.disabled || J(t);
138
+ function Z(e, t) {
139
+ e.preventDefault(), "create" in t ? Me() : t.disabled || je(t);
136
140
  }
137
- function Z(e) {
138
- k.value = T.multiple ? N.value.filter((t) => t !== e) : null;
141
+ function Ne(e) {
142
+ A.value = E.multiple ? F.value.filter((t) => t !== e) : null;
139
143
  }
140
- function Ae(e) {
144
+ function Pe(e) {
141
145
  [
142
146
  "ArrowUp",
143
147
  "ArrowDown",
144
148
  "Home",
145
149
  "End"
146
- ].includes(e.key) && (M.value = !0), e.key === "Backspace" && L.value && !j.value && N.value.length && Z(N.value[N.value.length - 1]);
150
+ ].includes(e.key) && (N.value = !0), e.key === "Backspace" && z.value && !M.value && F.value.length && Ne(F.value[F.value.length - 1]);
147
151
  }
148
- function je(e) {
149
- !A.value && ["ArrowDown", "ArrowUp"].includes(e.key) && (e.preventDefault(), G(!0));
152
+ function Fe(e) {
153
+ !j.value && ["ArrowDown", "ArrowUp"].includes(e.key) && (e.preventDefault(), q(!0));
150
154
  }
151
- function Me(e) {
152
- e.preventDefault(), m(() => e.target?.querySelector("[data-select-filter], [role=\"listbox\"]")?.focus());
155
+ function Ie(e) {
156
+ e.preventDefault(), h(() => e.target?.querySelector("[data-select-filter], [role=\"listbox\"]")?.focus());
153
157
  }
154
- let Q = _();
155
- function Ne(e) {
158
+ let Q = v();
159
+ function Le(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 Pe(e) {
163
+ function Re(e) {
160
164
  e.preventDefault(), Q.value?.$el?.focus();
161
165
  }
162
- let $ = _();
163
- function Fe(e) {
164
- !M.value && e && (N.value.includes(e.value) || m(() => $.value && !M.value && ($.value.highlightedElement = null)));
166
+ let $ = v();
167
+ function ze(e) {
168
+ N.value || P.value || !e || F.value.includes(e.value) || h(() => $.value && !N.value && !P.value && ($.value.highlightedElement = null));
165
169
  }
166
- let Ie = {
170
+ let Be = {
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 (e, a) => (g(), o(x(fe), {
171
- open: A.value,
172
- "onUpdate:open": a[2] ||= (e) => e ? G(!1) : K()
174
+ return (t, o) => (_(), s(S(fe), {
175
+ open: j.value,
176
+ "onUpdate:open": o[2] ||= (e) => e ? q(!1) : J()
173
177
  }, {
174
- default: S(() => [d(x(le), {
178
+ default: C(() => [f(S(le), {
175
179
  ref_key: "anchor",
176
180
  ref: Q,
177
181
  "as-child": ""
178
182
  }, {
179
- default: S(() => [y(e.$slots, "trigger", {
180
- open: A.value,
181
- selected: F.value,
182
- toggle: q
183
- }, () => [l("button", ee({
184
- ...x(Oe),
185
- ...Te.value
183
+ default: C(() => [b(t.$slots, "trigger", {
184
+ open: j.value,
185
+ selected: L.value,
186
+ toggle: Y
187
+ }, () => [u("button", ee({
188
+ ...S(Ae),
189
+ ...De.value
186
190
  }, {
187
- "aria-expanded": A.value,
191
+ "aria-expanded": j.value,
188
192
  "aria-haspopup": "listbox",
189
- "aria-invalid": x(U) ? "true" : void 0,
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", [Ie[f.size], x(U) ? "border-sui-danger-mid" : "border-sui-line-strong hover:border-sui-ink-muted"]],
191
- disabled: f.disabled,
193
+ "aria-invalid": S(G) ? "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", [Be[p.size], S(G) ? "border-sui-danger-mid" : "border-sui-line-strong hover:border-sui-ink-muted"]],
195
+ disabled: p.disabled,
192
196
  type: "button",
193
- onClick: q,
194
- onKeydown: je
197
+ onClick: Y,
198
+ onKeydown: Fe
195
199
  }), [
196
- O.value ? (g(), c("span", me, b(O.value) + ": ", 1)) : s("", !0),
197
- F.value.length ? f.multiple ? (g(), c("span", ge, [(g(!0), c(i, null, v(F.value, (e) => (g(), o(r, {
200
+ k.value ? (_(), l("span", me, x(k.value) + ": ", 1)) : c("", !0),
201
+ L.value.length ? p.multiple ? (_(), l("span", ge, [(_(!0), l(a, null, y(L.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, ["color", "name"]))), 128))])) : F.value[0].color ? (g(), o(r, {
207
+ }, null, 8, [
208
+ "color",
209
+ "icon",
210
+ "icon-color",
211
+ "name"
212
+ ]))), 128))])) : L.value[0].color ? (_(), s(i, {
202
213
  key: 3,
203
- color: F.value[0].color,
204
- name: F.value[0].label
205
- }, null, 8, ["color", "name"])) : (g(), c("span", _e, b(F.value[0].label), 1)) : (g(), c("span", he, b(f.placeholder), 1)),
206
- a[3] ||= l("svg", {
214
+ color: L.value[0].color,
215
+ icon: L.value[0].icon,
216
+ "icon-color": L.value[0].iconColor,
217
+ name: L.value[0].label
218
+ }, null, 8, [
219
+ "color",
220
+ "icon",
221
+ "icon-color",
222
+ "name"
223
+ ])) : L.value[0].icon ? (_(), l("span", _e, [f(e, {
224
+ class: "shrink-0",
225
+ color: L.value[0].iconColor ?? "ink-subtle",
226
+ name: L.value[0].icon
227
+ }, null, 8, ["color", "name"]), u("span", ve, x(L.value[0].label), 1)])) : (_(), l("span", ye, x(L.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
- }, [l("path", { d: "M4.5 6.5 8 10l3.5-3.5" })], -1)
235
+ }, [u("path", { d: "M4.5 6.5 8 10l3.5-3.5" })], -1)
214
236
  ], 16, pe)])]),
215
237
  _: 3
216
- }, 512), d(x(de), null, {
217
- default: S(() => [d(x(ue), {
218
- align: f.align,
219
- "aria-label": O.value ?? f.placeholder,
220
- class: h(["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", I.value ? "min-w-[max(var(--reka-popover-trigger-width),15rem)]" : "min-w-(--reka-popover-trigger-width)"]),
221
- side: f.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", R.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(f.panelWidth ? { width: f.panelWidth } : void 0),
224
- onCloseAutoFocus: Pe,
225
- onOpenAutoFocus: Me,
226
- onPointerDownOutside: Ne
245
+ style: te(p.panelWidth ? { width: p.panelWidth } : void 0),
246
+ onCloseAutoFocus: Re,
247
+ onOpenAutoFocus: Ie,
248
+ onPointerDownOutside: Le
227
249
  }, {
228
- default: S(() => [d(x(se), {
250
+ default: C(() => [f(S(se), {
229
251
  ref_key: "listbox",
230
252
  ref: $,
231
253
  "highlight-on-hover": !0,
232
- "model-value": f.multiple ? N.value : N.value[0] ?? void 0,
233
- multiple: f.multiple,
234
- onHighlight: Fe,
235
- onKeydown: Ae,
236
- onPointermove: a[1] ||= (e) => M.value = !1
254
+ "model-value": p.multiple ? F.value : F.value[0] ?? void 0,
255
+ multiple: p.multiple,
256
+ onHighlight: ze,
257
+ onKeydown: Pe,
258
+ onPointermoveCapture: o[1] ||= (e) => (N.value = !1, P.value = !0)
237
259
  }, {
238
- default: S(() => [I.value ? (g(), c("div", ve, [L.value ? (g(!0), c(i, { key: 0 }, v(F.value, (e) => (g(), o(r, {
260
+ default: C(() => [R.value ? (_(), l("div", be, [z.value ? (_(!0), l(a, { key: 0 }, y(L.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": f.removeLabel,
244
- onRemove: (t) => Z(e.value)
267
+ "remove-label": p.removeLabel,
268
+ onRemove: (t) => Ne(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)) : s("", !0), d(x(oe), {
251
- "aria-label": O.value ?? f.placeholder,
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": j.value,
255
- placeholder: L.value && F.value.length ? "" : f.searchPlaceholder,
256
- "onUpdate:modelValue": W
280
+ "model-value": M.value,
281
+ placeholder: z.value && L.value.length ? "" : p.searchPlaceholder,
282
+ "onUpdate:modelValue": K
257
283
  }, null, 8, [
258
284
  "aria-label",
259
285
  "model-value",
260
286
  "placeholder"
261
- ])])) : s("", !0), f.loading ? (g(), c("div", ye, [d(n, { size: "sm" })])) : R.value.length || z.value ? (g(), o(x(ae), {
287
+ ])])) : c("", !0), p.loading ? (_(), l("div", xe, [f(r, { size: "sm" })])) : B.value.length || V.value ? (_(), s(S(ae), {
262
288
  key: 2,
263
- "aria-label": O.value ?? f.placeholder,
289
+ "aria-label": k.value ?? p.placeholder,
264
290
  class: "group/list max-h-60 overflow-auto p-1 outline-hidden",
265
- "data-keyboard": M.value || void 0
291
+ "data-keyboard": N.value || void 0
266
292
  }, {
267
- default: S(() => [De.value ? (g(), o(x(ce), {
293
+ default: C(() => [ke.value ? (_(), s(S(ce), {
268
294
  key: 0,
269
295
  "estimate-size": 32,
270
- options: R.value,
296
+ options: B.value,
271
297
  "text-content": (e) => e.label
272
298
  }, {
273
- default: S(({ option: n }) => [d(x(C), {
274
- class: h(w),
275
- disabled: n.disabled,
276
- value: n.value,
277
- onSelect: (e) => X(e, n)
299
+ default: C(({ option: r }) => [f(S(w), {
300
+ class: g(T),
301
+ disabled: r.disabled,
302
+ value: r.value,
303
+ onSelect: (e) => Z(e, r)
278
304
  }, {
279
- default: S(() => [l("span", be, [y(e.$slots, "option", { option: n }, () => [n.color ? (g(), o(r, {
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
- color: n.color,
282
- name: n.label
283
- }, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]), N.value.includes(n.value) ? (g(), o(t, { key: 0 })) : s("", !0)]),
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))])]), F.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"])) : (g(!0), c(i, { key: 1 }, v(R.value, (n) => (g(), o(x(C), {
292
- key: n.value,
293
- class: h(w),
294
- disabled: n.disabled,
295
- value: n.value,
296
- onSelect: (e) => X(e, n)
329
+ }, 8, ["options", "text-content"])) : (_(!0), l(a, { key: 1 }, y(B.value, (r) => (_(), s(S(w), {
330
+ key: r.value,
331
+ class: g(T),
332
+ disabled: r.disabled,
333
+ value: r.value,
334
+ onSelect: (e) => Z(e, r)
297
335
  }, {
298
- default: S(() => [l("span", xe, [y(e.$slots, "option", { option: n }, () => [n.color ? (g(), o(r, {
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
- color: n.color,
301
- name: n.label
302
- }, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]), N.value.includes(n.value) ? (g(), o(t, { key: 0 })) : s("", !0)]),
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))])]), F.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)), z.value ? (g(), o(x(C), {
358
+ ]))), 128)), V.value ? (_(), s(S(w), {
309
359
  key: 2,
310
- class: h(w),
311
- value: H,
312
- onSelect: a[0] ||= (e) => X(e, H)
360
+ class: g(T),
361
+ value: W,
362
+ onSelect: o[0] ||= (e) => Z(e, W)
313
363
  }, {
314
- default: S(() => [l("span", Se, [u(b(f.createLabel) + " ", 1), f.createColored ? (g(), o(r, {
364
+ default: C(() => [u("span", we, [d(x(p.createLabel) + " ", 1), p.createColored ? (_(), s(i, {
315
365
  key: 0,
316
- color: V.value,
317
- name: j.value.trim()
318
- }, null, 8, ["color", "name"])) : (g(), c("strong", Ce, "“" + b(j.value.trim()) + "”", 1))])]),
366
+ color: U.value,
367
+ name: M.value.trim()
368
+ }, null, 8, ["color", "name"])) : (_(), l("strong", Te, "“" + x(M.value.trim()) + "”", 1))])]),
319
369
  _: 1
320
- })) : s("", !0)]),
370
+ })) : c("", !0)]),
321
371
  _: 3
322
- }, 8, ["aria-label", "data-keyboard"])) : (g(), c("div", we, b(f.emptyLabel), 1))]),
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 { T as default };
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","pointer","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)\nconst pointer = 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 pointer.value = false\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, the keyboard or the pointer should.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || pointer.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && !pointer.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.capture=\"((keyboard = false), (pointer = true))\"\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: {}, __VLS_3: {}, __VLS_5: {};
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 __VLS_3) => any;
36
+ default?: (props: typeof __VLS_8) => any;
32
37
  } & {
33
- default?: (props: typeof __VLS_5) => any;
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 "../internal/CloseSmallIcon.js";
2
- import { createCommentVNode as t, createElementBlock as n, createElementVNode as r, createTextVNode as i, createVNode as a, defineComponent as o, mergeModels as s, normalizeClass as c, openBlock as l, renderSlot as u, toDisplayString as d, useModel as f } from "vue";
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 p = ["aria-pressed", "disabled"], m = {
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
- }, h = {
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
- }, g = { class: "truncate" }, _ = ["aria-label"], v = /*@__PURE__*/ o({
16
+ }, v = { class: "truncate" }, y = ["aria-label"], b = /*@__PURE__*/ c({
16
17
  __name: "Tag",
17
- props: /*@__PURE__*/ s({
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__*/ s(["remove"], ["update:pressed"]),
35
- setup(o) {
36
- let s = f(o, "pressed"), v = {
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 (f, y) => o.pressable ? (l(), n("button", {
50
+ return (m, x) => c.pressable ? (d(), i("button", {
48
51
  key: 0,
49
- "aria-pressed": s.value,
50
- class: c(["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", [o.size === "sm" ? "h-5.5 gap-1 px-2 text-xs" : "h-7 gap-1.5 px-3 text-13", s.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"]]),
51
- disabled: o.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: y[0] ||= (e) => s.value = !s.value
54
- }, [s.value ? (l(), n("svg", m, [...y[2] ||= [r("path", { d: "M3.5 8.25l3 3 6-6.5" }, null, -1)]])) : t("", !0), u(f.$slots, "default", {}, () => [i(d(o.name), 1)])], 10, p)) : o.variant === "outline" ? (l(), n("span", {
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: c(["inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-xs whitespace-nowrap", o.muted ? "border-sui-line-subtle text-sui-ink-muted" : "border-sui-line bg-sui-sunken font-medium text-sui-ink"])
57
- }, [u(f.$slots, "default", {}, () => [i(d(o.name), 1)])], 2)) : (l(), n("div", h, [
58
- o.color ? (l(), n("span", {
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: c(["h-1.5 w-1.5 shrink-0 rounded-full", v[o.color]])
61
- }, null, 2)) : t("", !0),
62
- r("span", g, [u(f.$slots, "default", {}, () => [i(d(o.name), 1)])]),
63
- o.removable ? (l(), n("button", {
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
- "aria-label": o.removeLabel,
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: y[1] ||= (e) => f.$emit("remove")
69
- }, [a(e, { class: "fill-sui-ink-muted stroke-sui-ink-muted" })], 8, _)) : t("", !0)
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 { v as default };
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCA,IAAMqB,IAAUpB,EAAoBsB,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,aADRrB,EAAA,GAAAC,EA0BS,UAAA;;GAxBN,gBAAckB,EAAA;GACf,OAAKjB,EAAA,CAAC,yNAAuN,CAC7MmB,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,SADRnB,EAAA,GAAAC,EAUM,OAVNW,GAUM,CAAA,GAAA,AAAAc,EAAA,OAAA,CADJvB,EAAgC,QAAA,EAA1B,GAAE,sBAAqB,GAAA,MAAA,EAAA,C,kBAE/BE,EAAuByB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAdxB,EAAAC,EAAAc,EAAA,IAAI,GAAA,CAAA,C,eAGFA,EAAA,YAAO,aADpBrB,EAAA,GAAAC,EAQO,QAAA;;GANL,OAAKC,EAAA,CAAC,0FACSmB,EAAA,QAAK,8CAAA,wD;E,IAIpBhB,EAAuByB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAdxB,EAAAC,EAAAc,EAAA,IAAI,GAAA,CAAA,C,YAEfrB,EAAA,GAAAC,EAiBM,OAjBNY,GAiBM;GAbQQ,EAAA,SAAZrB,EAAA,GAAAC,EAAoF,QAAA;;IAAjE,OAAKC,EAAA,CAAC,qCAA4CoB,EAAKD,EAAA,MAAK,CAAA;;GAC/ElB,EAEC,QAFDW,GAEC,CADET,EAAuByB,EAAA,QAAA,WAAA,CAAA,SAAA,CAAdxB,EAAAC,EAAAc,EAAA,IAAI,GAAA,CAAA,C;GAGRA,EAAA,aADRrB,EAAA,GAAAC,EAQS,UAAA;;IANN,cAAYoB,EAAA;IACb,OAAM;IACN,MAAK;IACJ,SAAK,AAAAK,EAAA,QAAA,MAAE5B,EAAAA,MAAK,QAAA;G,IAEbU,EAAkEC,GAAA,EAAlD,OAAM,0CAAyC,CAAA,C"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparklingopinions/sparkling-ui",
3
- "version": "0.8.0",
3
+ "version": "0.9.2",
4
4
  "description": "A product-agnostic Vue 3 component library, themed by semantic tokens",
5
5
  "type": "module",
6
6
  "license": "MIT",