@treeui/vue 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/components/TAccordion.cjs.map +1 -1
  2. package/dist/components/TAccordion.js.map +1 -1
  3. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs +1 -1
  4. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  5. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js +3 -3
  6. package/dist/components/TAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/components/TAccordionItem.cjs.map +1 -1
  8. package/dist/components/TAccordionItem.js.map +1 -1
  9. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs +1 -1
  10. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  11. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/components/TAccordionItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/TDropdown.cjs.map +1 -1
  14. package/dist/components/TDropdown.js.map +1 -1
  15. package/dist/components/TDropdown.vue.d.ts +4 -0
  16. package/dist/components/TDropdown.vue.d.ts.map +1 -1
  17. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs +1 -1
  18. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  19. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js +22 -18
  20. package/dist/components/TDropdown.vue_vue_type_script_setup_true_lang.js.map +1 -1
  21. package/dist/components/TFlag.cjs +2 -0
  22. package/dist/components/TFlag.cjs.map +1 -0
  23. package/dist/components/TFlag.js +7 -0
  24. package/dist/components/TFlag.js.map +1 -0
  25. package/dist/components/TFlag.vue.d.ts +20 -0
  26. package/dist/components/TFlag.vue.d.ts.map +1 -0
  27. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs +2 -0
  28. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  29. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js +58 -0
  30. package/dist/components/TFlag.vue_vue_type_script_setup_true_lang.js.map +1 -0
  31. package/dist/components/TIcon.vue.d.ts +1 -1
  32. package/dist/components/TLanguageSelect.cjs +2 -0
  33. package/dist/components/TLanguageSelect.cjs.map +1 -0
  34. package/dist/components/TLanguageSelect.js +7 -0
  35. package/dist/components/TLanguageSelect.js.map +1 -0
  36. package/dist/components/TLanguageSelect.vue.d.ts +103 -0
  37. package/dist/components/TLanguageSelect.vue.d.ts.map +1 -0
  38. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs +2 -0
  39. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs.map +1 -0
  40. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js +299 -0
  41. package/dist/components/TLanguageSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
  42. package/dist/components/TPopover.cjs.map +1 -1
  43. package/dist/components/TPopover.js.map +1 -1
  44. package/dist/components/TPopover.vue.d.ts +4 -2
  45. package/dist/components/TPopover.vue.d.ts.map +1 -1
  46. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs +1 -1
  47. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.cjs.map +1 -1
  48. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js +10 -5
  49. package/dist/components/TPopover.vue_vue_type_script_setup_true_lang.js.map +1 -1
  50. package/dist/components/accordion.cjs.map +1 -1
  51. package/dist/components/accordion.d.ts +4 -4
  52. package/dist/components/accordion.d.ts.map +1 -1
  53. package/dist/components/accordion.js.map +1 -1
  54. package/dist/components/index.d.ts +4 -1
  55. package/dist/components/index.d.ts.map +1 -1
  56. package/dist/index.cjs +1 -1
  57. package/dist/index.js +80 -78
  58. package/dist/plugin.cjs +1 -1
  59. package/dist/plugin.cjs.map +1 -1
  60. package/dist/plugin.d.ts.map +1 -1
  61. package/dist/plugin.js +146 -142
  62. package/dist/plugin.js.map +1 -1
  63. package/dist/style.css +1 -1
  64. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { useControllableOpen as e } from "../composables/useControllableOpen.js";
2
- import { Fragment as t, Transition as n, computed as r, createCommentVNode as ee, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, defineComponent as c, mergeProps as l, nextTick as u, normalizeClass as d, normalizeStyle as f, onBeforeUnmount as p, openBlock as m, ref as h, renderList as g, renderSlot as _, toDisplayString as v, toRef as y, unref as b, useAttrs as x, watch as S, withCtx as C } from "vue";
3
- import { createId as w, focusFirst as te, isActivationKey as T, isEscapeKey as E } from "@treeui/utils";
2
+ import { Fragment as t, Transition as n, computed as r, createCommentVNode as i, createElementBlock as a, createElementVNode as o, createTextVNode as s, createVNode as c, defineComponent as l, mergeProps as ee, nextTick as u, normalizeClass as d, normalizeStyle as f, onBeforeUnmount as p, openBlock as m, ref as h, renderList as g, renderSlot as _, toDisplayString as v, toRef as y, unref as b, useAttrs as te, watch as x, withCtx as S } from "vue";
3
+ import { createId as C, focusFirst as w, isActivationKey as T, isEscapeKey as E } from "@treeui/utils";
4
4
  //#region src/components/TDropdown.vue?vue&type=script&setup=true&lang.ts
5
5
  var D = ["data-state"], O = [
6
6
  "disabled",
@@ -11,7 +11,7 @@ var D = ["data-state"], O = [
11
11
  "tabindex",
12
12
  "onClick",
13
13
  "onKeydown"
14
- ], M = /*@__PURE__*/ c({
14
+ ], M = /*@__PURE__*/ l({
15
15
  inheritAttrs: !1,
16
16
  __name: "TDropdown",
17
17
  props: {
@@ -30,15 +30,16 @@ var D = ["data-state"], O = [
30
30
  },
31
31
  size: { default: "md" },
32
32
  label: { default: "" },
33
- align: { default: "start" }
33
+ align: { default: "start" },
34
+ id: { default: void 0 }
34
35
  },
35
36
  emits: [
36
37
  "select",
37
38
  "update:open",
38
39
  "open-change"
39
40
  ],
40
- setup(c, { emit: M }) {
41
- let N = c, P = M, F = x(), I = w("t-dropdown"), L = h(null), R = h(null), z = h(/* @__PURE__ */ new Map()), B = h(-1), V = r(() => {
41
+ setup(l, { emit: M }) {
42
+ let N = l, P = M, F = te(), I = N.id ?? C("t-dropdown"), L = h(null), R = h(null), z = h(/* @__PURE__ */ new Map()), B = h(-1), V = r(() => {
42
43
  let { class: e, style: t, ...n } = F;
43
44
  return n;
44
45
  }), { value: H, setValue: U } = e(y(N, "open"), N.defaultOpen, (e) => {
@@ -56,7 +57,7 @@ var D = ["data-state"], O = [
56
57
  N.disabled || (B.value = K.value.length > 0 ? N.items.indexOf(K.value[0]) : 0, U(!0), u(() => Z(B.value)));
57
58
  }, J = (e = !1) => {
58
59
  U(!1), B.value = -1, e && u(() => {
59
- R.value && te(R.value);
60
+ R.value && w(R.value);
60
61
  });
61
62
  }, Y = () => {
62
63
  N.disabled || (H.value ? J() : q());
@@ -113,30 +114,33 @@ var D = ["data-state"], O = [
113
114
  let t = e.target;
114
115
  t instanceof Node && (L.value?.contains(t) || J());
115
116
  };
116
- return S(H, (e) => {
117
+ return x(H, (e) => {
117
118
  e ? document.addEventListener("pointerdown", $) : document.removeEventListener("pointerdown", $);
118
119
  }), p(() => {
119
120
  document.removeEventListener("pointerdown", $);
120
- }), (e, r) => (m(), i("div", {
121
+ }), (e, r) => (m(), a("div", {
121
122
  ref_key: "rootRef",
122
123
  ref: L,
123
124
  class: d(W.value),
124
125
  style: f(G.value),
125
126
  "data-state": b(H) ? "open" : "closed"
126
- }, [a("span", {
127
+ }, [o("span", {
127
128
  ref_key: "triggerRef",
128
129
  ref: R,
129
130
  class: "t-dropdown__trigger-wrapper",
130
131
  onClick: Y,
131
132
  onKeydown: re
132
- }, [_(e.$slots, "trigger", { isOpen: b(H) }, () => [a("button", l(V.value, {
133
+ }, [_(e.$slots, "trigger", {
134
+ isOpen: b(H),
135
+ menuId: b(I)
136
+ }, () => [o("button", ee(V.value, {
133
137
  type: "button",
134
138
  class: "t-dropdown__trigger",
135
- disabled: c.disabled,
139
+ disabled: l.disabled,
136
140
  "aria-controls": b(H) ? b(I) : void 0,
137
141
  "aria-haspopup": "menu",
138
142
  "aria-expanded": b(H)
139
- }), [a("span", k, v(c.label), 1), r[0] ||= a("svg", {
143
+ }), [o("span", k, v(l.label), 1), r[0] ||= o("svg", {
140
144
  class: "t-dropdown__chevron",
141
145
  viewBox: "0 0 16 16",
142
146
  fill: "none",
@@ -145,14 +149,14 @@ var D = ["data-state"], O = [
145
149
  "stroke-linecap": "round",
146
150
  "stroke-linejoin": "round",
147
151
  "aria-hidden": "true"
148
- }, [a("polyline", { points: "4 6 8 10 12 6" })], -1)], 16, O)])], 544), s(n, { name: "t-fade" }, {
149
- default: C(() => [b(H) && !c.disabled ? (m(), i("ul", {
152
+ }, [o("polyline", { points: "4 6 8 10 12 6" })], -1)], 16, O)])], 544), c(n, { name: "t-fade" }, {
153
+ default: S(() => [b(H) && !l.disabled ? (m(), a("ul", {
150
154
  key: 0,
151
155
  id: b(I),
152
156
  role: "menu",
153
157
  class: "t-dropdown__menu",
154
- "aria-label": c.label || void 0
155
- }, [(m(!0), i(t, null, g(c.items, (t, n) => (m(), i("li", {
158
+ "aria-label": l.label || void 0
159
+ }, [(m(!0), a(t, null, g(l.items, (t, n) => (m(), a("li", {
156
160
  key: t.value,
157
161
  ref_for: !0,
158
162
  ref: (e) => ne(e, t.value),
@@ -168,7 +172,7 @@ var D = ["data-state"], O = [
168
172
  }, [_(e.$slots, "item", {
169
173
  item: t,
170
174
  index: n
171
- }, () => [o(v(t.label), 1)])], 42, j))), 128))], 8, A)) : ee("", !0)]),
175
+ }, () => [s(v(t.label), 1)])], 42, j))), 128))], 8, A)) : i("", !0)]),
172
176
  _: 3
173
177
  })], 14, D));
174
178
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TDropdown.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/TDropdown.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, focusFirst, isActivationKey, isEscapeKey } from '@treeui/utils';\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TDropdownItem {\n label: string;\n value: string;\n disabled?: boolean;\n}\n\nexport type TDropdownAlign = 'start' | 'end';\n\nconst props = withDefaults(\n defineProps<{\n items?: TDropdownItem[];\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n size?: TSize;\n label?: string;\n align?: TDropdownAlign;\n }>(),\n {\n items: () => [],\n open: undefined,\n defaultOpen: false,\n disabled: false,\n size: 'md',\n label: '',\n align: 'start',\n },\n);\n\nconst emit = defineEmits<{\n select: [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean }): unknown;\n item(props: { item: TDropdownItem; index: number }): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst menuId = createId('t-dropdown');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLElement | null>(null);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst focusedIndex = ref(-1);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-dropdown',\n `t-dropdown--${props.size}`,\n {\n 't-dropdown--align-end': props.align === 'end',\n 'is-disabled': props.disabled,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst enabledItems = computed(() => props.items.filter((i) => !i.disabled));\n\nconst openMenu = () => {\n if (props.disabled) return;\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[0])\n : 0;\n setValue(true);\n nextTick(() => focusItem(focusedIndex.value));\n};\n\nconst closeMenu = (restoreFocus = false) => {\n setValue(false);\n focusedIndex.value = -1;\n if (restoreFocus) {\n nextTick(() => {\n if (triggerRef.value) focusFirst(triggerRef.value);\n });\n }\n};\n\nconst toggleMenu = () => {\n if (props.disabled) return;\n if (isOpen.value) {\n closeMenu();\n } else {\n openMenu();\n }\n};\n\nconst selectItem = (item: TDropdownItem) => {\n if (item.disabled) return;\n emit('select', item.value);\n closeMenu(true);\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n if (!item) return;\n const el = itemRefs.value.get(item.value);\n el?.focus();\n};\n\nconst setItemRef = (el: Element | null, value: string) => {\n if (el instanceof HTMLElement) {\n itemRefs.value.set(value, el);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst moveFocus = (direction: 1 | -1) => {\n let nextIndex = focusedIndex.value + direction;\n while (nextIndex >= 0 && nextIndex < props.items.length) {\n if (!props.items[nextIndex].disabled) {\n focusedIndex.value = nextIndex;\n focusItem(nextIndex);\n return;\n }\n nextIndex += direction;\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeMenu();\n return;\n }\n\n if (isActivationKey(event) || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n openMenu();\n }\n};\n\nconst onItemKeydown = (event: KeyboardEvent, item: TDropdownItem) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeMenu(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(item);\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(-1);\n break;\n case 'Home':\n event.preventDefault();\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[0])\n : 0;\n focusItem(focusedIndex.value);\n break;\n case 'End':\n event.preventDefault();\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[enabledItems.value.length - 1])\n : props.items.length - 1;\n focusItem(focusedIndex.value);\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closeMenu();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <span\n ref=\"triggerRef\"\n class=\"t-dropdown__trigger-wrapper\"\n @click=\"toggleMenu\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n >\n <button\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-dropdown__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? menuId : undefined\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n >\n <span class=\"t-dropdown__label\">{{ label }}</span>\n <svg\n class=\"t-dropdown__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"4 6 8 10 12 6\" />\n </svg>\n </button>\n </slot>\n </span>\n <transition name=\"t-fade\">\n <ul\n v-if=\"isOpen && !disabled\"\n :id=\"menuId\"\n role=\"menu\"\n class=\"t-dropdown__menu\"\n :aria-label=\"label || undefined\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n :ref=\"(el) => setItemRef(el as Element | null, item.value)\"\n role=\"menuitem\"\n class=\"t-dropdown__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-focused': index === focusedIndex,\n }\"\n :aria-disabled=\"item.disabled || undefined\"\n :tabindex=\"item.disabled ? -1 : 0\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, item)\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n >\n {{ item.label }}\n </slot>\n </li>\n </ul>\n </transition>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"TDropdown.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/TDropdown.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, focusFirst, isActivationKey, isEscapeKey } from '@treeui/utils';\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TSize } from '../types/contracts';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\nexport interface TDropdownItem {\n label: string;\n value: string;\n disabled?: boolean;\n}\n\nexport type TDropdownAlign = 'start' | 'end';\n\nconst props = withDefaults(\n defineProps<{\n items?: TDropdownItem[];\n open?: boolean;\n defaultOpen?: boolean;\n disabled?: boolean;\n size?: TSize;\n label?: string;\n align?: TDropdownAlign;\n id?: string;\n }>(),\n {\n items: () => [],\n open: undefined,\n defaultOpen: false,\n disabled: false,\n size: 'md',\n label: '',\n align: 'start',\n id: undefined,\n },\n);\n\nconst emit = defineEmits<{\n select: [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n trigger(props: { isOpen: boolean; menuId: string }): unknown;\n item(props: { item: TDropdownItem; index: number }): unknown;\n}>();\n\nconst attrs = useAttrs();\nconst menuId = props.id ?? createId('t-dropdown');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLElement | null>(null);\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map());\nconst focusedIndex = ref(-1);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst rootClasses = computed(() => [\n 't-dropdown',\n `t-dropdown--${props.size}`,\n {\n 't-dropdown--align-end': props.align === 'end',\n 'is-disabled': props.disabled,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\nconst rootStyle = computed(() => attrs.style);\n\nconst enabledItems = computed(() => props.items.filter((i) => !i.disabled));\n\nconst openMenu = () => {\n if (props.disabled) return;\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[0])\n : 0;\n setValue(true);\n nextTick(() => focusItem(focusedIndex.value));\n};\n\nconst closeMenu = (restoreFocus = false) => {\n setValue(false);\n focusedIndex.value = -1;\n if (restoreFocus) {\n nextTick(() => {\n if (triggerRef.value) focusFirst(triggerRef.value);\n });\n }\n};\n\nconst toggleMenu = () => {\n if (props.disabled) return;\n if (isOpen.value) {\n closeMenu();\n } else {\n openMenu();\n }\n};\n\nconst selectItem = (item: TDropdownItem) => {\n if (item.disabled) return;\n emit('select', item.value);\n closeMenu(true);\n};\n\nconst focusItem = (index: number) => {\n const item = props.items[index];\n if (!item) return;\n const el = itemRefs.value.get(item.value);\n el?.focus();\n};\n\nconst setItemRef = (el: Element | null, value: string) => {\n if (el instanceof HTMLElement) {\n itemRefs.value.set(value, el);\n } else {\n itemRefs.value.delete(value);\n }\n};\n\nconst moveFocus = (direction: 1 | -1) => {\n let nextIndex = focusedIndex.value + direction;\n while (nextIndex >= 0 && nextIndex < props.items.length) {\n if (!props.items[nextIndex].disabled) {\n focusedIndex.value = nextIndex;\n focusItem(nextIndex);\n return;\n }\n nextIndex += direction;\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeMenu();\n return;\n }\n\n if (isActivationKey(event) || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n openMenu();\n }\n};\n\nconst onItemKeydown = (event: KeyboardEvent, item: TDropdownItem) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeMenu(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectItem(item);\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(-1);\n break;\n case 'Home':\n event.preventDefault();\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[0])\n : 0;\n focusItem(focusedIndex.value);\n break;\n case 'End':\n event.preventDefault();\n focusedIndex.value = enabledItems.value.length > 0\n ? props.items.indexOf(enabledItems.value[enabledItems.value.length - 1])\n : props.items.length - 1;\n focusItem(focusedIndex.value);\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closeMenu();\n};\n\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n});\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <span\n ref=\"triggerRef\"\n class=\"t-dropdown__trigger-wrapper\"\n @click=\"toggleMenu\"\n @keydown=\"onTriggerKeydown\"\n >\n <slot\n name=\"trigger\"\n :is-open=\"isOpen\"\n :menu-id=\"menuId\"\n >\n <button\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-dropdown__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? menuId : undefined\"\n aria-haspopup=\"menu\"\n :aria-expanded=\"isOpen\"\n >\n <span class=\"t-dropdown__label\">{{ label }}</span>\n <svg\n class=\"t-dropdown__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"4 6 8 10 12 6\" />\n </svg>\n </button>\n </slot>\n </span>\n <transition name=\"t-fade\">\n <ul\n v-if=\"isOpen && !disabled\"\n :id=\"menuId\"\n role=\"menu\"\n class=\"t-dropdown__menu\"\n :aria-label=\"label || undefined\"\n >\n <li\n v-for=\"(item, index) in items\"\n :key=\"item.value\"\n :ref=\"(el) => setItemRef(el as Element | null, item.value)\"\n role=\"menuitem\"\n class=\"t-dropdown__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-focused': index === focusedIndex,\n }\"\n :aria-disabled=\"item.disabled || undefined\"\n :tabindex=\"item.disabled ? -1 : 0\"\n @click=\"selectItem(item)\"\n @keydown=\"onItemKeydown($event, item)\"\n >\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n >\n {{ item.label }}\n </slot>\n </li>\n </ul>\n </transition>\n </div>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var e=require("./TFlag.vue_vue_type_script_setup_true_lang.cjs").default;exports.default=e;
2
+ //# sourceMappingURL=TFlag.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TFlag.cjs","names":[],"sources":["../../src/components/TFlag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport type { TSize } from '../types/contracts';\n\n/**\n * Flags are requested from the CDN's fixed-height endpoints, which serve the\n * flat artwork at each flag's true proportions — Nepal stays a pennant and\n * Switzerland stays square. The CDN's 4:3 endpoints would normalise every ratio,\n * but only by switching to the waving artwork, which reads as decoration next to\n * flat UI icons. A common height is what actually aligns a column of flags; the\n * varying widths are absorbed by the fixed box in CSS.\n *\n * Each step's retina asset is at least twice its box height so 2x screens never\n * upscale.\n */\nconst FLAG_ASSETS: Record<TSize, { base: string; retina: string }> = {\n sm: { base: 'h20', retina: 'h40' },\n md: { base: 'h20', retina: 'h40' },\n lg: { base: 'h24', retina: 'h60' },\n};\n\nconst props = withDefaults(\n defineProps<{\n /** ISO 3166-1 alpha-2 country code. Case-insensitive. */\n code: string;\n /** Origin serving the flag assets. Point at a mirror to self-host. */\n baseUrl?: string;\n size?: TSize;\n /** Set to expose the flag to assistive tech; omit to keep it decorative. */\n label?: string;\n /** Text shown when the image cannot load. Defaults to the uppercased code. */\n fallbackText?: string;\n }>(),\n {\n baseUrl: 'https://flagcdn.com',\n size: 'md',\n label: undefined,\n fallbackText: undefined,\n },\n);\n\nconst classes = computed(() => [\n 't-flag',\n `t-flag--${props.size}`,\n]);\n\nconst normalizedCode = computed(() => props.code.trim().toLowerCase());\n\n// A JS consumer can hand us a size outside the union; fall back rather than\n// throwing on an undefined lookup.\nconst assets = computed(() => FLAG_ASSETS[props.size] ?? FLAG_ASSETS.md);\n\n// Tolerate a baseUrl written with a trailing slash so a self-hosted mirror does\n// not silently produce a double-slashed path.\nconst origin = computed(() => props.baseUrl.replace(/\\/+$/, ''));\n\nconst src = computed(\n () => `${origin.value}/${assets.value.base}/${normalizedCode.value}.png`,\n);\n\nconst srcset = computed(\n () => `${origin.value}/${assets.value.retina}/${normalizedCode.value}.png 2x`,\n);\n\nconst resolvedFallback = computed(\n () => props.fallbackText ?? normalizedCode.value.toUpperCase(),\n);\n\nconst failed = ref(false);\n\n// A new source deserves a fresh attempt — otherwise one bad code would leave the\n// component stuck on the fallback for every later flag.\nwatch(src, () => {\n failed.value = false;\n});\n\nconst onError = () => {\n failed.value = true;\n};\n</script>\n\n<template>\n <span\n :class=\"classes\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label || undefined\"\n :aria-hidden=\"label ? undefined : 'true'\"\n >\n <img\n v-if=\"!failed\"\n :src=\"src\"\n :srcset=\"srcset\"\n alt=\"\"\n class=\"t-flag__image\"\n @error=\"onError\"\n >\n <!--\n Flags come from a third-party origin, so the request can fail offline or\n behind a strict img-src policy. Showing the country code keeps the control\n legible instead of collapsing it to an empty box.\n -->\n <span\n v-else\n class=\"t-flag__fallback\"\n >\n {{ resolvedFallback }}\n </span>\n </span>\n</template>\n"],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import e from "./TFlag.vue_vue_type_script_setup_true_lang.js";
2
+ //#region src/components/TFlag.vue
3
+ var t = e;
4
+ //#endregion
5
+ export { t as default };
6
+
7
+ //# sourceMappingURL=TFlag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TFlag.js","names":[],"sources":["../../src/components/TFlag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport type { TSize } from '../types/contracts';\n\n/**\n * Flags are requested from the CDN's fixed-height endpoints, which serve the\n * flat artwork at each flag's true proportions — Nepal stays a pennant and\n * Switzerland stays square. The CDN's 4:3 endpoints would normalise every ratio,\n * but only by switching to the waving artwork, which reads as decoration next to\n * flat UI icons. A common height is what actually aligns a column of flags; the\n * varying widths are absorbed by the fixed box in CSS.\n *\n * Each step's retina asset is at least twice its box height so 2x screens never\n * upscale.\n */\nconst FLAG_ASSETS: Record<TSize, { base: string; retina: string }> = {\n sm: { base: 'h20', retina: 'h40' },\n md: { base: 'h20', retina: 'h40' },\n lg: { base: 'h24', retina: 'h60' },\n};\n\nconst props = withDefaults(\n defineProps<{\n /** ISO 3166-1 alpha-2 country code. Case-insensitive. */\n code: string;\n /** Origin serving the flag assets. Point at a mirror to self-host. */\n baseUrl?: string;\n size?: TSize;\n /** Set to expose the flag to assistive tech; omit to keep it decorative. */\n label?: string;\n /** Text shown when the image cannot load. Defaults to the uppercased code. */\n fallbackText?: string;\n }>(),\n {\n baseUrl: 'https://flagcdn.com',\n size: 'md',\n label: undefined,\n fallbackText: undefined,\n },\n);\n\nconst classes = computed(() => [\n 't-flag',\n `t-flag--${props.size}`,\n]);\n\nconst normalizedCode = computed(() => props.code.trim().toLowerCase());\n\n// A JS consumer can hand us a size outside the union; fall back rather than\n// throwing on an undefined lookup.\nconst assets = computed(() => FLAG_ASSETS[props.size] ?? FLAG_ASSETS.md);\n\n// Tolerate a baseUrl written with a trailing slash so a self-hosted mirror does\n// not silently produce a double-slashed path.\nconst origin = computed(() => props.baseUrl.replace(/\\/+$/, ''));\n\nconst src = computed(\n () => `${origin.value}/${assets.value.base}/${normalizedCode.value}.png`,\n);\n\nconst srcset = computed(\n () => `${origin.value}/${assets.value.retina}/${normalizedCode.value}.png 2x`,\n);\n\nconst resolvedFallback = computed(\n () => props.fallbackText ?? normalizedCode.value.toUpperCase(),\n);\n\nconst failed = ref(false);\n\n// A new source deserves a fresh attempt — otherwise one bad code would leave the\n// component stuck on the fallback for every later flag.\nwatch(src, () => {\n failed.value = false;\n});\n\nconst onError = () => {\n failed.value = true;\n};\n</script>\n\n<template>\n <span\n :class=\"classes\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label || undefined\"\n :aria-hidden=\"label ? undefined : 'true'\"\n >\n <img\n v-if=\"!failed\"\n :src=\"src\"\n :srcset=\"srcset\"\n alt=\"\"\n class=\"t-flag__image\"\n @error=\"onError\"\n >\n <!--\n Flags come from a third-party origin, so the request can fail offline or\n behind a strict img-src policy. Showing the country code keeps the control\n legible instead of collapsing it to an empty box.\n -->\n <span\n v-else\n class=\"t-flag__fallback\"\n >\n {{ resolvedFallback }}\n </span>\n </span>\n</template>\n"],"mappings":""}
@@ -0,0 +1,20 @@
1
+ import { TSize } from '../types/contracts';
2
+ type __VLS_Props = {
3
+ /** ISO 3166-1 alpha-2 country code. Case-insensitive. */
4
+ code: string;
5
+ /** Origin serving the flag assets. Point at a mirror to self-host. */
6
+ baseUrl?: string;
7
+ size?: TSize;
8
+ /** Set to expose the flag to assistive tech; omit to keep it decorative. */
9
+ label?: string;
10
+ /** Text shown when the image cannot load. Defaults to the uppercased code. */
11
+ fallbackText?: string;
12
+ };
13
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
14
+ size: TSize;
15
+ label: string;
16
+ baseUrl: string;
17
+ fallbackText: string;
18
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLSpanElement>;
19
+ export default _default;
20
+ //# sourceMappingURL=TFlag.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TFlag.vue.d.ts","sourceRoot":"","sources":["../../src/components/TFlag.vue"],"names":[],"mappings":"AAgHA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAmBhD,KAAK,WAAW,GAAG;IACf,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;;UALO,KAAK;WAEJ,MAAM;aAHJ,MAAM;kBAKD,MAAM;;AAuHzB,wBAQG"}
@@ -0,0 +1,2 @@
1
+ let e=require("vue");var t=[`role`,`aria-label`,`aria-hidden`],n=[`src`,`srcset`],r={key:1,class:`t-flag__fallback`},i=(0,e.defineComponent)({__name:`TFlag`,props:{code:{},baseUrl:{default:`https://flagcdn.com`},size:{default:`md`},label:{default:void 0},fallbackText:{default:void 0}},setup(i){let a={sm:{base:`h20`,retina:`h40`},md:{base:`h20`,retina:`h40`},lg:{base:`h24`,retina:`h60`}},o=i,s=(0,e.computed)(()=>[`t-flag`,`t-flag--${o.size}`]),c=(0,e.computed)(()=>o.code.trim().toLowerCase()),l=(0,e.computed)(()=>a[o.size]??a.md),u=(0,e.computed)(()=>o.baseUrl.replace(/\/+$/,``)),d=(0,e.computed)(()=>`${u.value}/${l.value.base}/${c.value}.png`),f=(0,e.computed)(()=>`${u.value}/${l.value.retina}/${c.value}.png 2x`),p=(0,e.computed)(()=>o.fallbackText??c.value.toUpperCase()),m=(0,e.ref)(!1);(0,e.watch)(d,()=>{m.value=!1});let h=()=>{m.value=!0};return(a,o)=>((0,e.openBlock)(),(0,e.createElementBlock)(`span`,{class:(0,e.normalizeClass)(s.value),role:i.label?`img`:void 0,"aria-label":i.label||void 0,"aria-hidden":i.label?void 0:`true`},[m.value?((0,e.openBlock)(),(0,e.createElementBlock)(`span`,r,(0,e.toDisplayString)(p.value),1)):((0,e.openBlock)(),(0,e.createElementBlock)(`img`,{key:0,src:d.value,srcset:f.value,alt:``,class:`t-flag__image`,onError:h},null,40,n))],10,t))}});exports.default=i;
2
+ //# sourceMappingURL=TFlag.vue_vue_type_script_setup_true_lang.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TFlag.vue_vue_type_script_setup_true_lang.cjs","names":[],"sources":["../../src/components/TFlag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport type { TSize } from '../types/contracts';\n\n/**\n * Flags are requested from the CDN's fixed-height endpoints, which serve the\n * flat artwork at each flag's true proportions — Nepal stays a pennant and\n * Switzerland stays square. The CDN's 4:3 endpoints would normalise every ratio,\n * but only by switching to the waving artwork, which reads as decoration next to\n * flat UI icons. A common height is what actually aligns a column of flags; the\n * varying widths are absorbed by the fixed box in CSS.\n *\n * Each step's retina asset is at least twice its box height so 2x screens never\n * upscale.\n */\nconst FLAG_ASSETS: Record<TSize, { base: string; retina: string }> = {\n sm: { base: 'h20', retina: 'h40' },\n md: { base: 'h20', retina: 'h40' },\n lg: { base: 'h24', retina: 'h60' },\n};\n\nconst props = withDefaults(\n defineProps<{\n /** ISO 3166-1 alpha-2 country code. Case-insensitive. */\n code: string;\n /** Origin serving the flag assets. Point at a mirror to self-host. */\n baseUrl?: string;\n size?: TSize;\n /** Set to expose the flag to assistive tech; omit to keep it decorative. */\n label?: string;\n /** Text shown when the image cannot load. Defaults to the uppercased code. */\n fallbackText?: string;\n }>(),\n {\n baseUrl: 'https://flagcdn.com',\n size: 'md',\n label: undefined,\n fallbackText: undefined,\n },\n);\n\nconst classes = computed(() => [\n 't-flag',\n `t-flag--${props.size}`,\n]);\n\nconst normalizedCode = computed(() => props.code.trim().toLowerCase());\n\n// A JS consumer can hand us a size outside the union; fall back rather than\n// throwing on an undefined lookup.\nconst assets = computed(() => FLAG_ASSETS[props.size] ?? FLAG_ASSETS.md);\n\n// Tolerate a baseUrl written with a trailing slash so a self-hosted mirror does\n// not silently produce a double-slashed path.\nconst origin = computed(() => props.baseUrl.replace(/\\/+$/, ''));\n\nconst src = computed(\n () => `${origin.value}/${assets.value.base}/${normalizedCode.value}.png`,\n);\n\nconst srcset = computed(\n () => `${origin.value}/${assets.value.retina}/${normalizedCode.value}.png 2x`,\n);\n\nconst resolvedFallback = computed(\n () => props.fallbackText ?? normalizedCode.value.toUpperCase(),\n);\n\nconst failed = ref(false);\n\n// A new source deserves a fresh attempt — otherwise one bad code would leave the\n// component stuck on the fallback for every later flag.\nwatch(src, () => {\n failed.value = false;\n});\n\nconst onError = () => {\n failed.value = true;\n};\n</script>\n\n<template>\n <span\n :class=\"classes\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label || undefined\"\n :aria-hidden=\"label ? undefined : 'true'\"\n >\n <img\n v-if=\"!failed\"\n :src=\"src\"\n :srcset=\"srcset\"\n alt=\"\"\n class=\"t-flag__image\"\n @error=\"onError\"\n >\n <!--\n Flags come from a third-party origin, so the request can fail offline or\n behind a strict img-src policy. Showing the country code keeps the control\n legible instead of collapsing it to an empty box.\n -->\n <span\n v-else\n class=\"t-flag__fallback\"\n >\n {{ resolvedFallback }}\n </span>\n </span>\n</template>\n"],"mappings":"uSAeA,IAAM,EAA+D,CACnE,GAAI,CAAE,KAAM,MAAO,OAAQ,KAAM,EACjC,GAAI,CAAE,KAAM,MAAO,OAAQ,KAAM,EACjC,GAAI,CAAE,KAAM,MAAO,OAAQ,KAAM,CACnC,EAEM,EAAQ,EAoBR,GAAA,EAAA,EAAA,SAAA,KAAyB,CAC7B,SACA,WAAW,EAAM,MACnB,CAAC,EAEK,GAAA,EAAA,EAAA,SAAA,KAAgC,EAAM,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC,EAI/D,GAAA,EAAA,EAAA,SAAA,KAAwB,EAAY,EAAM,OAAS,EAAY,EAAE,EAIjE,GAAA,EAAA,EAAA,SAAA,KAAwB,EAAM,QAAQ,QAAQ,OAAQ,EAAE,CAAC,EAEzD,GAAA,EAAA,EAAA,SAAA,KACE,GAAG,EAAO,MAAM,GAAG,EAAO,MAAM,KAAK,GAAG,EAAe,MAAM,KACrE,EAEM,GAAA,EAAA,EAAA,SAAA,KACE,GAAG,EAAO,MAAM,GAAG,EAAO,MAAM,OAAO,GAAG,EAAe,MAAM,QACvE,EAEM,GAAA,EAAA,EAAA,SAAA,KACE,EAAM,cAAgB,EAAe,MAAM,YAAY,CAC/D,EAEM,GAAA,EAAA,EAAA,IAAA,CAAa,EAAK,GAIxB,EAAA,EAAA,MAAA,CAAM,MAAW,CACf,EAAO,MAAQ,EACjB,CAAC,EAED,IAAM,MAAgB,CACpB,EAAO,MAAQ,EACjB,2DA6BS,OAAA,CAxBJ,OAAA,EAAA,EAAA,eAAA,CAAO,EAAA,KAAO,EACd,KAAM,EAAA,MAAK,MAAW,IAAA,GACtB,aAAY,EAAA,OAAS,IAAA,GACrB,cAAa,EAAA,MAAQ,IAAA,GAAS,SAGtB,EAAA,kDAiBF,OALP,GAAA,EAAA,EAAA,gBAAA,CAIK,EAAA,KAAgB,EAAA,CAAA,KAhBZ,EAAA,EAAA,UAAA,CAAA,GAAA,EAAA,EAAA,mBAAA,CAMR,MAAA,OALE,IAAK,EAAA,MACL,OAAQ,EAAA,MACT,IAAI,GACJ,MAAM,gBACE,sBAWW,EAAA,GAAA,CAAA"}
@@ -0,0 +1,58 @@
1
+ import { computed as e, createElementBlock as t, defineComponent as n, normalizeClass as r, openBlock as i, ref as a, toDisplayString as o, watch as s } from "vue";
2
+ //#region src/components/TFlag.vue?vue&type=script&setup=true&lang.ts
3
+ var c = [
4
+ "role",
5
+ "aria-label",
6
+ "aria-hidden"
7
+ ], l = ["src", "srcset"], u = {
8
+ key: 1,
9
+ class: "t-flag__fallback"
10
+ }, d = /*@__PURE__*/ n({
11
+ __name: "TFlag",
12
+ props: {
13
+ code: {},
14
+ baseUrl: { default: "https://flagcdn.com" },
15
+ size: { default: "md" },
16
+ label: { default: void 0 },
17
+ fallbackText: { default: void 0 }
18
+ },
19
+ setup(n) {
20
+ let d = {
21
+ sm: {
22
+ base: "h20",
23
+ retina: "h40"
24
+ },
25
+ md: {
26
+ base: "h20",
27
+ retina: "h40"
28
+ },
29
+ lg: {
30
+ base: "h24",
31
+ retina: "h60"
32
+ }
33
+ }, f = n, p = e(() => ["t-flag", `t-flag--${f.size}`]), m = e(() => f.code.trim().toLowerCase()), h = e(() => d[f.size] ?? d.md), g = e(() => f.baseUrl.replace(/\/+$/, "")), _ = e(() => `${g.value}/${h.value.base}/${m.value}.png`), v = e(() => `${g.value}/${h.value.retina}/${m.value}.png 2x`), y = e(() => f.fallbackText ?? m.value.toUpperCase()), b = a(!1);
34
+ s(_, () => {
35
+ b.value = !1;
36
+ });
37
+ let x = () => {
38
+ b.value = !0;
39
+ };
40
+ return (e, a) => (i(), t("span", {
41
+ class: r(p.value),
42
+ role: n.label ? "img" : void 0,
43
+ "aria-label": n.label || void 0,
44
+ "aria-hidden": n.label ? void 0 : "true"
45
+ }, [b.value ? (i(), t("span", u, o(y.value), 1)) : (i(), t("img", {
46
+ key: 0,
47
+ src: _.value,
48
+ srcset: v.value,
49
+ alt: "",
50
+ class: "t-flag__image",
51
+ onError: x
52
+ }, null, 40, l))], 10, c));
53
+ }
54
+ });
55
+ //#endregion
56
+ export { d as default };
57
+
58
+ //# sourceMappingURL=TFlag.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TFlag.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../src/components/TFlag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue';\nimport type { TSize } from '../types/contracts';\n\n/**\n * Flags are requested from the CDN's fixed-height endpoints, which serve the\n * flat artwork at each flag's true proportions — Nepal stays a pennant and\n * Switzerland stays square. The CDN's 4:3 endpoints would normalise every ratio,\n * but only by switching to the waving artwork, which reads as decoration next to\n * flat UI icons. A common height is what actually aligns a column of flags; the\n * varying widths are absorbed by the fixed box in CSS.\n *\n * Each step's retina asset is at least twice its box height so 2x screens never\n * upscale.\n */\nconst FLAG_ASSETS: Record<TSize, { base: string; retina: string }> = {\n sm: { base: 'h20', retina: 'h40' },\n md: { base: 'h20', retina: 'h40' },\n lg: { base: 'h24', retina: 'h60' },\n};\n\nconst props = withDefaults(\n defineProps<{\n /** ISO 3166-1 alpha-2 country code. Case-insensitive. */\n code: string;\n /** Origin serving the flag assets. Point at a mirror to self-host. */\n baseUrl?: string;\n size?: TSize;\n /** Set to expose the flag to assistive tech; omit to keep it decorative. */\n label?: string;\n /** Text shown when the image cannot load. Defaults to the uppercased code. */\n fallbackText?: string;\n }>(),\n {\n baseUrl: 'https://flagcdn.com',\n size: 'md',\n label: undefined,\n fallbackText: undefined,\n },\n);\n\nconst classes = computed(() => [\n 't-flag',\n `t-flag--${props.size}`,\n]);\n\nconst normalizedCode = computed(() => props.code.trim().toLowerCase());\n\n// A JS consumer can hand us a size outside the union; fall back rather than\n// throwing on an undefined lookup.\nconst assets = computed(() => FLAG_ASSETS[props.size] ?? FLAG_ASSETS.md);\n\n// Tolerate a baseUrl written with a trailing slash so a self-hosted mirror does\n// not silently produce a double-slashed path.\nconst origin = computed(() => props.baseUrl.replace(/\\/+$/, ''));\n\nconst src = computed(\n () => `${origin.value}/${assets.value.base}/${normalizedCode.value}.png`,\n);\n\nconst srcset = computed(\n () => `${origin.value}/${assets.value.retina}/${normalizedCode.value}.png 2x`,\n);\n\nconst resolvedFallback = computed(\n () => props.fallbackText ?? normalizedCode.value.toUpperCase(),\n);\n\nconst failed = ref(false);\n\n// A new source deserves a fresh attempt — otherwise one bad code would leave the\n// component stuck on the fallback for every later flag.\nwatch(src, () => {\n failed.value = false;\n});\n\nconst onError = () => {\n failed.value = true;\n};\n</script>\n\n<template>\n <span\n :class=\"classes\"\n :role=\"label ? 'img' : undefined\"\n :aria-label=\"label || undefined\"\n :aria-hidden=\"label ? undefined : 'true'\"\n >\n <img\n v-if=\"!failed\"\n :src=\"src\"\n :srcset=\"srcset\"\n alt=\"\"\n class=\"t-flag__image\"\n @error=\"onError\"\n >\n <!--\n Flags come from a third-party origin, so the request can fail offline or\n behind a strict img-src policy. Showing the country code keeps the control\n legible instead of collapsing it to an empty box.\n -->\n <span\n v-else\n class=\"t-flag__fallback\"\n >\n {{ resolvedFallback }}\n </span>\n </span>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;EAeA,IAAM,IAA+D;GACnE,IAAI;IAAE,MAAM;IAAO,QAAQ;GAAM;GACjC,IAAI;IAAE,MAAM;IAAO,QAAQ;GAAM;GACjC,IAAI;IAAE,MAAM;IAAO,QAAQ;GAAM;EACnC,GAEM,IAAQ,GAoBR,IAAU,QAAe,CAC7B,UACA,WAAW,EAAM,MACnB,CAAC,GAEK,IAAiB,QAAe,EAAM,KAAK,KAAK,CAAC,CAAC,YAAY,CAAC,GAI/D,IAAS,QAAe,EAAY,EAAM,SAAS,EAAY,EAAE,GAIjE,IAAS,QAAe,EAAM,QAAQ,QAAQ,QAAQ,EAAE,CAAC,GAEzD,IAAM,QACJ,GAAG,EAAO,MAAM,GAAG,EAAO,MAAM,KAAK,GAAG,EAAe,MAAM,KACrE,GAEM,IAAS,QACP,GAAG,EAAO,MAAM,GAAG,EAAO,MAAM,OAAO,GAAG,EAAe,MAAM,QACvE,GAEM,IAAmB,QACjB,EAAM,gBAAgB,EAAe,MAAM,YAAY,CAC/D,GAEM,IAAS,EAAI,EAAK;EAIxB,EAAM,SAAW;GACf,EAAO,QAAQ;EACjB,CAAC;EAED,IAAM,UAAgB;GACpB,EAAO,QAAQ;EACjB;yBAIE,EAyBO,QAAA;GAxBJ,OAAK,EAAE,EAAA,KAAO;GACd,MAAM,EAAA,QAAK,QAAW,KAAA;GACtB,cAAY,EAAA,SAAS,KAAA;GACrB,eAAa,EAAA,QAAQ,KAAA,IAAS;MAGtB,EAAA,cAYT,EAKO,QALP,GAKO,EADF,EAAA,KAAgB,GAAA,CAAA,MAhBZ,EAAA,GADT,EAOC,OAAA;;GALE,KAAK,EAAA;GACL,QAAQ,EAAA;GACT,KAAI;GACJ,OAAM;GACE;kBAWW,GAAA,IAAA,CAAA"}
@@ -1,4 +1,4 @@
1
- import { TIconName } from '../../../icons/src/index.ts';
1
+ import { TIconName } from '@treeui/icons';
2
2
  type __VLS_Props = {
3
3
  name: TIconName;
4
4
  size?: number | string;
@@ -0,0 +1,2 @@
1
+ var e=require("./TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs").default;exports.default=e;
2
+ //# sourceMappingURL=TLanguageSelect.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TLanguageSelect.cjs","names":[],"sources":["../../src/components/TLanguageSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, isActivationKey, isEscapeKey } from '@treeui/utils';\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TFieldWidth, TSize } from '../types/contracts';\nimport TFlag from './TFlag.vue';\nimport TIcon from './TIcon.vue';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\n/**\n * `field` is a form control: it reads as the value of a labelled question, so the\n * flag leads and the trigger fills its container.\n *\n * `switcher` is a page-level control, typically in a navbar, where nothing nearby\n * says what it changes. The translate icon states the purpose up front and the\n * current flag closes the row, so the whole thing reads as \"page language, now\n * set to this\" without an external label.\n */\nexport type TLanguageSelectVariant = 'field' | 'switcher';\n\nexport interface TLanguageOption {\n label: string;\n /** Opaque application key — typically a BCP-47 tag. Never parsed or validated. */\n value: string;\n /** ISO 3166-1 alpha-2 code for the flag. Omit to render the option without one. */\n code?: string;\n /** Secondary line, e.g. the language's endonym. */\n description?: string;\n disabled?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n options?: TLanguageOption[];\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n variant?: TLanguageSelectVariant;\n /** Hide the language name, leaving icon + flag. `switcher` only. */\n iconOnly?: boolean;\n /** Inline-size cap. Fluid (`full`) by default. */\n width?: TFieldWidth;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n /** Forwarded to every nested TFlag. Set once here to self-host flag assets. */\n flagBaseUrl?: string;\n emptyText?: string;\n }>(),\n {\n modelValue: '',\n options: () => [],\n open: undefined,\n defaultOpen: false,\n size: 'md',\n variant: 'field',\n iconOnly: false,\n width: 'full',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: '',\n flagBaseUrl: undefined,\n emptyText: 'No languages available.',\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n prefix?: () => unknown;\n option?: (props: { option: TLanguageOption; selected: boolean; focused: boolean }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst listboxId = createId('t-language-select');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst listboxRef = ref<HTMLElement | null>(null);\nconst optionRefs = ref<Map<string, HTMLElement>>(new Map());\nconst focusedIndex = ref(-1);\nconst dropUp = ref(false);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst isSwitcher = computed(() => props.variant === 'switcher');\n\nconst rootClasses = computed(() => [\n 't-language-select',\n `t-language-select--${props.size}`,\n `t-language-select--${props.variant}`,\n // A switcher sizes to its content, so the field width scale does not apply.\n !isSwitcher.value && props.width !== 'full' ? `t-field-width--${props.width}` : null,\n {\n 't-language-select--icon-only': isSwitcher.value && props.iconOnly,\n 't-language-select--drop-up': dropUp.value,\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\n// Nearest ancestor that can clip the listbox (scroll container or hidden overflow).\nconst findClippingAncestor = (element: HTMLElement): HTMLElement | null => {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n const { overflowY } = window.getComputedStyle(parent);\n if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'hidden') {\n return parent;\n }\n parent = parent.parentElement;\n }\n return null;\n};\n\n// Flip the listbox above the trigger when its rendered position overflows the\n// nearest clipping ancestor (e.g. a modal body) or the viewport, and there is\n// more room above the trigger than below it. Measuring the rendered rect keeps\n// the decision in sync with the CSS offset (--tree-space-2) automatically.\nconst updateDropDirection = () => {\n const trigger = triggerRef.value;\n const listbox = listboxRef.value;\n if (!trigger || !listbox) {\n dropUp.value = false;\n return;\n }\n const triggerRect = trigger.getBoundingClientRect();\n const listboxRect = listbox.getBoundingClientRect();\n const boundary = findClippingAncestor(trigger)?.getBoundingClientRect();\n const bottomLimit = Math.min(boundary?.bottom ?? Infinity, window.innerHeight);\n const topLimit = Math.max(boundary?.top ?? 0, 0);\n const spaceBelow = bottomLimit - triggerRect.bottom;\n const spaceAbove = triggerRect.top - topLimit;\n dropUp.value = listboxRect.bottom > bottomLimit && spaceAbove > spaceBelow;\n};\n\nconst rootStyle = computed(() => attrs.style);\n\nconst selectedOption = computed(() =>\n props.options.find((o) => o.value === props.modelValue),\n);\n\nconst selectedLabel = computed(() => selectedOption.value?.label ?? '');\n\n// iconOnly leans on the flag to carry the current value. With no flag to show it\n// would leave the trigger stating nothing at all, so the name comes back.\nconst showValue = computed(\n () => !(isSwitcher.value && props.iconOnly) || !selectedOption.value?.code,\n);\n\nconst enabledOptions = computed(() =>\n props.options.filter((o) => !o.disabled),\n);\n\n// Runs for every open, however it was triggered. The listbox can also be opened\n// through `open` / `defaultOpen` without going near openDropdown(), so this lives\n// on the isOpen watcher rather than inside the click handler.\nconst onOpened = () => {\n const selectedIdx = props.options.findIndex((o) => o.value === props.modelValue);\n focusedIndex.value = selectedIdx >= 0 ? selectedIdx : 0;\n // Render downward first so the measurement always starts from the default position.\n dropUp.value = false;\n nextTick(() => {\n updateDropDirection();\n focusOption(focusedIndex.value);\n });\n};\n\nconst openDropdown = () => {\n if (props.disabled) return;\n setValue(true);\n};\n\nconst closeDropdown = (restoreFocus = false) => {\n setValue(false);\n focusedIndex.value = -1;\n if (restoreFocus) {\n nextTick(() => triggerRef.value?.focus());\n }\n};\n\nconst toggleDropdown = () => {\n if (isOpen.value) {\n closeDropdown();\n } else {\n openDropdown();\n }\n};\n\nconst selectOption = (opt: TLanguageOption) => {\n if (opt.disabled) return;\n emit('update:modelValue', opt.value);\n closeDropdown(true);\n};\n\nconst focusOption = (index: number) => {\n const opt = props.options[index];\n if (!opt) return;\n const el = optionRefs.value.get(opt.value);\n el?.focus();\n};\n\nconst setOptionRef = (el: Element | null, value: string) => {\n if (el instanceof HTMLElement) {\n optionRefs.value.set(value, el);\n } else {\n optionRefs.value.delete(value);\n }\n};\n\nconst moveFocus = (direction: 1 | -1) => {\n let nextIndex = focusedIndex.value + direction;\n while (nextIndex >= 0 && nextIndex < props.options.length) {\n if (!props.options[nextIndex].disabled) {\n focusedIndex.value = nextIndex;\n focusOption(nextIndex);\n return;\n }\n nextIndex += direction;\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeDropdown();\n return;\n }\n\n if (isActivationKey(event) || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n openDropdown();\n }\n};\n\nconst onOptionKeydown = (event: KeyboardEvent, opt: TLanguageOption, _index: number) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeDropdown(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectOption(opt);\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(-1);\n break;\n case 'Home':\n event.preventDefault();\n focusedIndex.value = enabledOptions.value.length > 0\n ? props.options.indexOf(enabledOptions.value[0])\n : 0;\n focusOption(focusedIndex.value);\n break;\n case 'End':\n event.preventDefault();\n focusedIndex.value = enabledOptions.value.length > 0\n ? props.options.indexOf(enabledOptions.value[enabledOptions.value.length - 1])\n : props.options.length - 1;\n focusOption(focusedIndex.value);\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closeDropdown();\n};\n\nconst hasSelection = computed(() => props.modelValue !== '');\n\n// immediate, so a listbox that is already open on mount (`defaultOpen`, or a\n// controlled `open` that starts true) still dismisses on outside click and still\n// measures its drop direction.\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n onOpened();\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n}, { immediate: true });\n\n// Options can shrink underneath an open listbox. Leaving focusedIndex past the\n// end would strand moveFocus(), which only ever walks outward from it.\nwatch(\n () => props.options.length,\n (length) => {\n if (isOpen.value && focusedIndex.value >= length) {\n focusedIndex.value = length - 1;\n }\n },\n);\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-language-select__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? listboxId : undefined\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n :aria-busy=\"loading || undefined\"\n @click=\"toggleDropdown\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-language-select__slot t-language-select__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <!--\n The switcher leads with the translate icon so the control announces what\n it changes; the field leads with the flag, since a form label already\n does that job.\n -->\n <!-- Sized in CSS from the icon token scale, not TIcon's pixel `size` prop. -->\n <TIcon\n v-if=\"isSwitcher\"\n name=\"languages\"\n class=\"t-language-select__translate\"\n />\n <TFlag\n v-else-if=\"selectedOption?.code\"\n :code=\"selectedOption.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <span\n v-if=\"showValue\"\n class=\"t-language-select__value\"\n :data-placeholder=\"!hasSelection ? true : undefined\"\n >\n {{ selectedLabel || placeholder }}\n </span>\n <TFlag\n v-if=\"isSwitcher && selectedOption?.code\"\n :code=\"selectedOption.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <svg\n v-else\n class=\"t-language-select__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"4 6 8 10 12 6\" />\n </svg>\n </button>\n <transition name=\"t-fade\">\n <ul\n v-if=\"isOpen && !disabled\"\n :id=\"listboxId\"\n ref=\"listboxRef\"\n role=\"listbox\"\n class=\"t-language-select__listbox\"\n :aria-label=\"typeof triggerAttrs['aria-label'] === 'string' ? triggerAttrs['aria-label'] : undefined\"\n >\n <li\n v-if=\"options.length === 0\"\n class=\"t-language-select__empty\"\n role=\"presentation\"\n >\n <slot name=\"empty\">\n {{ emptyText }}\n </slot>\n </li>\n <li\n v-for=\"(opt, index) in options\"\n :key=\"opt.value\"\n :ref=\"(el) => setOptionRef(el as Element | null, opt.value)\"\n role=\"option\"\n class=\"t-language-select__option\"\n :class=\"{\n 'is-selected': opt.value === modelValue,\n 'is-disabled': opt.disabled,\n 'is-focused': index === focusedIndex,\n }\"\n :aria-selected=\"opt.value === modelValue\"\n :aria-disabled=\"opt.disabled || undefined\"\n :tabindex=\"opt.disabled ? -1 : 0\"\n @click=\"selectOption(opt)\"\n @keydown=\"onOptionKeydown($event, opt, index)\"\n >\n <slot\n name=\"option\"\n :option=\"opt\"\n :selected=\"opt.value === modelValue\"\n :focused=\"index === focusedIndex\"\n >\n <TFlag\n v-if=\"opt.code\"\n :code=\"opt.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <span class=\"t-language-select__option-text\">\n <span class=\"t-language-select__option-label\">{{ opt.label }}</span>\n <span\n v-if=\"opt.description\"\n class=\"t-language-select__description\"\n >\n {{ opt.description }}\n </span>\n </span>\n </slot>\n <svg\n v-if=\"opt.value === modelValue\"\n class=\"t-language-select__check\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"3.5 8.5 6.5 11.5 12.5 4.5\" />\n </svg>\n </li>\n </ul>\n </transition>\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import e from "./TLanguageSelect.vue_vue_type_script_setup_true_lang.js";
2
+ //#region src/components/TLanguageSelect.vue
3
+ var t = e;
4
+ //#endregion
5
+ export { t as default };
6
+
7
+ //# sourceMappingURL=TLanguageSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TLanguageSelect.js","names":[],"sources":["../../src/components/TLanguageSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { createId, isActivationKey, isEscapeKey } from '@treeui/utils';\nimport { computed, nextTick, onBeforeUnmount, ref, toRef, useAttrs, watch } from 'vue';\nimport { useControllableOpen } from '../composables/useControllableOpen';\nimport type { TFieldWidth, TSize } from '../types/contracts';\nimport TFlag from './TFlag.vue';\nimport TIcon from './TIcon.vue';\nimport TSpinner from './TSpinner.vue';\n\ndefineOptions({\n inheritAttrs: false,\n});\n\n/**\n * `field` is a form control: it reads as the value of a labelled question, so the\n * flag leads and the trigger fills its container.\n *\n * `switcher` is a page-level control, typically in a navbar, where nothing nearby\n * says what it changes. The translate icon states the purpose up front and the\n * current flag closes the row, so the whole thing reads as \"page language, now\n * set to this\" without an external label.\n */\nexport type TLanguageSelectVariant = 'field' | 'switcher';\n\nexport interface TLanguageOption {\n label: string;\n /** Opaque application key — typically a BCP-47 tag. Never parsed or validated. */\n value: string;\n /** ISO 3166-1 alpha-2 code for the flag. Omit to render the option without one. */\n code?: string;\n /** Secondary line, e.g. the language's endonym. */\n description?: string;\n disabled?: boolean;\n}\n\nconst props = withDefaults(\n defineProps<{\n modelValue?: string;\n options?: TLanguageOption[];\n open?: boolean;\n defaultOpen?: boolean;\n size?: TSize;\n variant?: TLanguageSelectVariant;\n /** Hide the language name, leaving icon + flag. `switcher` only. */\n iconOnly?: boolean;\n /** Inline-size cap. Fluid (`full`) by default. */\n width?: TFieldWidth;\n disabled?: boolean;\n loading?: boolean;\n invalid?: boolean;\n placeholder?: string;\n /** Forwarded to every nested TFlag. Set once here to self-host flag assets. */\n flagBaseUrl?: string;\n emptyText?: string;\n }>(),\n {\n modelValue: '',\n options: () => [],\n open: undefined,\n defaultOpen: false,\n size: 'md',\n variant: 'field',\n iconOnly: false,\n width: 'full',\n disabled: false,\n loading: false,\n invalid: false,\n placeholder: '',\n flagBaseUrl: undefined,\n emptyText: 'No languages available.',\n },\n);\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string];\n 'update:open': [value: boolean];\n 'open-change': [value: boolean];\n}>();\n\ndefineSlots<{\n prefix?: () => unknown;\n option?: (props: { option: TLanguageOption; selected: boolean; focused: boolean }) => unknown;\n empty?: () => unknown;\n}>();\n\nconst attrs = useAttrs();\nconst listboxId = createId('t-language-select');\nconst rootRef = ref<HTMLElement | null>(null);\nconst triggerRef = ref<HTMLButtonElement | null>(null);\nconst listboxRef = ref<HTMLElement | null>(null);\nconst optionRefs = ref<Map<string, HTMLElement>>(new Map());\nconst focusedIndex = ref(-1);\nconst dropUp = ref(false);\n\nconst triggerAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs;\n return rest;\n});\n\nconst { value: isOpen, setValue } = useControllableOpen(\n toRef(props, 'open'),\n props.defaultOpen,\n (value) => {\n emit('update:open', value);\n emit('open-change', value);\n },\n);\n\nconst isSwitcher = computed(() => props.variant === 'switcher');\n\nconst rootClasses = computed(() => [\n 't-language-select',\n `t-language-select--${props.size}`,\n `t-language-select--${props.variant}`,\n // A switcher sizes to its content, so the field width scale does not apply.\n !isSwitcher.value && props.width !== 'full' ? `t-field-width--${props.width}` : null,\n {\n 't-language-select--icon-only': isSwitcher.value && props.iconOnly,\n 't-language-select--drop-up': dropUp.value,\n 'is-disabled': props.disabled,\n 'is-invalid': props.invalid,\n 'is-loading': props.loading,\n 'is-open': isOpen.value,\n },\n attrs.class,\n]);\n\n// Nearest ancestor that can clip the listbox (scroll container or hidden overflow).\nconst findClippingAncestor = (element: HTMLElement): HTMLElement | null => {\n let parent = element.parentElement;\n while (parent && parent !== document.body) {\n const { overflowY } = window.getComputedStyle(parent);\n if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'hidden') {\n return parent;\n }\n parent = parent.parentElement;\n }\n return null;\n};\n\n// Flip the listbox above the trigger when its rendered position overflows the\n// nearest clipping ancestor (e.g. a modal body) or the viewport, and there is\n// more room above the trigger than below it. Measuring the rendered rect keeps\n// the decision in sync with the CSS offset (--tree-space-2) automatically.\nconst updateDropDirection = () => {\n const trigger = triggerRef.value;\n const listbox = listboxRef.value;\n if (!trigger || !listbox) {\n dropUp.value = false;\n return;\n }\n const triggerRect = trigger.getBoundingClientRect();\n const listboxRect = listbox.getBoundingClientRect();\n const boundary = findClippingAncestor(trigger)?.getBoundingClientRect();\n const bottomLimit = Math.min(boundary?.bottom ?? Infinity, window.innerHeight);\n const topLimit = Math.max(boundary?.top ?? 0, 0);\n const spaceBelow = bottomLimit - triggerRect.bottom;\n const spaceAbove = triggerRect.top - topLimit;\n dropUp.value = listboxRect.bottom > bottomLimit && spaceAbove > spaceBelow;\n};\n\nconst rootStyle = computed(() => attrs.style);\n\nconst selectedOption = computed(() =>\n props.options.find((o) => o.value === props.modelValue),\n);\n\nconst selectedLabel = computed(() => selectedOption.value?.label ?? '');\n\n// iconOnly leans on the flag to carry the current value. With no flag to show it\n// would leave the trigger stating nothing at all, so the name comes back.\nconst showValue = computed(\n () => !(isSwitcher.value && props.iconOnly) || !selectedOption.value?.code,\n);\n\nconst enabledOptions = computed(() =>\n props.options.filter((o) => !o.disabled),\n);\n\n// Runs for every open, however it was triggered. The listbox can also be opened\n// through `open` / `defaultOpen` without going near openDropdown(), so this lives\n// on the isOpen watcher rather than inside the click handler.\nconst onOpened = () => {\n const selectedIdx = props.options.findIndex((o) => o.value === props.modelValue);\n focusedIndex.value = selectedIdx >= 0 ? selectedIdx : 0;\n // Render downward first so the measurement always starts from the default position.\n dropUp.value = false;\n nextTick(() => {\n updateDropDirection();\n focusOption(focusedIndex.value);\n });\n};\n\nconst openDropdown = () => {\n if (props.disabled) return;\n setValue(true);\n};\n\nconst closeDropdown = (restoreFocus = false) => {\n setValue(false);\n focusedIndex.value = -1;\n if (restoreFocus) {\n nextTick(() => triggerRef.value?.focus());\n }\n};\n\nconst toggleDropdown = () => {\n if (isOpen.value) {\n closeDropdown();\n } else {\n openDropdown();\n }\n};\n\nconst selectOption = (opt: TLanguageOption) => {\n if (opt.disabled) return;\n emit('update:modelValue', opt.value);\n closeDropdown(true);\n};\n\nconst focusOption = (index: number) => {\n const opt = props.options[index];\n if (!opt) return;\n const el = optionRefs.value.get(opt.value);\n el?.focus();\n};\n\nconst setOptionRef = (el: Element | null, value: string) => {\n if (el instanceof HTMLElement) {\n optionRefs.value.set(value, el);\n } else {\n optionRefs.value.delete(value);\n }\n};\n\nconst moveFocus = (direction: 1 | -1) => {\n let nextIndex = focusedIndex.value + direction;\n while (nextIndex >= 0 && nextIndex < props.options.length) {\n if (!props.options[nextIndex].disabled) {\n focusedIndex.value = nextIndex;\n focusOption(nextIndex);\n return;\n }\n nextIndex += direction;\n }\n};\n\nconst onTriggerKeydown = (event: KeyboardEvent) => {\n if (props.disabled) return;\n\n if (isEscapeKey(event) && isOpen.value) {\n event.preventDefault();\n closeDropdown();\n return;\n }\n\n if (isActivationKey(event) || event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n openDropdown();\n }\n};\n\nconst onOptionKeydown = (event: KeyboardEvent, opt: TLanguageOption, _index: number) => {\n if (isEscapeKey(event)) {\n event.preventDefault();\n closeDropdown(true);\n return;\n }\n\n if (isActivationKey(event)) {\n event.preventDefault();\n selectOption(opt);\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n moveFocus(1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n moveFocus(-1);\n break;\n case 'Home':\n event.preventDefault();\n focusedIndex.value = enabledOptions.value.length > 0\n ? props.options.indexOf(enabledOptions.value[0])\n : 0;\n focusOption(focusedIndex.value);\n break;\n case 'End':\n event.preventDefault();\n focusedIndex.value = enabledOptions.value.length > 0\n ? props.options.indexOf(enabledOptions.value[enabledOptions.value.length - 1])\n : props.options.length - 1;\n focusOption(focusedIndex.value);\n break;\n default:\n break;\n }\n};\n\nconst onDocumentPointerDown = (event: PointerEvent) => {\n if (!isOpen.value) return;\n const target = event.target;\n if (!(target instanceof Node)) return;\n if (rootRef.value?.contains(target)) return;\n closeDropdown();\n};\n\nconst hasSelection = computed(() => props.modelValue !== '');\n\n// immediate, so a listbox that is already open on mount (`defaultOpen`, or a\n// controlled `open` that starts true) still dismisses on outside click and still\n// measures its drop direction.\nwatch(isOpen, (value) => {\n if (value) {\n document.addEventListener('pointerdown', onDocumentPointerDown);\n onOpened();\n } else {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n }\n}, { immediate: true });\n\n// Options can shrink underneath an open listbox. Leaving focusedIndex past the\n// end would strand moveFocus(), which only ever walks outward from it.\nwatch(\n () => props.options.length,\n (length) => {\n if (isOpen.value && focusedIndex.value >= length) {\n focusedIndex.value = length - 1;\n }\n },\n);\n\nonBeforeUnmount(() => {\n document.removeEventListener('pointerdown', onDocumentPointerDown);\n});\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n :class=\"rootClasses\"\n :style=\"rootStyle\"\n :data-state=\"isOpen ? 'open' : 'closed'\"\n >\n <button\n ref=\"triggerRef\"\n v-bind=\"triggerAttrs\"\n type=\"button\"\n class=\"t-language-select__trigger\"\n :disabled=\"disabled\"\n :aria-controls=\"isOpen ? listboxId : undefined\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-invalid=\"invalid || undefined\"\n :aria-busy=\"loading || undefined\"\n @click=\"toggleDropdown\"\n @keydown=\"onTriggerKeydown\"\n >\n <span\n v-if=\"$slots.prefix\"\n class=\"t-language-select__slot t-language-select__slot--prefix\"\n >\n <slot name=\"prefix\" />\n </span>\n <!--\n The switcher leads with the translate icon so the control announces what\n it changes; the field leads with the flag, since a form label already\n does that job.\n -->\n <!-- Sized in CSS from the icon token scale, not TIcon's pixel `size` prop. -->\n <TIcon\n v-if=\"isSwitcher\"\n name=\"languages\"\n class=\"t-language-select__translate\"\n />\n <TFlag\n v-else-if=\"selectedOption?.code\"\n :code=\"selectedOption.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <span\n v-if=\"showValue\"\n class=\"t-language-select__value\"\n :data-placeholder=\"!hasSelection ? true : undefined\"\n >\n {{ selectedLabel || placeholder }}\n </span>\n <TFlag\n v-if=\"isSwitcher && selectedOption?.code\"\n :code=\"selectedOption.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <TSpinner\n v-if=\"loading\"\n size=\"sm\"\n label=\"Loading\"\n />\n <svg\n v-else\n class=\"t-language-select__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"4 6 8 10 12 6\" />\n </svg>\n </button>\n <transition name=\"t-fade\">\n <ul\n v-if=\"isOpen && !disabled\"\n :id=\"listboxId\"\n ref=\"listboxRef\"\n role=\"listbox\"\n class=\"t-language-select__listbox\"\n :aria-label=\"typeof triggerAttrs['aria-label'] === 'string' ? triggerAttrs['aria-label'] : undefined\"\n >\n <li\n v-if=\"options.length === 0\"\n class=\"t-language-select__empty\"\n role=\"presentation\"\n >\n <slot name=\"empty\">\n {{ emptyText }}\n </slot>\n </li>\n <li\n v-for=\"(opt, index) in options\"\n :key=\"opt.value\"\n :ref=\"(el) => setOptionRef(el as Element | null, opt.value)\"\n role=\"option\"\n class=\"t-language-select__option\"\n :class=\"{\n 'is-selected': opt.value === modelValue,\n 'is-disabled': opt.disabled,\n 'is-focused': index === focusedIndex,\n }\"\n :aria-selected=\"opt.value === modelValue\"\n :aria-disabled=\"opt.disabled || undefined\"\n :tabindex=\"opt.disabled ? -1 : 0\"\n @click=\"selectOption(opt)\"\n @keydown=\"onOptionKeydown($event, opt, index)\"\n >\n <slot\n name=\"option\"\n :option=\"opt\"\n :selected=\"opt.value === modelValue\"\n :focused=\"index === focusedIndex\"\n >\n <TFlag\n v-if=\"opt.code\"\n :code=\"opt.code\"\n :base-url=\"flagBaseUrl\"\n :size=\"size\"\n />\n <span class=\"t-language-select__option-text\">\n <span class=\"t-language-select__option-label\">{{ opt.label }}</span>\n <span\n v-if=\"opt.description\"\n class=\"t-language-select__description\"\n >\n {{ opt.description }}\n </span>\n </span>\n </slot>\n <svg\n v-if=\"opt.value === modelValue\"\n class=\"t-language-select__check\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"3.5 8.5 6.5 11.5 12.5 4.5\" />\n </svg>\n </li>\n </ul>\n </transition>\n </div>\n</template>\n"],"mappings":""}
@@ -0,0 +1,103 @@
1
+ import { TFieldWidth, TSize } from '../types/contracts';
2
+ /**
3
+ * `field` is a form control: it reads as the value of a labelled question, so the
4
+ * flag leads and the trigger fills its container.
5
+ *
6
+ * `switcher` is a page-level control, typically in a navbar, where nothing nearby
7
+ * says what it changes. The translate icon states the purpose up front and the
8
+ * current flag closes the row, so the whole thing reads as "page language, now
9
+ * set to this" without an external label.
10
+ */
11
+ export type TLanguageSelectVariant = 'field' | 'switcher';
12
+ export interface TLanguageOption {
13
+ label: string;
14
+ /** Opaque application key — typically a BCP-47 tag. Never parsed or validated. */
15
+ value: string;
16
+ /** ISO 3166-1 alpha-2 code for the flag. Omit to render the option without one. */
17
+ code?: string;
18
+ /** Secondary line, e.g. the language's endonym. */
19
+ description?: string;
20
+ disabled?: boolean;
21
+ }
22
+ type __VLS_Props = {
23
+ modelValue?: string;
24
+ options?: TLanguageOption[];
25
+ open?: boolean;
26
+ defaultOpen?: boolean;
27
+ size?: TSize;
28
+ variant?: TLanguageSelectVariant;
29
+ /** Hide the language name, leaving icon + flag. `switcher` only. */
30
+ iconOnly?: boolean;
31
+ /** Inline-size cap. Fluid (`full`) by default. */
32
+ width?: TFieldWidth;
33
+ disabled?: boolean;
34
+ loading?: boolean;
35
+ invalid?: boolean;
36
+ placeholder?: string;
37
+ /** Forwarded to every nested TFlag. Set once here to self-host flag assets. */
38
+ flagBaseUrl?: string;
39
+ emptyText?: string;
40
+ };
41
+ declare function __VLS_template(): {
42
+ attrs: Partial<{}>;
43
+ slots: Readonly<{
44
+ prefix?: () => unknown;
45
+ option?: (props: {
46
+ option: TLanguageOption;
47
+ selected: boolean;
48
+ focused: boolean;
49
+ }) => unknown;
50
+ empty?: () => unknown;
51
+ }> & {
52
+ prefix?: () => unknown;
53
+ option?: (props: {
54
+ option: TLanguageOption;
55
+ selected: boolean;
56
+ focused: boolean;
57
+ }) => unknown;
58
+ empty?: () => unknown;
59
+ };
60
+ refs: {
61
+ rootRef: HTMLDivElement;
62
+ triggerRef: HTMLButtonElement;
63
+ listboxRef: HTMLUListElement;
64
+ };
65
+ rootEl: any;
66
+ };
67
+ type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
68
+ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
69
+ "update:modelValue": (value: string) => any;
70
+ "update:open": (value: boolean) => any;
71
+ "open-change": (value: boolean) => any;
72
+ }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
73
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
74
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
75
+ "onOpen-change"?: ((value: boolean) => any) | undefined;
76
+ }>, {
77
+ modelValue: string;
78
+ disabled: boolean;
79
+ size: TSize;
80
+ width: TFieldWidth;
81
+ variant: TLanguageSelectVariant;
82
+ loading: boolean;
83
+ invalid: boolean;
84
+ options: TLanguageOption[];
85
+ open: boolean;
86
+ defaultOpen: boolean;
87
+ placeholder: string;
88
+ emptyText: string;
89
+ iconOnly: boolean;
90
+ flagBaseUrl: string;
91
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
92
+ rootRef: HTMLDivElement;
93
+ triggerRef: HTMLButtonElement;
94
+ listboxRef: HTMLUListElement;
95
+ }, any>;
96
+ declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
97
+ export default _default;
98
+ type __VLS_WithTemplateSlots<T, S> = T & {
99
+ new (): {
100
+ $slots: S;
101
+ };
102
+ };
103
+ //# sourceMappingURL=TLanguageSelect.vue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TLanguageSelect.vue.d.ts","sourceRoot":"","sources":["../../src/components/TLanguageSelect.vue"],"names":[],"mappings":"AAifA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAS7D;;;;;;;;GAQG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,UAAU,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,KAAK,WAAW,GAAG;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAmTJ,iBAAS,cAAc;WAyOT,OAAO,IAA6B;;iBA/fvC,MAAM,OAAO;iBACb,CAAC,KAAK,EAAE;YAAE,MAAM,EAAE,eAAe,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,KAAK,OAAO;gBACrF,MAAM,OAAO;;iBAFZ,MAAM,OAAO;iBACb,CAAC,KAAK,EAAE;YAAE,MAAM,EAAE,eAAe,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,KAAK,OAAO;gBACrF,MAAM,OAAO;;;;;;;;EAkgBtB;AAgCD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;gBAnlBJ,MAAM;cAUR,OAAO;UANX,KAAK;WAKJ,WAAW;aAJT,sBAAsB;aAMtB,OAAO;aACP,OAAO;aAXP,eAAe,EAAE;UACpB,OAAO;iBACA,OAAO;iBAUP,MAAM;eAGR,MAAM;cATP,OAAO;iBAQJ,MAAM;;;;;OA6kBtB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -0,0 +1,2 @@
1
+ const e=require("./TSpinner.cjs"),t=require("../composables/useControllableOpen.cjs"),n=require("./TFlag.cjs"),r=require("./TIcon.cjs");let i=require("vue"),a=require("@treeui/utils");var o=[`data-state`],s=[`disabled`,`aria-controls`,`aria-expanded`,`aria-invalid`,`aria-busy`],c={key:0,class:`t-language-select__slot t-language-select__slot--prefix`},l=[`data-placeholder`],u={key:6,class:`t-language-select__chevron`,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},d=[`id`,`aria-label`],f={key:0,class:`t-language-select__empty`,role:`presentation`},p=[`aria-selected`,`aria-disabled`,`tabindex`,`onClick`,`onKeydown`],m={class:`t-language-select__option-text`},h={class:`t-language-select__option-label`},g={key:0,class:`t-language-select__description`},_={key:0,class:`t-language-select__check`,viewBox:`0 0 16 16`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},v=(0,i.defineComponent)({inheritAttrs:!1,__name:`TLanguageSelect`,props:{modelValue:{default:``},options:{default:()=>[]},open:{type:Boolean,default:void 0},defaultOpen:{type:Boolean,default:!1},size:{default:`md`},variant:{default:`field`},iconOnly:{type:Boolean,default:!1},width:{default:`full`},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},invalid:{type:Boolean,default:!1},placeholder:{default:``},flagBaseUrl:{default:void 0},emptyText:{default:`No languages available.`}},emits:[`update:modelValue`,`update:open`,`open-change`],setup(v,{emit:y}){let b=v,x=y,S=(0,i.useAttrs)(),C=(0,a.createId)(`t-language-select`),w=(0,i.ref)(null),T=(0,i.ref)(null),E=(0,i.ref)(null),D=(0,i.ref)(new Map),O=(0,i.ref)(-1),k=(0,i.ref)(!1),A=(0,i.computed)(()=>{let{class:e,style:t,...n}=S;return n}),{value:j,setValue:M}=t.useControllableOpen((0,i.toRef)(b,`open`),b.defaultOpen,e=>{x(`update:open`,e),x(`open-change`,e)}),N=(0,i.computed)(()=>b.variant===`switcher`),P=(0,i.computed)(()=>[`t-language-select`,`t-language-select--${b.size}`,`t-language-select--${b.variant}`,!N.value&&b.width!==`full`?`t-field-width--${b.width}`:null,{"t-language-select--icon-only":N.value&&b.iconOnly,"t-language-select--drop-up":k.value,"is-disabled":b.disabled,"is-invalid":b.invalid,"is-loading":b.loading,"is-open":j.value},S.class]),F=e=>{let t=e.parentElement;for(;t&&t!==document.body;){let{overflowY:e}=window.getComputedStyle(t);if(e===`auto`||e===`scroll`||e===`hidden`)return t;t=t.parentElement}return null},I=()=>{let e=T.value,t=E.value;if(!e||!t){k.value=!1;return}let n=e.getBoundingClientRect(),r=t.getBoundingClientRect(),i=F(e)?.getBoundingClientRect(),a=Math.min(i?.bottom??1/0,window.innerHeight),o=Math.max(i?.top??0,0),s=a-n.bottom,c=n.top-o;k.value=r.bottom>a&&c>s},L=(0,i.computed)(()=>S.style),R=(0,i.computed)(()=>b.options.find(e=>e.value===b.modelValue)),z=(0,i.computed)(()=>R.value?.label??``),B=(0,i.computed)(()=>!(N.value&&b.iconOnly)||!R.value?.code),V=(0,i.computed)(()=>b.options.filter(e=>!e.disabled)),H=()=>{let e=b.options.findIndex(e=>e.value===b.modelValue);O.value=e>=0?e:0,k.value=!1,(0,i.nextTick)(()=>{I(),q(O.value)})},U=()=>{b.disabled||M(!0)},W=(e=!1)=>{M(!1),O.value=-1,e&&(0,i.nextTick)(()=>T.value?.focus())},G=()=>{j.value?W():U()},K=e=>{e.disabled||(x(`update:modelValue`,e.value),W(!0))},q=e=>{let t=b.options[e];t&&D.value.get(t.value)?.focus()},J=(e,t)=>{e instanceof HTMLElement?D.value.set(t,e):D.value.delete(t)},Y=e=>{let t=O.value+e;for(;t>=0&&t<b.options.length;){if(!b.options[t].disabled){O.value=t,q(t);return}t+=e}},X=e=>{if(!b.disabled){if((0,a.isEscapeKey)(e)&&j.value){e.preventDefault(),W();return}((0,a.isActivationKey)(e)||e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),U())}},Z=(e,t,n)=>{if((0,a.isEscapeKey)(e)){e.preventDefault(),W(!0);return}if((0,a.isActivationKey)(e)){e.preventDefault(),K(t);return}switch(e.key){case`ArrowDown`:e.preventDefault(),Y(1);break;case`ArrowUp`:e.preventDefault(),Y(-1);break;case`Home`:e.preventDefault(),O.value=V.value.length>0?b.options.indexOf(V.value[0]):0,q(O.value);break;case`End`:e.preventDefault(),O.value=V.value.length>0?b.options.indexOf(V.value[V.value.length-1]):b.options.length-1,q(O.value);break;default:break}},Q=e=>{if(!j.value)return;let t=e.target;t instanceof Node&&(w.value?.contains(t)||W())},$=(0,i.computed)(()=>b.modelValue!==``);return(0,i.watch)(j,e=>{e?(document.addEventListener(`pointerdown`,Q),H()):document.removeEventListener(`pointerdown`,Q)},{immediate:!0}),(0,i.watch)(()=>b.options.length,e=>{j.value&&O.value>=e&&(O.value=e-1)}),(0,i.onBeforeUnmount)(()=>{document.removeEventListener(`pointerdown`,Q)}),(t,a)=>((0,i.openBlock)(),(0,i.createElementBlock)(`div`,{ref_key:`rootRef`,ref:w,class:(0,i.normalizeClass)(P.value),style:(0,i.normalizeStyle)(L.value),"data-state":(0,i.unref)(j)?`open`:`closed`},[(0,i.createElementVNode)(`button`,(0,i.mergeProps)({ref_key:`triggerRef`,ref:T},A.value,{type:`button`,class:`t-language-select__trigger`,disabled:v.disabled,"aria-controls":(0,i.unref)(j)?(0,i.unref)(C):void 0,"aria-haspopup":`listbox`,"aria-expanded":(0,i.unref)(j),"aria-invalid":v.invalid||void 0,"aria-busy":v.loading||void 0,onClick:G,onKeydown:X}),[t.$slots.prefix?((0,i.openBlock)(),(0,i.createElementBlock)(`span`,c,[(0,i.renderSlot)(t.$slots,`prefix`)])):(0,i.createCommentVNode)(``,!0),N.value?((0,i.openBlock)(),(0,i.createBlock)(r.default,{key:1,name:`languages`,class:`t-language-select__translate`})):R.value?.code?((0,i.openBlock)(),(0,i.createBlock)(n.default,{key:2,code:R.value.code,"base-url":v.flagBaseUrl,size:v.size},null,8,[`code`,`base-url`,`size`])):(0,i.createCommentVNode)(``,!0),B.value?((0,i.openBlock)(),(0,i.createElementBlock)(`span`,{key:3,class:`t-language-select__value`,"data-placeholder":!$.value||void 0},(0,i.toDisplayString)(z.value||v.placeholder),9,l)):(0,i.createCommentVNode)(``,!0),N.value&&R.value?.code?((0,i.openBlock)(),(0,i.createBlock)(n.default,{key:4,code:R.value.code,"base-url":v.flagBaseUrl,size:v.size},null,8,[`code`,`base-url`,`size`])):(0,i.createCommentVNode)(``,!0),v.loading?((0,i.openBlock)(),(0,i.createBlock)(e.default,{key:5,size:`sm`,label:`Loading`})):((0,i.openBlock)(),(0,i.createElementBlock)(`svg`,u,[...a[0]||=[(0,i.createElementVNode)(`polyline`,{points:`4 6 8 10 12 6`},null,-1)]]))],16,s),(0,i.createVNode)(i.Transition,{name:`t-fade`},{default:(0,i.withCtx)(()=>[(0,i.unref)(j)&&!v.disabled?((0,i.openBlock)(),(0,i.createElementBlock)(`ul`,{key:0,id:(0,i.unref)(C),ref_key:`listboxRef`,ref:E,role:`listbox`,class:`t-language-select__listbox`,"aria-label":typeof A.value[`aria-label`]==`string`?A.value[`aria-label`]:void 0},[v.options.length===0?((0,i.openBlock)(),(0,i.createElementBlock)(`li`,f,[(0,i.renderSlot)(t.$slots,`empty`,{},()=>[(0,i.createTextVNode)((0,i.toDisplayString)(v.emptyText),1)])])):(0,i.createCommentVNode)(``,!0),((0,i.openBlock)(!0),(0,i.createElementBlock)(i.Fragment,null,(0,i.renderList)(v.options,(e,r)=>((0,i.openBlock)(),(0,i.createElementBlock)(`li`,{key:e.value,ref_for:!0,ref:t=>J(t,e.value),role:`option`,class:(0,i.normalizeClass)([`t-language-select__option`,{"is-selected":e.value===v.modelValue,"is-disabled":e.disabled,"is-focused":r===O.value}]),"aria-selected":e.value===v.modelValue,"aria-disabled":e.disabled||void 0,tabindex:e.disabled?-1:0,onClick:t=>K(e),onKeydown:t=>Z(t,e,r)},[(0,i.renderSlot)(t.$slots,`option`,{option:e,selected:e.value===v.modelValue,focused:r===O.value},()=>[e.code?((0,i.openBlock)(),(0,i.createBlock)(n.default,{key:0,code:e.code,"base-url":v.flagBaseUrl,size:v.size},null,8,[`code`,`base-url`,`size`])):(0,i.createCommentVNode)(``,!0),(0,i.createElementVNode)(`span`,m,[(0,i.createElementVNode)(`span`,h,(0,i.toDisplayString)(e.label),1),e.description?((0,i.openBlock)(),(0,i.createElementBlock)(`span`,g,(0,i.toDisplayString)(e.description),1)):(0,i.createCommentVNode)(``,!0)])]),e.value===v.modelValue?((0,i.openBlock)(),(0,i.createElementBlock)(`svg`,_,[...a[1]||=[(0,i.createElementVNode)(`polyline`,{points:`3.5 8.5 6.5 11.5 12.5 4.5`},null,-1)]])):(0,i.createCommentVNode)(``,!0)],42,p))),128))],8,d)):(0,i.createCommentVNode)(``,!0)]),_:3})],14,o))}});exports.default=v;
2
+ //# sourceMappingURL=TLanguageSelect.vue_vue_type_script_setup_true_lang.cjs.map