@leaflink/stash 50.10.0 → 50.12.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 (80) hide show
  1. package/assets/icons/tier-1.svg +15 -0
  2. package/assets/icons/tier-2.svg +15 -0
  3. package/assets/icons/tier-3.svg +15 -0
  4. package/assets/spritesheet.svg +1 -1
  5. package/dist/Accordion.js +28 -29
  6. package/dist/Accordion.js.map +1 -1
  7. package/dist/Accordion.vue.d.ts +8 -3
  8. package/dist/AppNavigationItem.vue.d.ts +6 -1
  9. package/dist/AppSidebar.js +1 -1
  10. package/dist/AppSidebar.js.map +1 -1
  11. package/dist/CurrencyInput.js.map +1 -1
  12. package/dist/DataView.js +102 -98
  13. package/dist/DataView.js.map +1 -1
  14. package/dist/{DataView.keys-C7eaZg2G.js → DataView.keys-aSOnA4AD.js} +2 -1
  15. package/dist/DataView.keys-aSOnA4AD.js.map +1 -0
  16. package/dist/DataView.vue.d.ts +11 -0
  17. package/dist/DataViewFilters.js +1 -1
  18. package/dist/DataViewSortButton.js +1 -1
  19. package/dist/DataViewToolbar.js +61 -60
  20. package/dist/DataViewToolbar.js.map +1 -1
  21. package/dist/Dropdown.js +10 -10
  22. package/dist/Dropdown.js.map +1 -1
  23. package/dist/Field.vue_vue_type_script_setup_true_lang-DEizIcDo.js.map +1 -1
  24. package/dist/FilterDropdown.js +1 -1
  25. package/dist/Filters.js.map +1 -1
  26. package/dist/Icon.js +3 -0
  27. package/dist/Icon.js.map +1 -1
  28. package/dist/Icon.vue.d.ts +6 -1
  29. package/dist/IconLabel.vue.d.ts +6 -1
  30. package/dist/InlineEdit.js.map +1 -1
  31. package/dist/InputOptions.js.map +1 -1
  32. package/dist/ListView.js.map +1 -1
  33. package/dist/ListView.vue.d.ts +44 -19
  34. package/dist/Modals.js +7 -7
  35. package/dist/Modals.js.map +1 -1
  36. package/dist/Paginate.js +73 -55
  37. package/dist/Paginate.js.map +1 -1
  38. package/dist/Paginate.vue.d.ts +12 -1
  39. package/dist/QuickAction.vue.d.ts +6 -1
  40. package/dist/SearchBar.js +1 -1
  41. package/dist/SearchBar.js.map +1 -1
  42. package/dist/Select.js +5 -5
  43. package/dist/Select.js.map +1 -1
  44. package/dist/SelectStatus.js +15 -15
  45. package/dist/SelectStatus.js.map +1 -1
  46. package/dist/SelectStatus.vue.d.ts +6 -1
  47. package/dist/Step.js +15 -15
  48. package/dist/Step.js.map +1 -1
  49. package/dist/Table.js +5 -5
  50. package/dist/Table.js.map +1 -1
  51. package/dist/TableCell.js +2 -2
  52. package/dist/TableHeaderCell.js +2 -2
  53. package/dist/TableHeaderRow.js +1 -1
  54. package/dist/TableRow.js +1 -1
  55. package/dist/TextEditor.js +1 -0
  56. package/dist/TextEditor.js.map +1 -1
  57. package/dist/TextEditor.vue.d.ts +6 -1
  58. package/dist/Textarea.js.map +1 -1
  59. package/dist/components.css +1 -1
  60. package/dist/directives/autofocus.js +2 -2
  61. package/dist/directives/autofocus.js.map +1 -1
  62. package/dist/directives/sticky.js.map +1 -1
  63. package/dist/index.js.map +1 -1
  64. package/dist/locale.js +3 -2
  65. package/dist/locale.js.map +1 -1
  66. package/dist/tailwind-base.js +0 -2
  67. package/dist/tailwind-base.js.map +1 -1
  68. package/dist/useModals.js.map +1 -1
  69. package/dist/usePaginationStats-d_q39naC.js +12 -0
  70. package/dist/usePaginationStats-d_q39naC.js.map +1 -0
  71. package/dist/useSelection.js +4 -1
  72. package/dist/useSelection.js.map +1 -1
  73. package/dist/useValidation.js +39 -36
  74. package/dist/useValidation.js.map +1 -1
  75. package/dist/utils/helpers.js +29 -29
  76. package/dist/utils/helpers.js.map +1 -1
  77. package/package.json +27 -5
  78. package/tailwind-base.ts +0 -2
  79. package/types/jsonApi.ts +8 -8
  80. package/dist/DataView.keys-C7eaZg2G.js.map +0 -1
@@ -22,7 +22,12 @@ export default _default;
22
22
 
23
23
  declare type IconName = (typeof iconNames)[number];
24
24
 
25
- declare const iconNames: readonly ["action-dots", "activity", "alert-bell", "archive", "arrow-down", "arrow-left", "arrow-right", "arrow-up", "badge-discount", "badge-seller-elite", "badge-seller-power", "badge-seller-verified", "book-customer", "building-office", "bulk-add", "calendar-reschedule", "calendar", "camera", "caret-down", "caret-up", "change-log", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "circle-check", "circle-close", "circle-dollar", "circle-empty", "circle-info", "circle-partial", "circle-percent", "circle-question-mark", "circle-status", "circle-warning", "clipboard-checkmark", "clipboard-inventory", "close", "combine", "compass", "contact", "contract", "copy", "credit-card", "credit-profile", "dashboard", "document-accept", "document-invoice", "document-recieved", "document-sent", "document-view", "document", "dolly", "download", "edit", "ellipsis", "envelope-open", "envelope", "equals", "export", "figma", "file-csv", "file", "filter-funnel", "filter-line", "flag", "folder", "folder-bar-graph", "folder-orders", "font-bold", "font-clear-format", "font-italic", "font-underline", "gear", "github", "globe", "graph-bar-chart", "graph-line-chart", "graph-pie-chart", "hazard", "hazard-outline", "headset-agent", "headset-mic", "heart-filled", "heart-outline", "help-question-mark", "hide", "history", "home", "image", "import", "keyboard-return", "license-approved", "license-certificate", "lightbulb", "link-add", "link-unlink", "link", "list-bulleted", "list-items", "list-numbered", "loading-big", "loading-empty", "loading-small", "location", "lock-unlock", "lock", "logo-facebook", "logo-instagram", "logo-linkedin", "logo-ll", "logo-metrc", "logo-plaid", "logo-x", "logo-youtube", "logout", "medical", "megaphone-sound", "megaphone", "menu", "message-dispute", "message-reply", "message", "minus", "mj-leaf", "money", "note-add", "note", "open-in-new", "paperclip", "paper-plane", "performance", "phone", "plus", "preview", "print", "product-menu-manage", "product-menu-search", "product-menu", "queue-add", "queue", "recent", "refresh", "register", "reply", "report-download", "sample", "save", "scale-law", "scale-weight", "search", "seed-cycle", "share", "shop-bag-browse", "shop-bag-reorder", "shop-bag", "shop-basket", "shop-cart-add", "shop-cart", "show", "sign-dollar", "sign-percent", "sort", "split", "star-filled", "star-outline", "storefront", "submit", "swap-horizontal", "swap-vertical", "tag-star", "tag", "test-results", "ticket-star", "ticket", "tool-dropper", "tool-wrench", "transfer", "trashcan", "truck", "upload", "user-add", "user-admin", "user-check", "user", "view-card", "view-detailed", "view-list", "warehouse", "working"];
25
+ /**
26
+ * To add a new icon, follow these steps:
27
+ *
28
+ * https://github.com/LeafLink/stash/blob/main/CONTRIBUTING.md#adding-a-new-icon
29
+ */
30
+ declare const iconNames: readonly ["action-dots", "activity", "alert-bell", "archive", "arrow-down", "arrow-left", "arrow-right", "arrow-up", "badge-discount", "badge-seller-elite", "badge-seller-power", "badge-seller-verified", "book-customer", "building-office", "bulk-add", "calendar-reschedule", "calendar", "camera", "caret-down", "caret-up", "change-log", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "circle-check", "circle-close", "circle-dollar", "circle-empty", "circle-info", "circle-partial", "circle-percent", "circle-question-mark", "circle-status", "circle-warning", "clipboard-checkmark", "clipboard-inventory", "close", "combine", "compass", "contact", "contract", "copy", "credit-card", "credit-profile", "dashboard", "document-accept", "document-invoice", "document-recieved", "document-sent", "document-view", "document", "dolly", "download", "edit", "ellipsis", "envelope-open", "envelope", "equals", "export", "figma", "file-csv", "file", "filter-funnel", "filter-line", "flag", "folder", "folder-bar-graph", "folder-orders", "font-bold", "font-clear-format", "font-italic", "font-underline", "gear", "github", "globe", "graph-bar-chart", "graph-line-chart", "graph-pie-chart", "hazard", "hazard-outline", "headset-agent", "headset-mic", "heart-filled", "heart-outline", "help-question-mark", "hide", "history", "home", "image", "import", "keyboard-return", "tier-1", "tier-2", "tier-3", "license-approved", "license-certificate", "lightbulb", "link-add", "link-unlink", "link", "list-bulleted", "list-items", "list-numbered", "loading-big", "loading-empty", "loading-small", "location", "lock-unlock", "lock", "logo-facebook", "logo-instagram", "logo-linkedin", "logo-ll", "logo-metrc", "logo-plaid", "logo-x", "logo-youtube", "logout", "medical", "megaphone-sound", "megaphone", "menu", "message-dispute", "message-reply", "message", "minus", "mj-leaf", "money", "note-add", "note", "open-in-new", "paperclip", "paper-plane", "performance", "phone", "plus", "preview", "print", "product-menu-manage", "product-menu-search", "product-menu", "queue-add", "queue", "recent", "refresh", "register", "reply", "report-download", "sample", "save", "scale-law", "scale-weight", "search", "seed-cycle", "share", "shop-bag-browse", "shop-bag-reorder", "shop-bag", "shop-basket", "shop-cart-add", "shop-cart", "show", "sign-dollar", "sign-percent", "sort", "split", "star-filled", "star-outline", "storefront", "submit", "swap-horizontal", "swap-vertical", "tag-star", "tag", "test-results", "ticket-star", "ticket", "tool-dropper", "tool-wrench", "transfer", "trashcan", "truck", "upload", "user-add", "user-admin", "user-check", "user", "view-card", "view-detailed", "view-list", "warehouse", "working"];
26
31
 
27
32
  export declare interface QuickActionProps {
28
33
  /**
package/dist/SearchBar.js CHANGED
@@ -91,7 +91,7 @@ const N = /* @__PURE__ */ C({
91
91
  } : void 0
92
92
  ]), 1032, ["modelValue", "class", "label", "placeholder", "hint-text"]));
93
93
  }
94
- }), q = /* @__PURE__ */ w(N, [["__scopeId", "data-v-43ef4e0f"]]);
94
+ }), q = /* @__PURE__ */ w(N, [["__scopeId", "data-v-005a7e1b"]]);
95
95
  export {
96
96
  q as default
97
97
  };
@@ -1 +1 @@
1
- {"version":3,"file":"SearchBar.js","sources":["../src/components/SearchBar/SearchBar.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, ref, useSlots } from 'vue';\n\n import { t } from '../../locale';\n import Button from '../Button/Button.vue';\n import Icon from '../Icon/Icon.vue';\n import Input from '../Input/Input.vue';\n\n export interface SearchBarProps {\n /**\n * Indicates loading activity (for example async request) that should prevent search\n */\n isLoading?: boolean;\n\n /**\n * Sets a visual indicator that the search is active\n */\n isWorking?: boolean;\n\n /**\n * The search term\n */\n modelValue?: string;\n\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n\n /**\n * Label for search bar\n */\n label?: string;\n\n /**\n * Text below the input\n */\n hintText?: string;\n }\n\n defineOptions({\n name: 'll-search-bar',\n });\n\n const props = withDefaults(defineProps<SearchBarProps>(), {\n isLoading: false,\n isWorking: false,\n modelValue: '',\n placeholder: t('ll.search'),\n label: t('ll.search'),\n hintText: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when searching or clearing the search input\n */\n (e: 'search', searchTerm: string): void;\n\n /**\n * Emitted when the search term changes\n */\n (e: 'update:model-value', searchTerm: string): void;\n }>();\n\n const slots = useSlots();\n const searchTerm = ref(props.modelValue);\n const stashInputRef = ref<InstanceType<typeof Input>>();\n\n const searchTabIndex = computed(() => (searchTerm.value ? 0 : -1));\n\n function onSearch() {\n if (props.isLoading || props.isWorking) {\n return;\n }\n emit('search', searchTerm.value);\n }\n\n function clearSearchTerm() {\n searchTerm.value = '';\n emit('search', '');\n emit('update:model-value', '');\n\n stashInputRef.value?.inputEl?.focus();\n }\n\n function handleChange() {\n emit('update:model-value', searchTerm.value);\n }\n</script>\n\n<template>\n <Input\n ref=\"stashInputRef\"\n v-model=\"searchTerm\"\n class=\"stash-search-bar\"\n :class=\"[{ 'stash-search-bar--value': !!searchTerm }]\"\n :label=\"props.label\"\n :placeholder=\"props.placeholder\"\n :hint-text=\"props.hintText\"\n data-test=\"stash-search-bar\"\n @update:model-value=\"handleChange\"\n @keyup.enter=\"onSearch\"\n >\n <template #append>\n <Button\n v-if=\"searchTerm\"\n class=\"stash-search-bar__clear-button\"\n :disabled=\"props.isLoading\"\n data-test=\"stash-search-bar|clear-button\"\n @click=\"clearSearchTerm\"\n @keyup.enter=\"clearSearchTerm\"\n @keyup.space=\"clearSearchTerm\"\n >\n <Icon name=\"close\" />\n </Button>\n <Button\n :tabindex=\"searchTabIndex\"\n data-test=\"stash-search-bar|search-button\"\n :disabled=\"props.isLoading\"\n class=\"stash-search-bar__search-button\"\n @click=\"onSearch\"\n @keyup.enter=\"onSearch\"\n @keyup.space=\"onSearch\"\n >\n <Icon :class=\"{ 'tw-animate-spin': props.isWorking }\" :name=\"props.isWorking ? 'loading-empty' : 'search'\" />\n </Button>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Overrides the Input hint-text prop -->\n <slot name=\"hint\"></slot>\n </template>\n </Input>\n</template>\n\n<style scoped>\n .stash-search-bar :deep(input) {\n text-overflow: ellipsis;\n }\n\n .stash-search-bar--value :deep(input) {\n padding-right: 72px;\n }\n\n .stash-search-bar :deep(input):hover {\n border: 1px solid var(--color-blue-500);\n }\n\n .stash-search-bar :deep(.stash-input-append) {\n display: flex;\n justify-content: flex-end;\n width: 72px; /* double the the default width of 36px */\n overflow: visible;\n }\n\n .stash-search-bar__clear-button {\n color: var(--color-ice-700);\n height: theme('spacing.9');\n min-width: theme('spacing.9');\n width: theme('spacing.9');\n background: none;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &:focus-visible {\n outline: none;\n box-shadow: none;\n\n & svg {\n border-radius: theme(borderRadius.DEFAULT);\n\n @apply tw-ring-4 tw-ring-blue-500 tw-ring-opacity-15;\n }\n }\n }\n\n .stash-search-bar__clear-button:disabled {\n color: var(--color-ice-500);\n cursor: default;\n }\n\n .stash-search-bar__search-button {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n height: theme('spacing.9');\n min-width: theme('spacing.9');\n width: theme('spacing.9');\n display: flex;\n align-items: center;\n justify-content: center;\n\n &:focus {\n box-shadow: none;\n }\n\n &:disabled {\n border-color: var(--color-ice-500);\n }\n\n &:hover {\n background-color: var(--color-blue-600);\n }\n\n &:active {\n background-color: var(--color-blue-700);\n }\n\n &:focus-visible {\n outline: none;\n border: 1px solid var(--color-blue-900);\n /* Simulating a bigger border so the design does not break */\n box-shadow: inset 0 0 0 1px var(--color-blue-900), 0 0 0 4px rgb(0 114 240 / 15%); /* Unfortunatelly, we have to use RGBA to make the shadow transparent, this is blue-500 */\n }\n }\n</style>\n"],"names":["props","__props","emit","__emit","slots","useSlots","searchTerm","ref","stashInputRef","searchTabIndex","computed","onSearch","clearSearchTerm","_b","_a","handleChange"],"mappings":";;;;;;;;;;;;;;;;;;;AA4CE,UAAMA,IAAQC,GASRC,IAAOC,GAYPC,IAAQC,EAAS,GACjBC,IAAaC,EAAIP,EAAM,UAAU,GACjCQ,IAAgBD,EAAgC,GAEhDE,IAAiBC,EAAS,MAAOJ,EAAW,QAAQ,IAAI,EAAG;AAEjE,aAASK,IAAW;AACd,MAAAX,EAAM,aAAaA,EAAM,aAGxBE,EAAA,UAAUI,EAAW,KAAK;AAAA,IAAA;AAGjC,aAASM,IAAkB;;AACzB,MAAAN,EAAW,QAAQ,IACnBJ,EAAK,UAAU,EAAE,GACjBA,EAAK,sBAAsB,EAAE,IAEfW,KAAAC,IAAAN,EAAA,UAAA,gBAAAM,EAAO,YAAP,QAAAD,EAAgB;AAAA,IAAM;AAGtC,aAASE,IAAe;AACjB,MAAAb,EAAA,sBAAsBI,EAAW,KAAK;AAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"SearchBar.js","sources":["../src/components/SearchBar/SearchBar.vue"],"sourcesContent":["<script lang=\"ts\" setup>\n import { computed, ref, useSlots } from 'vue';\n\n import { t } from '../../locale';\n import Button from '../Button/Button.vue';\n import Icon from '../Icon/Icon.vue';\n import Input from '../Input/Input.vue';\n\n export interface SearchBarProps {\n /**\n * Indicates loading activity (for example async request) that should prevent search\n */\n isLoading?: boolean;\n\n /**\n * Sets a visual indicator that the search is active\n */\n isWorking?: boolean;\n\n /**\n * The search term\n */\n modelValue?: string;\n\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n\n /**\n * Label for search bar\n */\n label?: string;\n\n /**\n * Text below the input\n */\n hintText?: string;\n }\n\n defineOptions({\n name: 'll-search-bar',\n });\n\n const props = withDefaults(defineProps<SearchBarProps>(), {\n isLoading: false,\n isWorking: false,\n modelValue: '',\n placeholder: t('ll.search'),\n label: t('ll.search'),\n hintText: undefined,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when searching or clearing the search input\n */\n (e: 'search', searchTerm: string): void;\n\n /**\n * Emitted when the search term changes\n */\n (e: 'update:model-value', searchTerm: string): void;\n }>();\n\n const slots = useSlots();\n const searchTerm = ref(props.modelValue);\n const stashInputRef = ref<InstanceType<typeof Input>>();\n\n const searchTabIndex = computed(() => (searchTerm.value ? 0 : -1));\n\n function onSearch() {\n if (props.isLoading || props.isWorking) {\n return;\n }\n emit('search', searchTerm.value);\n }\n\n function clearSearchTerm() {\n searchTerm.value = '';\n emit('search', '');\n emit('update:model-value', '');\n\n stashInputRef.value?.inputEl?.focus();\n }\n\n function handleChange() {\n emit('update:model-value', searchTerm.value);\n }\n</script>\n\n<template>\n <Input\n ref=\"stashInputRef\"\n v-model=\"searchTerm\"\n class=\"stash-search-bar\"\n :class=\"[{ 'stash-search-bar--value': !!searchTerm }]\"\n :label=\"props.label\"\n :placeholder=\"props.placeholder\"\n :hint-text=\"props.hintText\"\n data-test=\"stash-search-bar\"\n @update:model-value=\"handleChange\"\n @keyup.enter=\"onSearch\"\n >\n <template #append>\n <Button\n v-if=\"searchTerm\"\n class=\"stash-search-bar__clear-button\"\n :disabled=\"props.isLoading\"\n data-test=\"stash-search-bar|clear-button\"\n @click=\"clearSearchTerm\"\n @keyup.enter=\"clearSearchTerm\"\n @keyup.space=\"clearSearchTerm\"\n >\n <Icon name=\"close\" />\n </Button>\n <Button\n :tabindex=\"searchTabIndex\"\n data-test=\"stash-search-bar|search-button\"\n :disabled=\"props.isLoading\"\n class=\"stash-search-bar__search-button\"\n @click=\"onSearch\"\n @keyup.enter=\"onSearch\"\n @keyup.space=\"onSearch\"\n >\n <Icon :class=\"{ 'tw-animate-spin': props.isWorking }\" :name=\"props.isWorking ? 'loading-empty' : 'search'\" />\n </Button>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <!-- @slot Overrides the Input hint-text prop -->\n <slot name=\"hint\"></slot>\n </template>\n </Input>\n</template>\n\n<style scoped>\n .stash-search-bar :deep(input) {\n text-overflow: ellipsis;\n }\n\n .stash-search-bar--value :deep(input) {\n padding-right: 72px;\n }\n\n .stash-search-bar :deep(input):hover {\n border: 1px solid var(--color-blue-500);\n }\n\n .stash-search-bar :deep(.stash-input-append) {\n display: flex;\n justify-content: flex-end;\n width: 72px; /* double the the default width of 36px */\n overflow: visible;\n }\n\n .stash-search-bar__clear-button {\n color: var(--color-ice-700);\n height: theme('spacing.9');\n min-width: theme('spacing.9');\n width: theme('spacing.9');\n background: none;\n display: flex;\n align-items: center;\n justify-content: center;\n\n &:focus-visible {\n outline: none;\n box-shadow: none;\n\n & svg {\n border-radius: theme(borderRadius.DEFAULT);\n\n @apply tw-ring-4 tw-ring-blue-500 tw-ring-opacity-15;\n }\n }\n }\n\n .stash-search-bar__clear-button:disabled {\n color: var(--color-ice-500);\n cursor: default;\n }\n\n .stash-search-bar__search-button {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n height: theme('spacing.9');\n min-width: theme('spacing.9');\n width: theme('spacing.9');\n display: flex;\n align-items: center;\n justify-content: center;\n\n &:focus {\n box-shadow: none;\n }\n\n &:disabled {\n border-color: var(--color-ice-500);\n }\n\n &:hover {\n background-color: var(--color-blue-600);\n }\n\n &:active {\n background-color: var(--color-blue-700);\n }\n\n &:focus-visible {\n outline: none;\n border: 1px solid var(--color-blue-900);\n /* Simulating a bigger border so the design does not break */\n box-shadow:\n inset 0 0 0 1px var(--color-blue-900),\n 0 0 0 4px rgb(0 114 240 / 15%); /* Unfortunatelly, we have to use RGBA to make the shadow transparent, this is blue-500 */\n }\n }\n</style>\n"],"names":["props","__props","emit","__emit","slots","useSlots","searchTerm","ref","stashInputRef","searchTabIndex","computed","onSearch","clearSearchTerm","_b","_a","handleChange"],"mappings":";;;;;;;;;;;;;;;;;;;AA4CE,UAAMA,IAAQC,GASRC,IAAOC,GAYPC,IAAQC,EAAS,GACjBC,IAAaC,EAAIP,EAAM,UAAU,GACjCQ,IAAgBD,EAAgC,GAEhDE,IAAiBC,EAAS,MAAOJ,EAAW,QAAQ,IAAI,EAAG;AAEjE,aAASK,IAAW;AACd,MAAAX,EAAM,aAAaA,EAAM,aAGxBE,EAAA,UAAUI,EAAW,KAAK;AAAA,IAAA;AAGjC,aAASM,IAAkB;;AACzB,MAAAN,EAAW,QAAQ,IACnBJ,EAAK,UAAU,EAAE,GACjBA,EAAK,sBAAsB,EAAE,IAEfW,KAAAC,IAAAN,EAAA,UAAA,gBAAAM,EAAO,YAAP,QAAAD,EAAgB;AAAA,IAAM;AAGtC,aAASE,IAAe;AACjB,MAAAb,EAAA,sBAAsBI,EAAW,KAAK;AAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/Select.js CHANGED
@@ -179,12 +179,12 @@ const tt = { class: "tw-flex tw-h-input tw-items-center tw-text-sm" }, lt = ["id
179
179
  }, be = (e) => {
180
180
  if (e.keyCode === E.ESCAPE)
181
181
  b();
182
- else if (e.keyCode === E.DOWN && v.value < C.value.length - 1)
183
- u.value && v.value++;
182
+ else if (e.keyCode === E.DOWN && v.value < C.value.length - 1 && u.value)
183
+ v.value++;
184
184
  else if (e.keyCode === E.UP && v.value > 0)
185
185
  u.value && v.value--, x.value && X.value && v.value === 0 && (x.value.scrollTop -= 100);
186
- else if (e.keyCode === E.ENTER && v.value !== -1)
187
- u.value && D(C.value[v.value]);
186
+ else if (e.keyCode === E.ENTER && v.value !== -1 && u.value)
187
+ D(C.value[v.value]);
188
188
  else
189
189
  return;
190
190
  e.preventDefault(), ge(e);
@@ -473,7 +473,7 @@ const tt = { class: "tw-flex tw-h-input tw-items-center tw-text-sm" }, lt = ["id
473
473
  } : void 0
474
474
  ]), 1040, ["class", "error-text", "hint-text"]));
475
475
  }
476
- }), $t = /* @__PURE__ */ et(pt, [["__scopeId", "data-v-8a707e00"]]);
476
+ }), $t = /* @__PURE__ */ et(pt, [["__scopeId", "data-v-514b8e8b"]]);
477
477
  export {
478
478
  $t as default
479
479
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Select.js","sources":["../src/components/Select/Select.vue"],"sourcesContent":["<script lang=\"ts\">\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n // Note: the `trackBy` prop, the `single` prop, and the fact that options can be strings prevents us from knowing the type of the selected options\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type SelectedOptions = Option | Option[];\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n export interface SelectProps extends FieldProps {\n /**\n * If there is only 1 option selected, it prevents that option from being de-selected\n */\n preventEmpty?: boolean;\n\n /**\n * @deprecated The `allowEmpty` prop is no longer supported; use `preventEmpty` instead.\n */\n allowEmpty?: string | boolean | null;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * List of one or more fields to search on. When empty, displayBy will be used\n */\n searchBy?: string[];\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n\n /**\n * Placeholder text.\n */\n placeholder?: string;\n\n /**\n * @deprecated Use the `error-text` prop instead\n */\n error?: string;\n\n /**\n * @deprecated Use the `hint-text` prop instead\n */\n hint?: string;\n\n /**\n * Sets a custom icon.\n */\n icon?: string | boolean;\n\n /**\n * Sets a name attribute on the native (hidden) select element\n *\n * Setting the default value to `undefined` so the attribute isn't rendered if not explicitly provided to ensure backwards compatibility with MP django forms\n */\n name?: string;\n\n /**\n * The list of all options to select from.\n */\n options?: Option[] | readonly Option[];\n\n /**\n * Lazily evaluate and set component `options`. Will execute upon user mouseover.\n */\n lazy?: () => Option[];\n\n /**\n * Sets the currently-selected value(s) for the component.\n * Accepts an array of Objects, or a single Object (if `single` is true), or a string\n */\n modelValue?: SelectedOptions;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: SelectedOptions | null;\n\n /**\n * Hides the search input\n */\n hideSearch?: boolean;\n\n /**\n * @deprecated The `searchable` prop is no longer supported; Use `hideSearch` instead.\n */\n searchable?: string | boolean | null;\n\n /**\n * If true, prevents the search term from being cleared when the drawer is dismissed.\n */\n preserveSearchTerm?: boolean;\n\n /**\n * Prevents the options from being filtered when the search term changes.\n * Allows the parent to filter the options.\n */\n disableFiltering?: boolean;\n\n /**\n * Functions as a single select, if true\n */\n single?: boolean;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * On an ajaxed request when the dropdown is open, we may be experiencing slower load times.\n * This flag will drop a loader into the dropdown. Note: This has never been design reviewed.\n */\n loading?: boolean;\n\n /**\n * @deprecated Instead, use the `@search` event (the onSearch prop) and ensure its event handler returns a Promise that resolves after the search is complete.\n */\n searchLoading?: boolean;\n\n /**\n * In the selection text we use this to give more visual distinction to what type of item(s) are selected\n * E.g. 2 customers selected. An empty string will result in `2 selected`.\n */\n selectItemType?: string;\n\n /**\n * Hides the \"check\" icon if truthy\n */\n hideCheck?: boolean;\n\n searchPlaceholder?: string;\n\n /**\n * Equivalent to emitting a \"search\" event, except this prop can be used to populate the list of `options` using an HTTP request because a prop's return value can be received and awaited, unlike an event.\n *\n * **Tip:** to show a loading indicator while searching, return a Promise that resolves after the search is complete.\n *\n * **Warning:** the search input is debounced so there is no need to debounce this function.\n */\n onSearch?: (searchTerm: string) => Promise<void> | void;\n\n /**\n * Uses the \"fuzzy search\" algorithm when searching, if true\n */\n useFuzzySearch?: boolean;\n\n /**\n * Sets the placement of the dropdown\n * @default 'bottom-start'\n */\n menuPlacement?: Placement;\n\n /**\n * Enables teleporting the dropdown\n * @default false\n */\n enableTeleport?: boolean;\n\n /**\n * The selector or element to which the dropdown should be teleported\n * @default `'#stash-menus-mount-node'`\n */\n teleportTo?: string | HTMLElement;\n }\n</script>\n\n<script setup lang=\"ts\">\n import { autoUpdate, flip, offset, type Placement, type Side, size, useFloating } from '@floating-ui/vue';\n import logger from '@leaflink/snitch';\n import debounce from 'lodash-es/debounce';\n import isEmpty from 'lodash-es/isEmpty';\n import isEqual from 'lodash-es/isEqual';\n import isPlainObject from 'lodash-es/isPlainObject';\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, nextTick, onMounted, onUnmounted, type Ref, ref, useAttrs, useSlots, watch } from 'vue';\n\n import useSearch from '../../composables/useSearch/useSearch';\n import { DEBOUNCE, KEY_CODES } from '../../constants';\n import vClickoutside from '../../directives/clickoutside/clickoutside';\n import { DEFAULT_MENUS_PLUGIN_NODE_ID } from '../../plugins/MenusPlugin';\n import Chip from '../Chip/Chip.vue';\n import Icon, { IconName } from '../Icon/Icon.vue';\n\n const GAP_HEIGHT = 6;\n\n const MENU_MAX_HEIGHT_PX = 300;\n\n defineOptions({\n name: 'll-select',\n });\n\n const props = withDefaults(defineProps<SelectProps>(), {\n preventEmpty: false,\n allowEmpty: null,\n displayBy: 'name',\n searchBy: () => [],\n trackBy: 'id',\n placeholder: 'Select option',\n error: '',\n hint: '',\n icon: 'caret-down',\n name: undefined,\n options: () => [],\n lazy: undefined,\n modelValue: () => [],\n value: null,\n hideSearch: false,\n searchable: null,\n preserveSearchTerm: false,\n disableFiltering: false,\n single: false,\n noTruncate: false,\n loading: false,\n searchLoading: false,\n selectItemType: '',\n hideCheck: false,\n searchPlaceholder: 'Search',\n onSearch: undefined,\n useFuzzySearch: false,\n menuPlacement: 'bottom-start',\n enableTeleport: false,\n teleportTo: `#${DEFAULT_MENUS_PLUGIN_NODE_ID}`,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', selectedOptions: SelectedOptions): void;\n /**\n * Emitted the selected value(s) a cleared.\n */\n (e: 'clear'): void;\n /**\n * Emitted when an option is added.\n */\n (e: 'add', optionAdded: Option): void;\n /**\n * Emitted when an option is removed.\n */\n (e: 'remove', optionRemoved: Option, index: number): void;\n /**\n * Emitted when the select is opened.\n */\n (e: 'opened'): void;\n /**\n * Emitted when the select is closed.\n */\n (e: 'closed', selectedOptions: SelectedOptions): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n\n const selectRef = ref<HTMLDivElement | null>(null);\n const contentRef = ref<HTMLDivElement | null>(null);\n const optionsWrapperRef = ref<HTMLDivElement | null>(null);\n const chipsRef = ref<HTMLUListElement | null>(null);\n const searchRef = ref<HTMLInputElement | null>(null);\n\n const chipsHeight = ref(0);\n const contentHeight = ref(0);\n const internalValue = ref<Option[]>([]) as Ref<Option[]>;\n const searchFor = ref<(searchTerm: string) => Option[]>(() => []); // initialized in the watch handler for options\n const searchTerm = ref('');\n const activeIndex = ref(-1);\n const isActive = ref(false);\n const pendingSearchRequests = ref({});\n const shouldUseLazyOptions = ref(false);\n\n const { floatingStyles } = useFloating(selectRef, optionsWrapperRef, {\n whileElementsMounted: autoUpdate,\n placement: props.menuPlacement,\n middleware: [\n flip(),\n offset(({ rects }) => {\n if (props.menuPlacement.includes('bottom' satisfies Side) && chipsHeight.value > contentHeight.value) {\n return chipsHeight.value - rects.reference.height + GAP_HEIGHT;\n } else {\n return GAP_HEIGHT;\n }\n }),\n size({\n apply({ availableHeight, elements, rects }) {\n if (props.enableTeleport) {\n Object.assign(elements.floating.style, {\n maxWidth: `${rects.reference.width}px`,\n maxHeight: `${Math.min(availableHeight, MENU_MAX_HEIGHT_PX)}px`,\n });\n }\n },\n }),\n ],\n });\n\n const isSearchable = computed(() => !props.hideSearch && props.searchable !== 'false' && props.searchable !== false);\n\n const internalOptions = computed(() => {\n let OPTIONS = (shouldUseLazyOptions.value && props.lazy ? props.lazy() : props.options).filter(Boolean); // prevent [undefined]\n\n // if `options` is an array... but not an array of objects\n if (!isPlainObject(OPTIONS[0])) {\n OPTIONS = OPTIONS.map((name, id) => ({ name, id } as unknown as Option));\n }\n\n return OPTIONS;\n });\n\n const readOnlyValue = computed(() => {\n if (!internalValue.value.length) {\n return '';\n }\n\n return internalValue.value.map((option) => option[props.displayBy] || option).join(', ');\n });\n\n const isSearching = computed(() => props.searchLoading || Object.keys(pendingSearchRequests.value).length > 0);\n\n const filteredOptions = computed(() => {\n if (props.disableFiltering || !(searchTerm.value || '').trim()) {\n return internalOptions.value;\n }\n\n return searchFor.value(searchTerm.value);\n });\n\n const isChipsOneLine = computed(() => chipsHeight.value <= contentHeight.value);\n\n const shouldShowTotal = computed(\n () => !isActive.value && !props.single && !isChipsOneLine.value && internalValue.value.length,\n );\n\n const clearSelectionText = computed(() => {\n return [internalValue.value.length, props.selectItemType, 'selected'].filter(Boolean).join(' ');\n });\n\n const shouldPreventEmpty = computed(() => {\n return props.preventEmpty || props.allowEmpty === 'false' || props.allowEmpty === false;\n });\n\n watch(searchTerm, (currentValue, oldValue) => oldValue !== currentValue && setIndex(-1));\n\n watch(internalValue, () => updateChipsHeight(), { deep: true });\n\n watch(\n () => props.modelValue,\n (value) => {\n // multiple\n if (Array.isArray(value)) {\n const newValue = value.filter(Boolean); // sometimes modelValue is [undefined]\n\n if (!isEqual(newValue, internalValue.value)) {\n internalValue.value = newValue;\n }\n\n return;\n }\n\n // single; if value is a non-empty string or a non-empty object\n if (!isEmpty(value)) {\n if (!isEqual(value, internalValue.value[0])) {\n internalValue.value[0] = value;\n }\n\n return;\n }\n\n // if value is an empty object or falsy\n internalValue.value = [];\n },\n { immediate: true },\n );\n\n watch(\n () => props.options,\n () => {\n const { searchFor: searchForFn } = useSearch<Option>({\n items: computed(() => internalOptions.value),\n fieldNames: props.searchBy.length ? props.searchBy : [props.displayBy],\n trackBy: props.trackBy,\n });\n\n searchFor.value = (searchTerm) => searchForFn(searchTerm, { fuzzy: props.useFuzzySearch });\n },\n { immediate: true },\n );\n\n const clear = () => {\n internalValue.value = [];\n emit('update:model-value', props.single ? undefined : internalValue.value);\n emit('clear');\n };\n\n /**\n * Adds (or selects) an option.\n * @param {Option} option The selected option\n */\n const handleSelect = (option?: Option) => {\n if (props.disabled || !option || (typeof option === 'object' && 'disabled' in option && option?.disabled)) {\n return;\n }\n\n if (!isSelected(option)) {\n if (props.single) {\n internalValue.value = [];\n }\n\n internalValue.value.push(option);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('add', option);\n } else {\n remove(option);\n }\n\n if (props.single) {\n dismiss();\n }\n };\n\n const handleSelectChange = (evt) => {\n // If this is a single select, just pass along the option\n // This short-circuit is purely for readability, as the iterations for multi would also work for single selects\n if (props.single) {\n const selectedOption = internalOptions.value.find((opt) => opt[props.trackBy]?.toString() === evt.target.value);\n handleSelect(selectedOption);\n return;\n }\n\n // Spread the HTMLCollection (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) to array so we can invoke `find`\n const selectedOptions = [...evt.target.selectedOptions];\n\n // If it's a multiselect, the HTML select used for the screen reader can have multiple items\n // deselected and a new one selected in one change event.\n // We need to reconcile the new selected options with our internal state\n\n // 1. remove all the items in our internal state that are no longer present\n // (Spread this array so we can remove items from it while iterating)\n for (const internalOption of [...internalValue.value]) {\n const selectedOption = selectedOptions.find((opt) => opt.value === internalOption[props.trackBy]?.toString());\n\n if (!selectedOption) {\n remove(internalOption);\n }\n }\n\n // 2. add all the items that are now selected if needed\n for (const selectedOption of selectedOptions) {\n const internalOption = internalOptions.value.find(\n (opt) => opt[props.trackBy]?.toString() === selectedOption.value,\n );\n\n if (isSelected(internalOption)) {\n continue;\n } else {\n handleSelect(internalOption);\n }\n }\n };\n\n /**\n * Removes (or un-selects) an option.\n * @param {Option} option The option to be removed\n */\n const remove = (option: Option) => {\n if (props.disabled || (shouldPreventEmpty.value && internalValue.value.length === 1)) {\n return;\n }\n\n const index = internalValue.value.findIndex((opt) => opt[props.trackBy] === option[props.trackBy]);\n\n if (index === -1) {\n logger.warn('ll-select: could not find option to remove', option);\n return;\n }\n\n internalValue.value.splice(index, 1);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('remove', option, index);\n };\n\n /**\n * Opens the `options` drawer and provides focus.\n */\n const open = () => {\n if (isActive.value) {\n return;\n }\n\n updateChipsHeight();\n\n isActive.value = true;\n emit('opened');\n\n if (isSearchable.value) {\n nextTick(() => {\n searchRef.value?.focus({ preventScroll: true });\n });\n }\n };\n\n /**\n * Closes the drawer and cleans up a few settings.\n */\n const dismiss = () => {\n if (isActive.value) {\n emit('closed', internalValue.value);\n }\n\n setIndex(-1);\n isActive.value = false;\n\n if (!props.preserveSearchTerm) {\n searchTerm.value = '';\n }\n\n // The optional chaining here is required.\n // There are cases when the component is removed from the DOM and the ref is lost.\n // .ie when the component is used in a modal and the modal is dismissed.\n // Tracked in this ticket: F-1893\n contentRef.value?.blur(); // if the component remains in focus, clicking it again will not open the options list\n };\n\n /**\n * Scroll list items when up and down keys are pressed\n * @param {KeyboardEvent} e The native keydown event.\n */\n const scrollItems = async (e: KeyboardEvent) => {\n await nextTick();\n\n if (!contentRef.value) {\n return;\n }\n\n const allowedKeyCodes = [KEY_CODES.ENTER, KEY_CODES.ESCAPE] as number[];\n\n // Wait for highlighted item update after using keyboard (`onKeyDown`)\n if (!allowedKeyCodes.includes(e.keyCode)) {\n const el = contentRef.value.querySelector('.stash-select__option--highlighted');\n const elBCR = el?.getBoundingClientRect();\n const parentBCR = contentRef.value.getBoundingClientRect();\n\n if (!elBCR || !parentBCR) {\n return;\n }\n\n if (elBCR.bottom >= parentBCR.bottom || elBCR.top <= parentBCR.top + elBCR.height) {\n el?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n }\n };\n\n /**\n * Sets the `activeIndex` on hover, so that the user may then fine-tune\n * their selection with the arrow keys.\n */\n const setIndex = (index: number) => {\n activeIndex.value = index;\n };\n\n /**\n * Handles all user keyboard input on the Dropdown.\n * @param {KeyboardEvent} e The native keydown event.\n */\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.keyCode === KEY_CODES.ESCAPE) {\n dismiss();\n } else if (e.keyCode === KEY_CODES.DOWN && activeIndex.value < filteredOptions.value.length - 1) {\n isActive.value && activeIndex.value++;\n } else if (e.keyCode === KEY_CODES.UP && activeIndex.value > 0) {\n isActive.value && activeIndex.value--;\n\n // Scroll to top when search is available and first element is highlighted (Using 100 to be sure that it'll be sure that search is visible)\n if (optionsWrapperRef.value && isSearchable.value && activeIndex.value === 0) {\n optionsWrapperRef.value.scrollTop -= 100;\n }\n } else if (e.keyCode === KEY_CODES.ENTER && activeIndex.value !== -1) {\n isActive.value && handleSelect(filteredOptions.value[activeIndex.value]);\n } else {\n return;\n }\n\n e.preventDefault(); // if keyDown did something / we didn't return\n scrollItems(e);\n };\n\n const handleSearchInput = debounce(function () {\n search();\n }, DEBOUNCE.FAST);\n\n const search = async function () {\n if (!props.onSearch) {\n return;\n }\n\n const requestId = uniqueId('search-request-');\n\n try {\n pendingSearchRequests.value[requestId] = true;\n await props.onSearch(searchTerm.value);\n } finally {\n delete pendingSearchRequests.value[requestId];\n }\n };\n\n /**\n * Dynamically sets classes on an `option` in the drawer.\n * @param {Option} option The `option` for which to generate classes\n * @param {number} index The index of the `option` in the list of `options`\n */\n const optionClass = (option: Option, index: number) => {\n return [\n {\n 'stash-select__option': true,\n 'stash-select__option--disabled':\n option && typeof option === 'object' && 'disabled' in option && option?.disabled,\n 'stash-select__option--highlighted': activeIndex.value === index,\n 'stash-select__option--selected': isSelected(option),\n },\n\n // @deprecated\n {\n 'tw-bg-ice-200': activeIndex.value === index,\n 'tw-bg-blue-100 tw-text-ice-700': isSelected(option),\n },\n ];\n };\n\n /**\n * Finds out if the given item is already present in the selected options\n * @param {Option} option The item to check\n * @returns {boolean} Returns true if element is selected\n */\n const isSelected = (option?: Option | null): boolean => {\n if (option === undefined || option === null) {\n return false;\n }\n\n return internalValue.value.some((value) => value?.[props.trackBy] === option[props.trackBy]);\n };\n\n /**\n * Checks if the element is within the Select menu by checking both the content and the teleportTo element\n */\n function isElementWithinSelectMenu(element: Node | null) {\n if (!element) {\n return false;\n }\n\n const isElementInMenu = contentRef.value?.contains(element);\n const isElementInTeleportedMenu = optionsWrapperRef.value?.contains(element);\n\n return isElementInMenu || isElementInTeleportedMenu;\n }\n\n function handleFocusOut(focusEvent: FocusEvent) {\n const nextFocusedElement = focusEvent.relatedTarget as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(nextFocusedElement);\n\n if (!isFocusedElementInMenu && nextFocusedElement) {\n dismiss();\n }\n }\n\n function handleOutsideClick(event: MouseEvent) {\n const target = event.target as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(target);\n\n if (!isFocusedElementInMenu) {\n dismiss();\n }\n }\n\n /**\n * Handles the mouse leave of the select and clears item highlight\n */\n const handleMouseLeave = () => {\n setIndex(-1);\n };\n\n /**\n * Update height of chips container\n */\n const updateChipsHeight = async () => {\n // Wait for the DOM to update so we can get an accurate value for chips' clientHeight\n await nextTick();\n\n if (chipsRef.value && contentRef.value) {\n contentHeight.value = contentHeight.value === 0 ? contentRef.value.clientHeight : contentHeight.value;\n chipsHeight.value = chipsRef.value.clientHeight;\n }\n };\n\n onMounted(() => {\n if (attrs.onInput) {\n throw new Error('ll-select: use the @update:model-value event instead of @input.');\n }\n\n window.addEventListener('resize', updateChipsHeight);\n\n updateChipsHeight();\n });\n\n /**\n * Remove event listener to handle component width when resizing window\n */\n onUnmounted(() => {\n window.removeEventListener('resize', updateChipsHeight);\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"input ll-select stash-select\"\n data-test=\"stash-select\"\n :class=\"attrs.class\"\n :error-text=\"props.errorText || props.error\"\n :hint-text=\"props.hintText || props.hint\"\n >\n <template v-if=\"props.isReadOnly\" #default=\"{ fieldId, labelId }\">\n <div class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"show-empty tw-h-min\">\n {{ readOnlyValue }}\n </span>\n </div>\n </template>\n <template v-else #default=\"{ fieldId, fieldErrorId, hasError }\">\n <!-- SCREEN READER ONLY -->\n <select\n :id=\"fieldId\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n class=\"tw-sr-only\"\n :disabled=\"props.disabled\"\n :multiple=\"!props.single\"\n :name=\"props.name\"\n @change=\"handleSelectChange\"\n >\n <option\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`srOnlyOption-${index}`\"\n :selected=\"isSelected(option)\"\n :value=\"option[props.trackBy]\"\n >\n {{ option[props.displayBy] || '' }}\n </option>\n\n <!-- Empty option to allow deselecting non-multiselects -->\n <option value=\"\"></option>\n </select>\n\n <!-- SELECT -->\n <div\n ref=\"selectRef\"\n v-clickoutside=\"handleOutsideClick\"\n role=\"listbox\"\n aria-hidden=\"true\"\n class=\"stash-select__content-wrapper\"\n :aria-controls=\"'listbox-' + fieldId\"\n :aria-expanded=\"isActive\"\n :aria-label=\"props.placeholder\"\n :aria-disabled=\"props.disabled || undefined\"\n :class=\"[\n {\n 'stash-select--disabled': !!props.disabled,\n 'stash-select--error': !!(props.errorText || props.error),\n 'stash-select--active': isActive,\n 'stash-select--single': props.single,\n },\n\n // @deprecated\n {\n 'is-active': isActive,\n 'is-single': props.single,\n 'is-disabled': props.disabled,\n },\n ]\"\n @keydown=\"onKeyDown\"\n @keyup.esc=\"dismiss\"\n >\n <!-- CONTENT -->\n <div\n :id=\"'listbox-' + fieldId\"\n ref=\"contentRef\"\n class=\"input-field stash-select__content\"\n tabindex=\"0\"\n @focusin=\"open\"\n @focusin.once=\"shouldUseLazyOptions = true\"\n @focusout=\"handleFocusOut\"\n @keydown=\"open\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- CHIPS -->\n <ul ref=\"chipsRef\" class=\"stash-select__chips\">\n <!-- PLACEHOLDER -->\n <li\n v-if=\"!internalValue.length\"\n class=\"stash-select__placeholder tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n {{ props.placeholder }}\n </li>\n\n <template v-if=\"props.single\">\n <li\n v-for=\"option of internalValue\"\n :key=\"`chip-${option[props.trackBy]}`\"\n class=\"stash-select__selected tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n <!-- @slot Selected value(s) custom text. Exposes the option object. -->\n <slot name=\"selected\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n <button\n tabindex=\"-1\"\n class=\"stash-select__remove\"\n @keypress.enter.prevent=\"remove(option)\"\n @mousedown.prevent.stop=\"remove(option)\"\n >\n <Icon icon=\"close\" name=\"close\" size=\"small\" />\n </button>\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li v-for=\"option of internalValue\" :key=\"`chip-${option[props.trackBy]}`\" class=\"tw-inline-block\">\n <Chip\n bg-color=\"blue-500\"\n is-removable\n text-color=\"white\"\n class=\"stash-select__chip\"\n @remove=\"remove(option)\"\n >\n {{ option[props.displayBy] || option }}\n </Chip>\n </li>\n </template>\n </ul>\n\n <!-- TOTAL -->\n <div v-if=\"shouldShowTotal\" class=\"stash-select__total\">\n <Chip bg-color=\"blue-500\" is-removable text-color=\"white\" class=\"stash-select__chip\" @remove=\"clear\">\n {{ clearSelectionText }}\n </Chip>\n </div>\n\n <!-- TOGGLE -->\n <Icon\n v-if=\"props.icon\"\n class=\"stash-select__icon\"\n data-test=\"stash-select|toggle-icon\"\n :name=\"(props.icon as IconName)\"\n :class=\"{ 'tw-text-ice-500': props.disabled }\"\n @mousedown.prevent=\"isActive && dismiss()\"\n />\n <!-- DRAWER -->\n <Teleport :to=\"props.teleportTo\" :disabled=\"!props.enableTeleport\">\n <transition name=\"fade\">\n <div\n v-show=\"isActive && !props.disabled\"\n ref=\"optionsWrapperRef\"\n class=\"stash-select__border-selector tw-w-full tw-shadow-2xl\"\n :style=\"floatingStyles\"\n @click.stop\n >\n <!-- SEARCH -->\n <div v-if=\"isSearchable\" class=\"tw-flex tw-items-center tw-border-b tw-border-blue-500 tw-pr-1.5\">\n <input\n ref=\"searchRef\"\n v-model=\"searchTerm\"\n type=\"text\"\n autocomplete=\"off\"\n class=\"stash-select__search\"\n :data-test=\"attrs['data-test'] ? attrs['data-test'] + '-search' : 'stash-select|search'\"\n :placeholder=\"props.searchPlaceholder\"\n :spellcheck=\"false\"\n @input=\"handleSearchInput\"\n />\n <Icon name=\"search\" class=\"tw-text-ice-500\" />\n </div>\n\n <!-- OPTIONS -->\n <ul class=\"stash-select__options options tw-my-1.5 tw-w-full tw-border-white tw-bg-white\">\n <li\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`option-${option[props.trackBy]}`\"\n :data-test=\"option[props.trackBy]\"\n :class=\"optionClass(option, index)\"\n @click=\"handleSelect(option)\"\n @mouseenter.self=\"setIndex(index)\"\n >\n <!-- @slot Select custom option text. Exposes the option object. -->\n <slot name=\"option\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n </slot>\n\n <Icon\n v-if=\"isSelected(option) && !props.hideCheck\"\n class=\"tw-ml-auto tw-text-blue-500\"\n name=\"check\"\n />\n </li>\n <li v-show=\"props.loading || isSearching\" class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\">\n <Icon\n data-test=\"stash-select|options-loading\"\n name=\"working\"\n class=\"tw-animate-spin tw-text-ice-500\"\n />\n </li>\n <li\n v-show=\"!props.loading && !isSearching && !filteredOptions.length\"\n class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\"\n data-test=\"stash-select|no-options\"\n >\n <!-- @slot No options text. -->\n <slot name=\"no-options\"> No options </slot>\n </li>\n </ul>\n </div>\n </transition>\n </Teleport>\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style scoped>\n .stash-select {\n position: relative;\n }\n\n .stash-select__content,\n .stash-select__content-wrapper {\n height: theme('height.input');\n }\n\n .stash-select__content {\n cursor: pointer;\n overflow: hidden;\n position: absolute;\n background: theme('colors.white');\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n outline: none;\n padding: 0 !important;\n width: 100%;\n\n &:hover {\n border-color: var(--color-ice-500);\n }\n\n &:focus-within {\n border-color: var(--color-blue-500) !important;\n }\n }\n\n .stash-select--disabled .stash-select__content {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none !important;\n }\n\n .stash-select--active .stash-select__content {\n cursor: default;\n height: auto;\n min-width: theme('width.select-menu');\n overflow: visible;\n z-index: theme('zIndex.control');\n box-shadow: theme('boxShadow.DEFAULT');\n }\n\n .stash-select--error :where(.stash-select__content, .stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500) !important;\n }\n\n .stash-select__placeholder {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__placeholder {\n cursor: default;\n }\n\n .stash-select__icon {\n color: var(--color-ice-700);\n cursor: pointer;\n pointer-events: none;\n position: absolute;\n right: theme('spacing[1.5]');\n top: theme('spacing[1.5]');\n }\n\n .stash-select--disabled .stash-select__icon {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__icon {\n pointer-events: auto;\n }\n\n .stash-select__chips {\n /* distribute (or split?) the margin between chip and chips container. TODO? */\n --half-space: calc(theme('spacing[1.5]') / 2);\n\n padding: var(--half-space);\n padding-right: theme('spacing.9');\n }\n\n /* chip AND placeholder AND selected: */\n .stash-select__chips > li {\n cursor: pointer;\n height: theme('height.chip');\n margin: var(--half-space);\n max-width: 100%;\n }\n\n .stash-select--disabled .stash-select__chips > li {\n cursor: default;\n }\n\n .stash-select__content-wrapper .stash-select__chip {\n border-radius: theme('borderRadius.DEFAULT');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n height: theme('height.chip');\n max-width: inherit;\n text-transform: none;\n }\n\n .stash-select--disabled .stash-select__chip {\n background: var(--color-ice-500);\n color: theme('colors.white');\n }\n\n .stash-select__content-wrapper .stash-select__total .stash-select__chip {\n font-weight: theme('fontWeight.normal');\n }\n\n .stash-select__selected {\n color: var(--color-ice-700);\n }\n\n .stash-select--disabled .stash-select__selected {\n color: var(--color-ice-500);\n }\n\n .stash-select__selected :where(.stash-select__remove) {\n display: none;\n }\n\n .stash-select__total {\n display: block;\n font-weight: theme('fontWeight.semibold');\n line-height: theme('lineHeight.button');\n position: absolute;\n right: theme('spacing.6');\n white-space: nowrap;\n z-index: theme('zIndex.content');\n\n /* -- design TBD --- */\n top: 0;\n left: 0;\n bottom: 0;\n color: theme('colors.white');\n background: white;\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__search {\n position: relative;\n width: 100%;\n height: theme('height.input');\n background: theme('colors.white');\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n border: 0 !important;\n }\n\n .stash-select__border-selector {\n background: white;\n border-radius: theme('borderRadius.DEFAULT');\n border: 1px solid var(--color-blue-500);\n max-height: theme('maxHeight.select-menu');\n overflow: auto;\n z-index: theme('zIndex.dialog');\n }\n\n .stash-select__option {\n align-items: center;\n border-radius: theme('borderRadius.DEFAULT');\n cursor: pointer;\n display: flex;\n line-height: theme('spacing.6');\n margin: theme('spacing[1.5]');\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__option--disabled {\n color: var(--color-ice-500);\n cursor: not-allowed;\n }\n\n .stash-select__option--highlighted {\n background-color: var(--color-ice-200);\n }\n\n .stash-select__option--selected {\n background-color: var(--color-blue-100);\n color: var(--color-ice-700);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","selectRef","ref","contentRef","optionsWrapperRef","chipsRef","searchRef","chipsHeight","contentHeight","internalValue","searchFor","searchTerm","activeIndex","isActive","pendingSearchRequests","shouldUseLazyOptions","floatingStyles","useFloating","autoUpdate","flip","offset","rects","size","availableHeight","elements","isSearchable","computed","internalOptions","OPTIONS","isPlainObject","name","id","readOnlyValue","option","isSearching","filteredOptions","isChipsOneLine","shouldShowTotal","clearSelectionText","shouldPreventEmpty","watch","currentValue","oldValue","setIndex","updateChipsHeight","value","newValue","isEqual","isEmpty","searchForFn","useSearch","clear","handleSelect","isSelected","remove","dismiss","handleSelectChange","evt","selectedOption","opt","_a","selectedOptions","internalOption","index","logger","open","nextTick","scrollItems","KEY_CODES","el","elBCR","parentBCR","onKeyDown","handleSearchInput","debounce","search","DEBOUNCE","requestId","uniqueId","optionClass","isElementWithinSelectMenu","element","isElementInMenu","isElementInTeleportedMenu","_b","handleFocusOut","focusEvent","nextFocusedElement","handleOutsideClick","event","target","handleMouseLeave","onMounted","onUnmounted"],"mappings":"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"}
1
+ {"version":3,"file":"Select.js","sources":["../src/components/Select/Select.vue"],"sourcesContent":["<script lang=\"ts\">\n import { FieldProps } from '../Field/Field.types';\n import Field from '../Field/Field.vue';\n\n // Note: the `trackBy` prop, the `single` prop, and the fact that options can be strings prevents us from knowing the type of the selected options\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type Option = any;\n\n type SelectedOptions = Option | Option[];\n\n export interface SelectProps extends FieldProps {\n /**\n * If there is only 1 option selected, it prevents that option from being de-selected\n */\n preventEmpty?: boolean;\n\n /**\n * @deprecated The `allowEmpty` prop is no longer supported; use `preventEmpty` instead.\n */\n allowEmpty?: string | boolean | null;\n\n /**\n * If `options` are an object, this is what prop to use for display.\n */\n displayBy?: string;\n\n /**\n * List of one or more fields to search on. When empty, displayBy will be used\n */\n searchBy?: string[];\n\n /**\n * Default field to track selected options by.\n */\n trackBy?: string;\n\n /**\n * Placeholder text.\n */\n placeholder?: string;\n\n /**\n * @deprecated Use the `error-text` prop instead\n */\n error?: string;\n\n /**\n * @deprecated Use the `hint-text` prop instead\n */\n hint?: string;\n\n /**\n * Sets a custom icon.\n */\n icon?: string | boolean;\n\n /**\n * Sets a name attribute on the native (hidden) select element\n *\n * Setting the default value to `undefined` so the attribute isn't rendered if not explicitly provided to ensure backwards compatibility with MP django forms\n */\n name?: string;\n\n /**\n * The list of all options to select from.\n */\n options?: Option[] | readonly Option[];\n\n /**\n * Lazily evaluate and set component `options`. Will execute upon user mouseover.\n */\n lazy?: () => Option[];\n\n /**\n * Sets the currently-selected value(s) for the component.\n * Accepts an array of Objects, or a single Object (if `single` is true), or a string\n */\n modelValue?: SelectedOptions;\n\n /**\n * @deprecated Use :model-value or v-model instead of :value.\n */\n value?: SelectedOptions | null;\n\n /**\n * Hides the search input\n */\n hideSearch?: boolean;\n\n /**\n * @deprecated The `searchable` prop is no longer supported; Use `hideSearch` instead.\n */\n searchable?: string | boolean | null;\n\n /**\n * If true, prevents the search term from being cleared when the drawer is dismissed.\n */\n preserveSearchTerm?: boolean;\n\n /**\n * Prevents the options from being filtered when the search term changes.\n * Allows the parent to filter the options.\n */\n disableFiltering?: boolean;\n\n /**\n * Functions as a single select, if true\n */\n single?: boolean;\n\n /**\n * Prevents the Selected Option from being truncated, if true\n */\n noTruncate?: boolean;\n\n /**\n * On an ajaxed request when the dropdown is open, we may be experiencing slower load times.\n * This flag will drop a loader into the dropdown. Note: This has never been design reviewed.\n */\n loading?: boolean;\n\n /**\n * @deprecated Instead, use the `@search` event (the onSearch prop) and ensure its event handler returns a Promise that resolves after the search is complete.\n */\n searchLoading?: boolean;\n\n /**\n * In the selection text we use this to give more visual distinction to what type of item(s) are selected\n * E.g. 2 customers selected. An empty string will result in `2 selected`.\n */\n selectItemType?: string;\n\n /**\n * Hides the \"check\" icon if truthy\n */\n hideCheck?: boolean;\n\n searchPlaceholder?: string;\n\n /**\n * Equivalent to emitting a \"search\" event, except this prop can be used to populate the list of `options` using an HTTP request because a prop's return value can be received and awaited, unlike an event.\n *\n * **Tip:** to show a loading indicator while searching, return a Promise that resolves after the search is complete.\n *\n * **Warning:** the search input is debounced so there is no need to debounce this function.\n */\n onSearch?: (searchTerm: string) => Promise<void> | void;\n\n /**\n * Uses the \"fuzzy search\" algorithm when searching, if true\n */\n useFuzzySearch?: boolean;\n\n /**\n * Sets the placement of the dropdown\n * @default 'bottom-start'\n */\n menuPlacement?: Placement;\n\n /**\n * Enables teleporting the dropdown\n * @default false\n */\n enableTeleport?: boolean;\n\n /**\n * The selector or element to which the dropdown should be teleported\n * @default `'#stash-menus-mount-node'`\n */\n teleportTo?: string | HTMLElement;\n }\n</script>\n\n<script setup lang=\"ts\">\n import { autoUpdate, flip, offset, type Placement, type Side, size, useFloating } from '@floating-ui/vue';\n import logger from '@leaflink/snitch';\n import debounce from 'lodash-es/debounce';\n import isEmpty from 'lodash-es/isEmpty';\n import isEqual from 'lodash-es/isEqual';\n import isPlainObject from 'lodash-es/isPlainObject';\n import uniqueId from 'lodash-es/uniqueId';\n import { computed, nextTick, onMounted, onUnmounted, type Ref, ref, useAttrs, useSlots, watch } from 'vue';\n\n import useSearch from '../../composables/useSearch/useSearch';\n import { DEBOUNCE, KEY_CODES } from '../../constants';\n import vClickoutside from '../../directives/clickoutside/clickoutside';\n import { DEFAULT_MENUS_PLUGIN_NODE_ID } from '../../plugins/MenusPlugin';\n import Chip from '../Chip/Chip.vue';\n import Icon, { IconName } from '../Icon/Icon.vue';\n\n const GAP_HEIGHT = 6;\n\n const MENU_MAX_HEIGHT_PX = 300;\n\n defineOptions({\n name: 'll-select',\n });\n\n const props = withDefaults(defineProps<SelectProps>(), {\n preventEmpty: false,\n allowEmpty: null,\n displayBy: 'name',\n searchBy: () => [],\n trackBy: 'id',\n placeholder: 'Select option',\n error: '',\n hint: '',\n icon: 'caret-down',\n name: undefined,\n options: () => [],\n lazy: undefined,\n modelValue: () => [],\n value: null,\n hideSearch: false,\n searchable: null,\n preserveSearchTerm: false,\n disableFiltering: false,\n single: false,\n noTruncate: false,\n loading: false,\n searchLoading: false,\n selectItemType: '',\n hideCheck: false,\n searchPlaceholder: 'Search',\n onSearch: undefined,\n useFuzzySearch: false,\n menuPlacement: 'bottom-start',\n enableTeleport: false,\n teleportTo: `#${DEFAULT_MENUS_PLUGIN_NODE_ID}`,\n });\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes.\n */\n (e: 'update:model-value', selectedOptions: SelectedOptions): void;\n /**\n * Emitted the selected value(s) a cleared.\n */\n (e: 'clear'): void;\n /**\n * Emitted when an option is added.\n */\n (e: 'add', optionAdded: Option): void;\n /**\n * Emitted when an option is removed.\n */\n (e: 'remove', optionRemoved: Option, index: number): void;\n /**\n * Emitted when the select is opened.\n */\n (e: 'opened'): void;\n /**\n * Emitted when the select is closed.\n */\n (e: 'closed', selectedOptions: SelectedOptions): void;\n }>();\n\n const attrs = useAttrs();\n const slots = useSlots();\n\n const selectRef = ref<HTMLDivElement | null>(null);\n const contentRef = ref<HTMLDivElement | null>(null);\n const optionsWrapperRef = ref<HTMLDivElement | null>(null);\n const chipsRef = ref<HTMLUListElement | null>(null);\n const searchRef = ref<HTMLInputElement | null>(null);\n\n const chipsHeight = ref(0);\n const contentHeight = ref(0);\n const internalValue = ref<Option[]>([]) as Ref<Option[]>;\n const searchFor = ref<(searchTerm: string) => Option[]>(() => []); // initialized in the watch handler for options\n const searchTerm = ref('');\n const activeIndex = ref(-1);\n const isActive = ref(false);\n const pendingSearchRequests = ref({});\n const shouldUseLazyOptions = ref(false);\n\n const { floatingStyles } = useFloating(selectRef, optionsWrapperRef, {\n whileElementsMounted: autoUpdate,\n placement: props.menuPlacement,\n middleware: [\n flip(),\n offset(({ rects }) => {\n if (props.menuPlacement.includes('bottom' satisfies Side) && chipsHeight.value > contentHeight.value) {\n return chipsHeight.value - rects.reference.height + GAP_HEIGHT;\n } else {\n return GAP_HEIGHT;\n }\n }),\n size({\n apply({ availableHeight, elements, rects }) {\n if (props.enableTeleport) {\n Object.assign(elements.floating.style, {\n maxWidth: `${rects.reference.width}px`,\n maxHeight: `${Math.min(availableHeight, MENU_MAX_HEIGHT_PX)}px`,\n });\n }\n },\n }),\n ],\n });\n\n const isSearchable = computed(() => !props.hideSearch && props.searchable !== 'false' && props.searchable !== false);\n\n const internalOptions = computed(() => {\n let OPTIONS = (shouldUseLazyOptions.value && props.lazy ? props.lazy() : props.options).filter(Boolean); // prevent [undefined]\n\n // if `options` is an array... but not an array of objects\n if (!isPlainObject(OPTIONS[0])) {\n OPTIONS = OPTIONS.map((name, id) => ({ name, id }) as unknown as Option);\n }\n\n return OPTIONS;\n });\n\n const readOnlyValue = computed(() => {\n if (!internalValue.value.length) {\n return '';\n }\n\n return internalValue.value.map((option) => option[props.displayBy] || option).join(', ');\n });\n\n const isSearching = computed(() => props.searchLoading || Object.keys(pendingSearchRequests.value).length > 0);\n\n const filteredOptions = computed(() => {\n if (props.disableFiltering || !(searchTerm.value || '').trim()) {\n return internalOptions.value;\n }\n\n return searchFor.value(searchTerm.value);\n });\n\n const isChipsOneLine = computed(() => chipsHeight.value <= contentHeight.value);\n\n const shouldShowTotal = computed(\n () => !isActive.value && !props.single && !isChipsOneLine.value && internalValue.value.length,\n );\n\n const clearSelectionText = computed(() => {\n return [internalValue.value.length, props.selectItemType, 'selected'].filter(Boolean).join(' ');\n });\n\n const shouldPreventEmpty = computed(() => {\n return props.preventEmpty || props.allowEmpty === 'false' || props.allowEmpty === false;\n });\n\n watch(searchTerm, (currentValue, oldValue) => oldValue !== currentValue && setIndex(-1));\n\n watch(internalValue, () => updateChipsHeight(), { deep: true });\n\n watch(\n () => props.modelValue,\n (value) => {\n // multiple\n if (Array.isArray(value)) {\n const newValue = value.filter(Boolean); // sometimes modelValue is [undefined]\n\n if (!isEqual(newValue, internalValue.value)) {\n internalValue.value = newValue;\n }\n\n return;\n }\n\n // single; if value is a non-empty string or a non-empty object\n if (!isEmpty(value)) {\n if (!isEqual(value, internalValue.value[0])) {\n internalValue.value[0] = value;\n }\n\n return;\n }\n\n // if value is an empty object or falsy\n internalValue.value = [];\n },\n { immediate: true },\n );\n\n watch(\n () => props.options,\n () => {\n const { searchFor: searchForFn } = useSearch<Option>({\n items: computed(() => internalOptions.value),\n fieldNames: props.searchBy.length ? props.searchBy : [props.displayBy],\n trackBy: props.trackBy,\n });\n\n searchFor.value = (searchTerm) => searchForFn(searchTerm, { fuzzy: props.useFuzzySearch });\n },\n { immediate: true },\n );\n\n const clear = () => {\n internalValue.value = [];\n emit('update:model-value', props.single ? undefined : internalValue.value);\n emit('clear');\n };\n\n /**\n * Adds (or selects) an option.\n * @param {Option} option The selected option\n */\n const handleSelect = (option?: Option) => {\n if (props.disabled || !option || (typeof option === 'object' && 'disabled' in option && option?.disabled)) {\n return;\n }\n\n if (!isSelected(option)) {\n if (props.single) {\n internalValue.value = [];\n }\n\n internalValue.value.push(option);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('add', option);\n } else {\n remove(option);\n }\n\n if (props.single) {\n dismiss();\n }\n };\n\n const handleSelectChange = (evt) => {\n // If this is a single select, just pass along the option\n // This short-circuit is purely for readability, as the iterations for multi would also work for single selects\n if (props.single) {\n const selectedOption = internalOptions.value.find((opt) => opt[props.trackBy]?.toString() === evt.target.value);\n handleSelect(selectedOption);\n return;\n }\n\n // Spread the HTMLCollection (https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) to array so we can invoke `find`\n const selectedOptions = [...evt.target.selectedOptions];\n\n // If it's a multiselect, the HTML select used for the screen reader can have multiple items\n // deselected and a new one selected in one change event.\n // We need to reconcile the new selected options with our internal state\n\n // 1. remove all the items in our internal state that are no longer present\n // (Spread this array so we can remove items from it while iterating)\n for (const internalOption of [...internalValue.value]) {\n const selectedOption = selectedOptions.find((opt) => opt.value === internalOption[props.trackBy]?.toString());\n\n if (!selectedOption) {\n remove(internalOption);\n }\n }\n\n // 2. add all the items that are now selected if needed\n for (const selectedOption of selectedOptions) {\n const internalOption = internalOptions.value.find(\n (opt) => opt[props.trackBy]?.toString() === selectedOption.value,\n );\n\n if (isSelected(internalOption)) {\n continue;\n } else {\n handleSelect(internalOption);\n }\n }\n };\n\n /**\n * Removes (or un-selects) an option.\n * @param {Option} option The option to be removed\n */\n const remove = (option: Option) => {\n if (props.disabled || (shouldPreventEmpty.value && internalValue.value.length === 1)) {\n return;\n }\n\n const index = internalValue.value.findIndex((opt) => opt[props.trackBy] === option[props.trackBy]);\n\n if (index === -1) {\n logger.warn('ll-select: could not find option to remove', option);\n return;\n }\n\n internalValue.value.splice(index, 1);\n emit('update:model-value', props.single ? internalValue.value[0] : internalValue.value);\n emit('remove', option, index);\n };\n\n /**\n * Opens the `options` drawer and provides focus.\n */\n const open = () => {\n if (isActive.value) {\n return;\n }\n\n updateChipsHeight();\n\n isActive.value = true;\n emit('opened');\n\n if (isSearchable.value) {\n nextTick(() => {\n searchRef.value?.focus({ preventScroll: true });\n });\n }\n };\n\n /**\n * Closes the drawer and cleans up a few settings.\n */\n const dismiss = () => {\n if (isActive.value) {\n emit('closed', internalValue.value);\n }\n\n setIndex(-1);\n isActive.value = false;\n\n if (!props.preserveSearchTerm) {\n searchTerm.value = '';\n }\n\n // The optional chaining here is required.\n // There are cases when the component is removed from the DOM and the ref is lost.\n // .ie when the component is used in a modal and the modal is dismissed.\n // Tracked in this ticket: F-1893\n contentRef.value?.blur(); // if the component remains in focus, clicking it again will not open the options list\n };\n\n /**\n * Scroll list items when up and down keys are pressed\n * @param {KeyboardEvent} e The native keydown event.\n */\n const scrollItems = async (e: KeyboardEvent) => {\n await nextTick();\n\n if (!contentRef.value) {\n return;\n }\n\n const allowedKeyCodes = [KEY_CODES.ENTER, KEY_CODES.ESCAPE] as number[];\n\n // Wait for highlighted item update after using keyboard (`onKeyDown`)\n if (!allowedKeyCodes.includes(e.keyCode)) {\n const el = contentRef.value.querySelector('.stash-select__option--highlighted');\n const elBCR = el?.getBoundingClientRect();\n const parentBCR = contentRef.value.getBoundingClientRect();\n\n if (!elBCR || !parentBCR) {\n return;\n }\n\n if (elBCR.bottom >= parentBCR.bottom || elBCR.top <= parentBCR.top + elBCR.height) {\n el?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n }\n }\n };\n\n /**\n * Sets the `activeIndex` on hover, so that the user may then fine-tune\n * their selection with the arrow keys.\n */\n const setIndex = (index: number) => {\n activeIndex.value = index;\n };\n\n /**\n * Handles all user keyboard input on the Dropdown.\n * @param {KeyboardEvent} e The native keydown event.\n */\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.keyCode === KEY_CODES.ESCAPE) {\n dismiss();\n } else if (e.keyCode === KEY_CODES.DOWN && activeIndex.value < filteredOptions.value.length - 1 && isActive.value) {\n activeIndex.value++;\n } else if (e.keyCode === KEY_CODES.UP && activeIndex.value > 0) {\n if (isActive.value) {\n activeIndex.value--;\n }\n\n // Scroll to top when search is available and first element is highlighted (Using 100 to be sure that it'll be sure that search is visible)\n if (optionsWrapperRef.value && isSearchable.value && activeIndex.value === 0) {\n optionsWrapperRef.value.scrollTop -= 100;\n }\n } else if (e.keyCode === KEY_CODES.ENTER && activeIndex.value !== -1 && isActive.value) {\n handleSelect(filteredOptions.value[activeIndex.value]);\n } else {\n return;\n }\n\n e.preventDefault(); // if keyDown did something / we didn't return\n scrollItems(e);\n };\n\n const handleSearchInput = debounce(function () {\n search();\n }, DEBOUNCE.FAST);\n\n const search = async function () {\n if (!props.onSearch) {\n return;\n }\n\n const requestId = uniqueId('search-request-');\n\n try {\n pendingSearchRequests.value[requestId] = true;\n await props.onSearch(searchTerm.value);\n } finally {\n delete pendingSearchRequests.value[requestId];\n }\n };\n\n /**\n * Dynamically sets classes on an `option` in the drawer.\n * @param {Option} option The `option` for which to generate classes\n * @param {number} index The index of the `option` in the list of `options`\n */\n const optionClass = (option: Option, index: number) => {\n return [\n {\n 'stash-select__option': true,\n 'stash-select__option--disabled':\n option && typeof option === 'object' && 'disabled' in option && option?.disabled,\n 'stash-select__option--highlighted': activeIndex.value === index,\n 'stash-select__option--selected': isSelected(option),\n },\n\n // @deprecated\n {\n 'tw-bg-ice-200': activeIndex.value === index,\n 'tw-bg-blue-100 tw-text-ice-700': isSelected(option),\n },\n ];\n };\n\n /**\n * Finds out if the given item is already present in the selected options\n * @param {Option} option The item to check\n * @returns {boolean} Returns true if element is selected\n */\n const isSelected = (option?: Option | null): boolean => {\n if (option === undefined || option === null) {\n return false;\n }\n\n return internalValue.value.some((value) => value?.[props.trackBy] === option[props.trackBy]);\n };\n\n /**\n * Checks if the element is within the Select menu by checking both the content and the teleportTo element\n */\n function isElementWithinSelectMenu(element: Node | null) {\n if (!element) {\n return false;\n }\n\n const isElementInMenu = contentRef.value?.contains(element);\n const isElementInTeleportedMenu = optionsWrapperRef.value?.contains(element);\n\n return isElementInMenu || isElementInTeleportedMenu;\n }\n\n function handleFocusOut(focusEvent: FocusEvent) {\n const nextFocusedElement = focusEvent.relatedTarget as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(nextFocusedElement);\n\n if (!isFocusedElementInMenu && nextFocusedElement) {\n dismiss();\n }\n }\n\n function handleOutsideClick(event: MouseEvent) {\n const target = event.target as Node | null;\n const isFocusedElementInMenu = isElementWithinSelectMenu(target);\n\n if (!isFocusedElementInMenu) {\n dismiss();\n }\n }\n\n /**\n * Handles the mouse leave of the select and clears item highlight\n */\n const handleMouseLeave = () => {\n setIndex(-1);\n };\n\n /**\n * Update height of chips container\n */\n const updateChipsHeight = async () => {\n // Wait for the DOM to update so we can get an accurate value for chips' clientHeight\n await nextTick();\n\n if (chipsRef.value && contentRef.value) {\n contentHeight.value = contentHeight.value === 0 ? contentRef.value.clientHeight : contentHeight.value;\n chipsHeight.value = chipsRef.value.clientHeight;\n }\n };\n\n onMounted(() => {\n if (attrs.onInput) {\n throw new Error('ll-select: use the @update:model-value event instead of @input.');\n }\n\n window.addEventListener('resize', updateChipsHeight);\n\n updateChipsHeight();\n });\n\n /**\n * Remove event listener to handle component width when resizing window\n */\n onUnmounted(() => {\n window.removeEventListener('resize', updateChipsHeight);\n });\n</script>\n\n<template>\n <Field\n v-bind=\"props\"\n class=\"input ll-select stash-select\"\n data-test=\"stash-select\"\n :class=\"attrs.class\"\n :error-text=\"props.errorText || props.error\"\n :hint-text=\"props.hintText || props.hint\"\n >\n <template v-if=\"props.isReadOnly\" #default=\"{ fieldId, labelId }\">\n <div class=\"tw-flex tw-h-input tw-items-center tw-text-sm\">\n <span :id=\"fieldId\" :aria-labelledby=\"labelId\" class=\"show-empty tw-h-min\">\n {{ readOnlyValue }}\n </span>\n </div>\n </template>\n <template v-else #default=\"{ fieldId, fieldErrorId, hasError }\">\n <!-- SCREEN READER ONLY -->\n <select\n :id=\"fieldId\"\n :aria-errormessage=\"fieldErrorId\"\n :aria-invalid=\"hasError\"\n class=\"tw-sr-only\"\n :disabled=\"props.disabled\"\n :multiple=\"!props.single\"\n :name=\"props.name\"\n @change=\"handleSelectChange\"\n >\n <option\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`srOnlyOption-${index}`\"\n :selected=\"isSelected(option)\"\n :value=\"option[props.trackBy]\"\n >\n {{ option[props.displayBy] || '' }}\n </option>\n\n <!-- Empty option to allow deselecting non-multiselects -->\n <option value=\"\"></option>\n </select>\n\n <!-- SELECT -->\n <div\n ref=\"selectRef\"\n v-clickoutside=\"handleOutsideClick\"\n role=\"listbox\"\n aria-hidden=\"true\"\n class=\"stash-select__content-wrapper\"\n :aria-controls=\"'listbox-' + fieldId\"\n :aria-expanded=\"isActive\"\n :aria-label=\"props.placeholder\"\n :aria-disabled=\"props.disabled || undefined\"\n :class=\"[\n {\n 'stash-select--disabled': !!props.disabled,\n 'stash-select--error': !!(props.errorText || props.error),\n 'stash-select--active': isActive,\n 'stash-select--single': props.single,\n },\n\n // @deprecated\n {\n 'is-active': isActive,\n 'is-single': props.single,\n 'is-disabled': props.disabled,\n },\n ]\"\n @keydown=\"onKeyDown\"\n @keyup.esc=\"dismiss\"\n >\n <!-- CONTENT -->\n <div\n :id=\"'listbox-' + fieldId\"\n ref=\"contentRef\"\n class=\"input-field stash-select__content\"\n tabindex=\"0\"\n @focusin=\"open\"\n @focusin.once=\"shouldUseLazyOptions = true\"\n @focusout=\"handleFocusOut\"\n @keydown=\"open\"\n @mouseleave=\"handleMouseLeave\"\n >\n <!-- CHIPS -->\n <ul ref=\"chipsRef\" class=\"stash-select__chips\">\n <!-- PLACEHOLDER -->\n <li\n v-if=\"!internalValue.length\"\n class=\"stash-select__placeholder tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n {{ props.placeholder }}\n </li>\n\n <template v-if=\"props.single\">\n <li\n v-for=\"option of internalValue\"\n :key=\"`chip-${option[props.trackBy]}`\"\n class=\"stash-select__selected tw-mr-0 tw-pl-1.5\"\n :class=\"{ 'tw-truncate': !props.noTruncate }\"\n >\n <!-- @slot Selected value(s) custom text. Exposes the option object. -->\n <slot name=\"selected\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n <button\n tabindex=\"-1\"\n class=\"stash-select__remove\"\n @keypress.enter.prevent=\"remove(option)\"\n @mousedown.prevent.stop=\"remove(option)\"\n >\n <Icon icon=\"close\" name=\"close\" size=\"small\" />\n </button>\n </slot>\n </li>\n </template>\n\n <template v-else>\n <li v-for=\"option of internalValue\" :key=\"`chip-${option[props.trackBy]}`\" class=\"tw-inline-block\">\n <Chip\n bg-color=\"blue-500\"\n is-removable\n text-color=\"white\"\n class=\"stash-select__chip\"\n @remove=\"remove(option)\"\n >\n {{ option[props.displayBy] || option }}\n </Chip>\n </li>\n </template>\n </ul>\n\n <!-- TOTAL -->\n <div v-if=\"shouldShowTotal\" class=\"stash-select__total\">\n <Chip bg-color=\"blue-500\" is-removable text-color=\"white\" class=\"stash-select__chip\" @remove=\"clear\">\n {{ clearSelectionText }}\n </Chip>\n </div>\n\n <!-- TOGGLE -->\n <Icon\n v-if=\"props.icon\"\n class=\"stash-select__icon\"\n data-test=\"stash-select|toggle-icon\"\n :name=\"props.icon as IconName\"\n :class=\"{ 'tw-text-ice-500': props.disabled }\"\n @mousedown.prevent=\"isActive && dismiss()\"\n />\n <!-- DRAWER -->\n <Teleport :to=\"props.teleportTo\" :disabled=\"!props.enableTeleport\">\n <transition name=\"fade\">\n <div\n v-show=\"isActive && !props.disabled\"\n ref=\"optionsWrapperRef\"\n class=\"stash-select__border-selector tw-w-full tw-shadow-2xl\"\n :style=\"floatingStyles\"\n @click.stop\n >\n <!-- SEARCH -->\n <div v-if=\"isSearchable\" class=\"tw-flex tw-items-center tw-border-b tw-border-blue-500 tw-pr-1.5\">\n <input\n ref=\"searchRef\"\n v-model=\"searchTerm\"\n type=\"text\"\n autocomplete=\"off\"\n class=\"stash-select__search\"\n :data-test=\"attrs['data-test'] ? attrs['data-test'] + '-search' : 'stash-select|search'\"\n :placeholder=\"props.searchPlaceholder\"\n :spellcheck=\"false\"\n @input=\"handleSearchInput\"\n />\n <Icon name=\"search\" class=\"tw-text-ice-500\" />\n </div>\n\n <!-- OPTIONS -->\n <ul class=\"stash-select__options options tw-my-1.5 tw-w-full tw-border-white tw-bg-white\">\n <li\n v-for=\"(option, index) in filteredOptions\"\n :key=\"`option-${option[props.trackBy]}`\"\n :data-test=\"option[props.trackBy]\"\n :class=\"optionClass(option, index)\"\n @click=\"handleSelect(option)\"\n @mouseenter.self=\"setIndex(index)\"\n >\n <!-- @slot Select custom option text. Exposes the option object. -->\n <slot name=\"option\" :option=\"option\">\n {{ option[props.displayBy] || option }}\n </slot>\n\n <Icon\n v-if=\"isSelected(option) && !props.hideCheck\"\n class=\"tw-ml-auto tw-text-blue-500\"\n name=\"check\"\n />\n </li>\n <li v-show=\"props.loading || isSearching\" class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\">\n <Icon\n data-test=\"stash-select|options-loading\"\n name=\"working\"\n class=\"tw-animate-spin tw-text-ice-500\"\n />\n </li>\n <li\n v-show=\"!props.loading && !isSearching && !filteredOptions.length\"\n class=\"tw-m-1.5 tw-cursor-default tw-p-1.5\"\n data-test=\"stash-select|no-options\"\n >\n <!-- @slot No options text. -->\n <slot name=\"no-options\"> No options </slot>\n </li>\n </ul>\n </div>\n </transition>\n </Teleport>\n </div>\n </div>\n </template>\n <template v-if=\"slots.hint\" #hint>\n <slot name=\"hint\"></slot>\n </template>\n </Field>\n</template>\n\n<style scoped>\n .stash-select {\n position: relative;\n }\n\n .stash-select__content,\n .stash-select__content-wrapper {\n height: theme('height.input');\n }\n\n .stash-select__content {\n cursor: pointer;\n overflow: hidden;\n position: absolute;\n background: theme('colors.white');\n border: 1px solid var(--color-ice-500);\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n outline: none;\n padding: 0 !important;\n width: 100%;\n\n &:hover {\n border-color: var(--color-ice-500);\n }\n\n &:focus-within {\n border-color: var(--color-blue-500) !important;\n }\n }\n\n .stash-select--disabled .stash-select__content {\n background-color: var(--color-ice-100);\n border-color: var(--color-ice-500);\n color: var(--color-ice-500);\n pointer-events: none !important;\n }\n\n .stash-select--active .stash-select__content {\n cursor: default;\n height: auto;\n min-width: theme('width.select-menu');\n overflow: visible;\n z-index: theme('zIndex.control');\n box-shadow: theme('boxShadow.DEFAULT');\n }\n\n .stash-select--error :where(.stash-select__content, .stash-select__content:hover:not(:focus)) {\n border-color: var(--color-red-500) !important;\n }\n\n .stash-select__placeholder {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__placeholder {\n cursor: default;\n }\n\n .stash-select__icon {\n color: var(--color-ice-700);\n cursor: pointer;\n pointer-events: none;\n position: absolute;\n right: theme('spacing[1.5]');\n top: theme('spacing[1.5]');\n }\n\n .stash-select--disabled .stash-select__icon {\n color: var(--color-ice-500);\n }\n\n .stash-select--active .stash-select__icon {\n pointer-events: auto;\n }\n\n .stash-select__chips {\n /* distribute (or split?) the margin between chip and chips container. TODO? */\n --half-space: calc(theme('spacing[1.5]') / 2);\n\n padding: var(--half-space);\n padding-right: theme('spacing.9');\n }\n\n /* chip AND placeholder AND selected: */\n .stash-select__chips > li {\n cursor: pointer;\n height: theme('height.chip');\n margin: var(--half-space);\n max-width: 100%;\n }\n\n .stash-select--disabled .stash-select__chips > li {\n cursor: default;\n }\n\n .stash-select__content-wrapper .stash-select__chip {\n border-radius: theme('borderRadius.DEFAULT');\n font-size: theme('fontSize.sm');\n font-weight: normal;\n height: theme('height.chip');\n max-width: inherit;\n text-transform: none;\n }\n\n .stash-select--disabled .stash-select__chip {\n background: var(--color-ice-500);\n color: theme('colors.white');\n }\n\n .stash-select__content-wrapper .stash-select__total .stash-select__chip {\n font-weight: theme('fontWeight.normal');\n }\n\n .stash-select__selected {\n color: var(--color-ice-700);\n }\n\n .stash-select--disabled .stash-select__selected {\n color: var(--color-ice-500);\n }\n\n .stash-select__selected :where(.stash-select__remove) {\n display: none;\n }\n\n .stash-select__total {\n display: block;\n font-weight: theme('fontWeight.semibold');\n line-height: theme('lineHeight.button');\n position: absolute;\n right: theme('spacing.6');\n white-space: nowrap;\n z-index: theme('zIndex.content');\n\n /* -- design TBD --- */\n top: 0;\n left: 0;\n bottom: 0;\n color: theme('colors.white');\n background: white;\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__search {\n position: relative;\n width: 100%;\n height: theme('height.input');\n background: theme('colors.white');\n border-radius: theme('borderRadius.DEFAULT');\n color: var(--color-ice-700);\n display: block;\n font-family: theme('fontFamily.sofia');\n font-size: theme('fontSize.sm');\n font-weight: theme('fontWeight.normal');\n outline: none;\n padding: 0 theme('spacing.3');\n border: 0 !important;\n }\n\n .stash-select__border-selector {\n background: white;\n border-radius: theme('borderRadius.DEFAULT');\n border: 1px solid var(--color-blue-500);\n max-height: theme('maxHeight.select-menu');\n overflow: auto;\n z-index: theme('zIndex.dialog');\n }\n\n .stash-select__option {\n align-items: center;\n border-radius: theme('borderRadius.DEFAULT');\n cursor: pointer;\n display: flex;\n line-height: theme('spacing.6');\n margin: theme('spacing[1.5]');\n padding: theme('spacing[1.5]');\n }\n\n .stash-select__option--disabled {\n color: var(--color-ice-500);\n cursor: not-allowed;\n }\n\n .stash-select__option--highlighted {\n background-color: var(--color-ice-200);\n }\n\n .stash-select__option--selected {\n background-color: var(--color-blue-100);\n color: var(--color-ice-700);\n }\n</style>\n"],"names":["props","__props","emit","__emit","attrs","useAttrs","slots","useSlots","selectRef","ref","contentRef","optionsWrapperRef","chipsRef","searchRef","chipsHeight","contentHeight","internalValue","searchFor","searchTerm","activeIndex","isActive","pendingSearchRequests","shouldUseLazyOptions","floatingStyles","useFloating","autoUpdate","flip","offset","rects","size","availableHeight","elements","isSearchable","computed","internalOptions","OPTIONS","isPlainObject","name","id","readOnlyValue","option","isSearching","filteredOptions","isChipsOneLine","shouldShowTotal","clearSelectionText","shouldPreventEmpty","watch","currentValue","oldValue","setIndex","updateChipsHeight","value","newValue","isEqual","isEmpty","searchForFn","useSearch","clear","handleSelect","isSelected","remove","dismiss","handleSelectChange","evt","selectedOption","opt","_a","selectedOptions","internalOption","index","logger","open","nextTick","scrollItems","KEY_CODES","el","elBCR","parentBCR","onKeyDown","handleSearchInput","debounce","search","DEBOUNCE","requestId","uniqueId","optionClass","isElementWithinSelectMenu","element","isElementInMenu","isElementInTeleportedMenu","_b","handleFocusOut","focusEvent","nextFocusedElement","handleOutsideClick","event","target","handleMouseLeave","onMounted","onUnmounted"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
- import { defineComponent as g, useAttrs as S, useCssModule as C, computed as u, openBlock as c, createBlock as O, normalizeClass as l, unref as V, withCtx as r, createElementBlock as i, createElementVNode as a, toDisplayString as n, createVNode as m, normalizeStyle as f } from "vue";
2
- import y from "./Icon.js";
1
+ import { defineComponent as S, useAttrs as g, useCssModule as C, computed as u, openBlock as c, createBlock as O, normalizeClass as l, unref as V, withCtx as r, createElementBlock as i, createElementVNode as a, toDisplayString as n, createVNode as m, normalizeStyle as f } from "vue";
2
+ import p from "./Icon.js";
3
3
  import $ from "./Select.js";
4
4
  import { _ as E } from "./_plugin-vue_export-helper-CHgC5LLL.js";
5
5
  const N = { class: "tw-mr-6 tw-truncate" }, D = {
6
6
  key: 1,
7
7
  class: "tw-flex tw-items-center",
8
8
  "data-test": "select-status|value"
9
- }, I = { class: "tw-ml-1.5 tw-mr-6 tw-truncate" }, L = { class: "tw-flex tw-min-w-0" }, M = /* @__PURE__ */ g({
9
+ }, I = { class: "tw-ml-1.5 tw-mr-6 tw-truncate" }, L = { class: "tw-flex tw-min-w-0" }, M = /* @__PURE__ */ S({
10
10
  name: "ll-select-status",
11
11
  __name: "SelectStatus",
12
12
  props: {
@@ -22,13 +22,13 @@ const N = { class: "tw-mr-6 tw-truncate" }, D = {
22
22
  label: { default: void 0 }
23
23
  },
24
24
  emits: ["update:model-value"],
25
- setup(p, { emit: w }) {
26
- const _ = ["green", "teal", "ice", "yellow", "seafoam", "seafoam-700"], h = S(), v = C(), t = p;
25
+ setup(w, { emit: y }) {
26
+ const _ = ["green", "teal", "ice", "yellow", "seafoam", "seafoam-700"], h = g(), v = C(), t = w;
27
27
  if (t.value)
28
28
  throw new Error("ll-select-status: use :model-value or v-model instead of :value.");
29
29
  if (h.onInput)
30
30
  throw new Error("ll-select-status: use the @update:model-value event instead of @input");
31
- const x = w, o = u(() => t.statusOptions.find((e) => e[t.trackBy] === t.modelValue)), B = u(() => {
31
+ const x = y, o = u(() => t.statusOptions.find((e) => e[t.trackBy] === t.modelValue)), B = u(() => {
32
32
  const { color: e = "" } = o.value || {};
33
33
  return _.includes(e);
34
34
  });
@@ -41,7 +41,7 @@ const N = { class: "tw-mr-6 tw-truncate" }, D = {
41
41
  function k(e) {
42
42
  x("update:model-value", (e == null ? void 0 : e[t.trackBy]) ?? "");
43
43
  }
44
- return (e, A) => (c(), O($, {
44
+ return (e, F) => (c(), O($, {
45
45
  ref: "select",
46
46
  "hide-search": "",
47
47
  single: "",
@@ -70,7 +70,7 @@ const N = { class: "tw-mr-6 tw-truncate" }, D = {
70
70
  }, [
71
71
  a("span", N, n(s[t.displayBy]), 1)
72
72
  ], 2)) : (c(), i("div", D, [
73
- m(y, {
73
+ m(p, {
74
74
  "data-test": "select-status|value-icon",
75
75
  name: s.icon || "circle-status",
76
76
  class: l([
@@ -84,7 +84,7 @@ const N = { class: "tw-mr-6 tw-truncate" }, D = {
84
84
  ]),
85
85
  option: r(({ option: s }) => [
86
86
  a("div", L, [
87
- m(y, {
87
+ m(p, {
88
88
  "data-test": "svg|status-color",
89
89
  name: s.icon || "circle-status",
90
90
  class: l([[
@@ -102,13 +102,13 @@ const N = { class: "tw-mr-6 tw-truncate" }, D = {
102
102
  _: 1
103
103
  }, 8, ["class", "disabled", "icon", "options", "placeholder", "track-by", "model-value", "label"]));
104
104
  }
105
- }), T = "_select_1gtqy_27", q = {
105
+ }), T = "_select_f7xpn_27", z = {
106
106
  select: T,
107
- "stash-select__chips": "_stash-select__chips_1gtqy_32"
108
- }, z = {
109
- $style: q
110
- }, j = /* @__PURE__ */ E(M, [["__cssModules", z]]);
107
+ "stash-select__chips": "_stash-select__chips_f7xpn_32"
108
+ }, A = {
109
+ $style: z
110
+ }, q = /* @__PURE__ */ E(M, [["__cssModules", A]]);
111
111
  export {
112
- j as default
112
+ q as default
113
113
  };
114
114
  //# sourceMappingURL=SelectStatus.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SelectStatus.js","sources":["../src/components/SelectStatus/SelectStatus.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, useAttrs, useCssModule } from 'vue';\n\n import { StashCommonColor, StashPrimaryColorGroup } from '../../../types';\n import { IconName } from '../Icon/Icon.types';\n import Icon from '../Icon/Icon.vue';\n import Select from '../Select/Select.vue';\n\n defineOptions({\n name: 'll-select-status',\n });\n\n export type SelectStatusOption = {\n id: number | string | boolean;\n name: string;\n color?: StashPrimaryColorGroup | StashCommonColor; // todo: just `StashPrimaryColor`?.\n icon?: IconName;\n };\n\n export interface SelectStatusProps {\n /**\n * Disables the component, if true\n */\n disabled?: boolean;\n /**\n * Which key of the prop option to use for display\n */\n displayBy?: string;\n /**\n * Sets the background color of the component to the color of the selected option\n */\n secondary?: boolean;\n /**\n * Allows option text to wrap to next line when set to true\n */\n noTruncate?: boolean;\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * The list of all options to select the status from\n * each object must have the following structure: { id: number | string, name: string, color?: string }\n */\n statusOptions: Array<SelectStatusOption>;\n /**\n * Default field to track the selected status by\n */\n trackBy?: string;\n /**\n * Sets the currently-selected option for the component\n */\n modelValue: number | string | boolean;\n /**\n * @deprecated Use `:model-value` or `v-model` instead of `:value`.\n */\n value?: number | string | boolean | null;\n\n /**\n * Shows a label above the select\n */\n label?: string;\n }\n\n /**\n * Array containing the available dark fill colors that need a different text color.\n */\n const DARK_FILL_COLORS = ['green', 'teal', 'ice', 'yellow', 'seafoam', 'seafoam-700'];\n\n const attrs = useAttrs();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<SelectStatusProps>(), {\n disabled: false,\n displayBy: 'name',\n secondary: false,\n noTruncate: false,\n placeholder: 'Select Status',\n trackBy: 'id',\n value: null,\n label: undefined,\n });\n\n if (props.value) {\n throw new Error('ll-select-status: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-select-status: use the @update:model-value event instead of @input');\n }\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes\n */\n (e: 'update:model-value', value: unknown): void;\n }>();\n\n const valueOption = computed(() => {\n return props.statusOptions.find((status) => status[props.trackBy] === props.modelValue);\n });\n\n const hasDarkFill = computed(() => {\n const { color = '' } = valueOption.value || {};\n return DARK_FILL_COLORS.includes(color);\n });\n\n /**\n * @param {string} color Valid color name\n * @return {string} tailwind class for given color\n */\n function getBgColor(color: SelectStatusOption['color']): string {\n // if there's no hyphen then it's a color name and has no shade, so we default to 500\n return color?.includes('-') ? `tw-bg-${color}` : `tw-bg-${color}-500`;\n }\n\n /**\n * @param {string} color Valid color name\n * @return {string} tailwind class for given color\n */\n function getTextColor(color: SelectStatusOption['color']): string {\n // if there's no hyphen then it's a color name and has no shade, so we default to 500\n return color?.includes('-') ? `tw-text-${color}` : `tw-text-${color}-500`;\n }\n\n function onSelect(value?: SelectStatusOption): void {\n emit('update:model-value', value?.[props.trackBy] ?? '');\n }\n</script>\n\n<template>\n <Select\n ref=\"select\"\n hide-search\n single\n :class=\"[\n classes.select,\n {\n 'stash-select-status--filled': props.secondary,\n 'stash-select-status--filled-dark': props.secondary && hasDarkFill,\n },\n ]\"\n :disabled=\"props.disabled\"\n :icon=\"!props.disabled && 'caret-down'\"\n :options=\"props.statusOptions\"\n :placeholder=\"props.placeholder\"\n :track-by=\"props.trackBy\"\n :model-value=\"valueOption\"\n :label=\"props.label\"\n data-test=\"stash-select-status\"\n class=\"stash-select-status\"\n @update:model-value=\"onSelect\"\n >\n <template #selected=\"{ option }\">\n <div\n v-if=\"props.secondary\"\n class=\"stash-select-status--selected tw-absolute tw-inset-0 tw-flex tw-h-full tw-w-full tw-items-center tw-rounded tw-pl-3\"\n data-test=\"select-status|value\"\n :class=\"[getBgColor(option.color)]\"\n >\n <span class=\"tw-mr-6 tw-truncate\">{{ option[props.displayBy] }}</span>\n </div>\n\n <div v-else class=\"tw-flex tw-items-center\" data-test=\"select-status|value\">\n <Icon\n data-test=\"select-status|value-icon\"\n :name=\"option.icon || 'circle-status'\"\n :class=\"[\n { [getTextColor(option.color)]: option.color },\n { 'tw-text-ice-700': !option.color && !option.hexCode },\n ]\"\n :style=\"{ color: option.hexCode }\"\n />\n <span class=\"tw-ml-1.5 tw-mr-6 tw-truncate\">{{ option[props.displayBy] }}</span>\n </div>\n </template>\n\n <template #option=\"{ option }\">\n <div class=\"tw-flex tw-min-w-0\">\n <Icon\n data-test=\"svg|status-color\"\n :name=\"option.icon || 'circle-status'\"\n :class=\"[\n { [getTextColor(option.color)]: option.color },\n { 'tw-text-ice-700': !option.color && !option.hexCode },\n ]\"\n class=\"!tw-shrink-0\"\n :style=\"{ color: option.hexCode }\"\n />\n <span data-test=\"span|option-text\" class=\"tw-ml-1.5\" :class=\"!props.noTruncate && 'tw-truncate'\">\n {{ option[props.displayBy] }}\n </span>\n </div>\n </template>\n </Select>\n</template>\n\n<style module>\n :global(.stash-select-status--filled .stash-select__content) {\n border: none;\n padding: 0;\n }\n\n :global(\n .stash-select-status--filled\n .stash-select--active\n .stash-select__content:focus-within\n .stash-select__border-selector\n ) {\n box-sizing: content-box;\n max-width: calc(100% - 2px);\n }\n\n :global(.stash-select-status--filled .stash-select-status--selected),\n :global(.stash-select-status--filled :has(.stash-select-status--selected) .icon--caret-down) {\n color: theme('colors.white');\n }\n\n :global(.stash-select-status--filled-dark .stash-select-status--selected),\n :global(.stash-select-status--filled-dark :has(.stash-select-status--selected) .icon--caret-down) {\n color: var(--color-ice-900);\n }\n\n .select :global(.stash-select--disabled > .stash-select__content) {\n background-color: transparent;\n border-color: transparent;\n }\n\n .select :deep(.stash-select__chips) {\n margin: 0;\n }\n\n .select :global(.stash-select__options) {\n margin: 0;\n padding: 0;\n }\n\n .select ul {\n position: relative;\n }\n</style>\n"],"names":["DARK_FILL_COLORS","attrs","useAttrs","classes","useCssModule","props","__props","emit","__emit","valueOption","computed","status","hasDarkFill","color","getBgColor","getTextColor","onSelect","value"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmEE,UAAMA,IAAmB,CAAC,SAAS,QAAQ,OAAO,UAAU,WAAW,aAAa,GAE9EC,IAAQC,EAAS,GACjBC,IAAUC,EAAa,GAEvBC,IAAQC;AAWd,QAAID,EAAM;AACF,YAAA,IAAI,MAAM,kEAAkE;AAGpF,QAAIJ,EAAM;AACF,YAAA,IAAI,MAAM,uEAAuE;AAGzF,UAAMM,IAAOC,GAOPC,IAAcC,EAAS,MACpBL,EAAM,cAAc,KAAK,CAACM,MAAWA,EAAON,EAAM,OAAO,MAAMA,EAAM,UAAU,CACvF,GAEKO,IAAcF,EAAS,MAAM;AACjC,YAAM,EAAE,OAAAG,IAAQ,GAAO,IAAAJ,EAAY,SAAS,CAAC;AACtC,aAAAT,EAAiB,SAASa,CAAK;AAAA,IAAA,CACvC;AAMD,aAASC,EAAWD,GAA4C;AAEvD,aAAAA,KAAA,QAAAA,EAAO,SAAS,OAAO,SAASA,CAAK,KAAK,SAASA,CAAK;AAAA,IAAA;AAOjE,aAASE,EAAaF,GAA4C;AAEzD,aAAAA,KAAA,QAAAA,EAAO,SAAS,OAAO,WAAWA,CAAK,KAAK,WAAWA,CAAK;AAAA,IAAA;AAGrE,aAASG,EAASC,GAAkC;AAClD,MAAAV,EAAK,uBAAsBU,KAAA,gBAAAA,EAAQZ,EAAM,aAAY,EAAE;AAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"SelectStatus.js","sources":["../src/components/SelectStatus/SelectStatus.vue"],"sourcesContent":["<script setup lang=\"ts\">\n import { computed, useAttrs, useCssModule } from 'vue';\n\n import { StashCommonColor, StashPrimaryColorGroup } from '../../../types';\n import { IconName } from '../Icon/Icon.types';\n import Icon from '../Icon/Icon.vue';\n import Select from '../Select/Select.vue';\n\n defineOptions({\n name: 'll-select-status',\n });\n\n export type SelectStatusOption = {\n id: number | string | boolean;\n name: string;\n color?: StashPrimaryColorGroup | StashCommonColor; // todo: just `StashPrimaryColor`?.\n icon?: IconName;\n };\n\n export interface SelectStatusProps {\n /**\n * Disables the component, if true\n */\n disabled?: boolean;\n /**\n * Which key of the prop option to use for display\n */\n displayBy?: string;\n /**\n * Sets the background color of the component to the color of the selected option\n */\n secondary?: boolean;\n /**\n * Allows option text to wrap to next line when set to true\n */\n noTruncate?: boolean;\n /**\n * Placeholder text\n */\n placeholder?: string;\n /**\n * The list of all options to select the status from\n * each object must have the following structure: { id: number | string, name: string, color?: string }\n */\n statusOptions: Array<SelectStatusOption>;\n /**\n * Default field to track the selected status by\n */\n trackBy?: string;\n /**\n * Sets the currently-selected option for the component\n */\n modelValue: number | string | boolean;\n /**\n * @deprecated Use `:model-value` or `v-model` instead of `:value`.\n */\n value?: number | string | boolean | null;\n\n /**\n * Shows a label above the select\n */\n label?: string;\n }\n\n /**\n * Array containing the available dark fill colors that need a different text color.\n */\n const DARK_FILL_COLORS = ['green', 'teal', 'ice', 'yellow', 'seafoam', 'seafoam-700'];\n\n const attrs = useAttrs();\n const classes = useCssModule();\n\n const props = withDefaults(defineProps<SelectStatusProps>(), {\n disabled: false,\n displayBy: 'name',\n secondary: false,\n noTruncate: false,\n placeholder: 'Select Status',\n trackBy: 'id',\n value: null,\n label: undefined,\n });\n\n if (props.value) {\n throw new Error('ll-select-status: use :model-value or v-model instead of :value.');\n }\n\n if (attrs.onInput) {\n throw new Error('ll-select-status: use the @update:model-value event instead of @input');\n }\n\n const emit = defineEmits<{\n /**\n * Emitted when the model value changes\n */\n (e: 'update:model-value', value: unknown): void;\n }>();\n\n const valueOption = computed(() => {\n return props.statusOptions.find((status) => status[props.trackBy] === props.modelValue);\n });\n\n const hasDarkFill = computed(() => {\n const { color = '' } = valueOption.value || {};\n return DARK_FILL_COLORS.includes(color);\n });\n\n /**\n * @param {string} color Valid color name\n * @return {string} tailwind class for given color\n */\n function getBgColor(color: SelectStatusOption['color']): string {\n // if there's no hyphen then it's a color name and has no shade, so we default to 500\n return color?.includes('-') ? `tw-bg-${color}` : `tw-bg-${color}-500`;\n }\n\n /**\n * @param {string} color Valid color name\n * @return {string} tailwind class for given color\n */\n function getTextColor(color: SelectStatusOption['color']): string {\n // if there's no hyphen then it's a color name and has no shade, so we default to 500\n return color?.includes('-') ? `tw-text-${color}` : `tw-text-${color}-500`;\n }\n\n function onSelect(value?: SelectStatusOption): void {\n emit('update:model-value', value?.[props.trackBy] ?? '');\n }\n</script>\n\n<template>\n <Select\n ref=\"select\"\n hide-search\n single\n :class=\"[\n classes.select,\n {\n 'stash-select-status--filled': props.secondary,\n 'stash-select-status--filled-dark': props.secondary && hasDarkFill,\n },\n ]\"\n :disabled=\"props.disabled\"\n :icon=\"!props.disabled && 'caret-down'\"\n :options=\"props.statusOptions\"\n :placeholder=\"props.placeholder\"\n :track-by=\"props.trackBy\"\n :model-value=\"valueOption\"\n :label=\"props.label\"\n data-test=\"stash-select-status\"\n class=\"stash-select-status\"\n @update:model-value=\"onSelect\"\n >\n <template #selected=\"{ option }\">\n <div\n v-if=\"props.secondary\"\n class=\"stash-select-status--selected tw-absolute tw-inset-0 tw-flex tw-h-full tw-w-full tw-items-center tw-rounded tw-pl-3\"\n data-test=\"select-status|value\"\n :class=\"[getBgColor(option.color)]\"\n >\n <span class=\"tw-mr-6 tw-truncate\">{{ option[props.displayBy] }}</span>\n </div>\n\n <div v-else class=\"tw-flex tw-items-center\" data-test=\"select-status|value\">\n <Icon\n data-test=\"select-status|value-icon\"\n :name=\"option.icon || 'circle-status'\"\n :class=\"[\n { [getTextColor(option.color)]: option.color },\n { 'tw-text-ice-700': !option.color && !option.hexCode },\n ]\"\n :style=\"{ color: option.hexCode }\"\n />\n <span class=\"tw-ml-1.5 tw-mr-6 tw-truncate\">{{ option[props.displayBy] }}</span>\n </div>\n </template>\n\n <template #option=\"{ option }\">\n <div class=\"tw-flex tw-min-w-0\">\n <Icon\n data-test=\"svg|status-color\"\n :name=\"option.icon || 'circle-status'\"\n :class=\"[\n { [getTextColor(option.color)]: option.color },\n { 'tw-text-ice-700': !option.color && !option.hexCode },\n ]\"\n class=\"!tw-shrink-0\"\n :style=\"{ color: option.hexCode }\"\n />\n <span data-test=\"span|option-text\" class=\"tw-ml-1.5\" :class=\"!props.noTruncate && 'tw-truncate'\">\n {{ option[props.displayBy] }}\n </span>\n </div>\n </template>\n </Select>\n</template>\n\n<style module>\n :global(.stash-select-status--filled .stash-select__content) {\n border: none;\n padding: 0;\n }\n\n :global(\n .stash-select-status--filled\n .stash-select--active\n .stash-select__content:focus-within\n .stash-select__border-selector\n ) {\n box-sizing: content-box;\n max-width: calc(100% - 2px);\n }\n\n :global(.stash-select-status--filled .stash-select-status--selected),\n :global(.stash-select-status--filled :has(.stash-select-status--selected) .icon--caret-down) {\n color: theme('colors.white');\n }\n\n :global(.stash-select-status--filled-dark .stash-select-status--selected),\n :global(.stash-select-status--filled-dark :has(.stash-select-status--selected) .icon--caret-down) {\n color: var(--color-ice-900);\n }\n\n .select :global(.stash-select--disabled > .stash-select__content) {\n background-color: transparent;\n border-color: transparent;\n }\n\n .select :deep(.stash-select__chips) {\n margin: 0;\n }\n\n .select :global(.stash-select__options) {\n margin: 0;\n padding: 0;\n }\n\n .select ul {\n position: relative;\n }\n</style>\n"],"names":["DARK_FILL_COLORS","attrs","useAttrs","classes","useCssModule","props","__props","emit","__emit","valueOption","computed","status","hasDarkFill","color","getBgColor","getTextColor","onSelect","value"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmEE,UAAMA,IAAmB,CAAC,SAAS,QAAQ,OAAO,UAAU,WAAW,aAAa,GAE9EC,IAAQC,EAAS,GACjBC,IAAUC,EAAa,GAEvBC,IAAQC;AAWd,QAAID,EAAM;AACF,YAAA,IAAI,MAAM,kEAAkE;AAGpF,QAAIJ,EAAM;AACF,YAAA,IAAI,MAAM,uEAAuE;AAGzF,UAAMM,IAAOC,GAOPC,IAAcC,EAAS,MACpBL,EAAM,cAAc,KAAK,CAACM,MAAWA,EAAON,EAAM,OAAO,MAAMA,EAAM,UAAU,CACvF,GAEKO,IAAcF,EAAS,MAAM;AACjC,YAAM,EAAE,OAAAG,IAAQ,GAAO,IAAAJ,EAAY,SAAS,CAAC;AACtC,aAAAT,EAAiB,SAASa,CAAK;AAAA,IAAA,CACvC;AAMD,aAASC,EAAWD,GAA4C;AAEvD,aAAAA,KAAA,QAAAA,EAAO,SAAS,OAAO,SAASA,CAAK,KAAK,SAASA,CAAK;AAAA,IAAA;AAOjE,aAASE,EAAaF,GAA4C;AAEzD,aAAAA,KAAA,QAAAA,EAAO,SAAS,OAAO,WAAWA,CAAK,KAAK,WAAWA,CAAK;AAAA,IAAA;AAGrE,aAASG,EAASC,GAAkC;AAClD,MAAAV,EAAK,uBAAsBU,KAAA,gBAAAA,EAAQZ,EAAM,aAAY,EAAE;AAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -62,7 +62,12 @@ export default _default;
62
62
 
63
63
  declare type IconName = (typeof iconNames)[number];
64
64
 
65
- declare const iconNames: readonly ["action-dots", "activity", "alert-bell", "archive", "arrow-down", "arrow-left", "arrow-right", "arrow-up", "badge-discount", "badge-seller-elite", "badge-seller-power", "badge-seller-verified", "book-customer", "building-office", "bulk-add", "calendar-reschedule", "calendar", "camera", "caret-down", "caret-up", "change-log", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "circle-check", "circle-close", "circle-dollar", "circle-empty", "circle-info", "circle-partial", "circle-percent", "circle-question-mark", "circle-status", "circle-warning", "clipboard-checkmark", "clipboard-inventory", "close", "combine", "compass", "contact", "contract", "copy", "credit-card", "credit-profile", "dashboard", "document-accept", "document-invoice", "document-recieved", "document-sent", "document-view", "document", "dolly", "download", "edit", "ellipsis", "envelope-open", "envelope", "equals", "export", "figma", "file-csv", "file", "filter-funnel", "filter-line", "flag", "folder", "folder-bar-graph", "folder-orders", "font-bold", "font-clear-format", "font-italic", "font-underline", "gear", "github", "globe", "graph-bar-chart", "graph-line-chart", "graph-pie-chart", "hazard", "hazard-outline", "headset-agent", "headset-mic", "heart-filled", "heart-outline", "help-question-mark", "hide", "history", "home", "image", "import", "keyboard-return", "license-approved", "license-certificate", "lightbulb", "link-add", "link-unlink", "link", "list-bulleted", "list-items", "list-numbered", "loading-big", "loading-empty", "loading-small", "location", "lock-unlock", "lock", "logo-facebook", "logo-instagram", "logo-linkedin", "logo-ll", "logo-metrc", "logo-plaid", "logo-x", "logo-youtube", "logout", "medical", "megaphone-sound", "megaphone", "menu", "message-dispute", "message-reply", "message", "minus", "mj-leaf", "money", "note-add", "note", "open-in-new", "paperclip", "paper-plane", "performance", "phone", "plus", "preview", "print", "product-menu-manage", "product-menu-search", "product-menu", "queue-add", "queue", "recent", "refresh", "register", "reply", "report-download", "sample", "save", "scale-law", "scale-weight", "search", "seed-cycle", "share", "shop-bag-browse", "shop-bag-reorder", "shop-bag", "shop-basket", "shop-cart-add", "shop-cart", "show", "sign-dollar", "sign-percent", "sort", "split", "star-filled", "star-outline", "storefront", "submit", "swap-horizontal", "swap-vertical", "tag-star", "tag", "test-results", "ticket-star", "ticket", "tool-dropper", "tool-wrench", "transfer", "trashcan", "truck", "upload", "user-add", "user-admin", "user-check", "user", "view-card", "view-detailed", "view-list", "warehouse", "working"];
65
+ /**
66
+ * To add a new icon, follow these steps:
67
+ *
68
+ * https://github.com/LeafLink/stash/blob/main/CONTRIBUTING.md#adding-a-new-icon
69
+ */
70
+ declare const iconNames: readonly ["action-dots", "activity", "alert-bell", "archive", "arrow-down", "arrow-left", "arrow-right", "arrow-up", "badge-discount", "badge-seller-elite", "badge-seller-power", "badge-seller-verified", "book-customer", "building-office", "bulk-add", "calendar-reschedule", "calendar", "camera", "caret-down", "caret-up", "change-log", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "circle-check", "circle-close", "circle-dollar", "circle-empty", "circle-info", "circle-partial", "circle-percent", "circle-question-mark", "circle-status", "circle-warning", "clipboard-checkmark", "clipboard-inventory", "close", "combine", "compass", "contact", "contract", "copy", "credit-card", "credit-profile", "dashboard", "document-accept", "document-invoice", "document-recieved", "document-sent", "document-view", "document", "dolly", "download", "edit", "ellipsis", "envelope-open", "envelope", "equals", "export", "figma", "file-csv", "file", "filter-funnel", "filter-line", "flag", "folder", "folder-bar-graph", "folder-orders", "font-bold", "font-clear-format", "font-italic", "font-underline", "gear", "github", "globe", "graph-bar-chart", "graph-line-chart", "graph-pie-chart", "hazard", "hazard-outline", "headset-agent", "headset-mic", "heart-filled", "heart-outline", "help-question-mark", "hide", "history", "home", "image", "import", "keyboard-return", "tier-1", "tier-2", "tier-3", "license-approved", "license-certificate", "lightbulb", "link-add", "link-unlink", "link", "list-bulleted", "list-items", "list-numbered", "loading-big", "loading-empty", "loading-small", "location", "lock-unlock", "lock", "logo-facebook", "logo-instagram", "logo-linkedin", "logo-ll", "logo-metrc", "logo-plaid", "logo-x", "logo-youtube", "logout", "medical", "megaphone-sound", "megaphone", "menu", "message-dispute", "message-reply", "message", "minus", "mj-leaf", "money", "note-add", "note", "open-in-new", "paperclip", "paper-plane", "performance", "phone", "plus", "preview", "print", "product-menu-manage", "product-menu-search", "product-menu", "queue-add", "queue", "recent", "refresh", "register", "reply", "report-download", "sample", "save", "scale-law", "scale-weight", "search", "seed-cycle", "share", "shop-bag-browse", "shop-bag-reorder", "shop-bag", "shop-basket", "shop-cart-add", "shop-cart", "show", "sign-dollar", "sign-percent", "sort", "split", "star-filled", "star-outline", "storefront", "submit", "swap-horizontal", "swap-vertical", "tag-star", "tag", "test-results", "ticket-star", "ticket", "tool-dropper", "tool-wrench", "transfer", "trashcan", "truck", "upload", "user-add", "user-admin", "user-check", "user", "view-card", "view-detailed", "view-list", "warehouse", "working"];
66
71
 
67
72
  export declare type SelectStatusOption = {
68
73
  id: number | string | boolean;
package/dist/Step.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineComponent as x, useSlots as b, useCssModule as g, inject as y, computed as d, onBeforeMount as k, openBlock as i, createElementBlock as r, normalizeClass as a, unref as t, createElementVNode as n, createBlock as B, toDisplayString as w, createCommentVNode as u, createTextVNode as N, renderSlot as C } from "vue";
1
+ import { defineComponent as x, useSlots as g, useCssModule as y, inject as k, computed as d, onBeforeMount as b, openBlock as i, createElementBlock as r, normalizeClass as a, unref as t, createElementVNode as n, createBlock as B, toDisplayString as w, createCommentVNode as u, createTextVNode as N, renderSlot as C } from "vue";
2
2
  import { t as E } from "./locale.js";
3
3
  import T from "./Icon.js";
4
4
  import { S as z } from "./keys-C8Zfr_By.js";
@@ -15,11 +15,11 @@ const S = { key: 1 }, I = /* @__PURE__ */ x({
15
15
  disabled: { type: Boolean, default: !1 },
16
16
  loading: { type: Boolean, default: !1 }
17
17
  },
18
- setup(h) {
19
- const e = h, _ = b(), s = g(), l = y(z), c = d(() => (l == null ? void 0 : l.orientation) === "horizontal"), p = d(() => (l == null ? void 0 : l.theme) === "light"), o = l == null ? void 0 : l.showResponsiveNav, m = d(() => e.loading ? "working" : e.completed ? "circle-check" : "circle-info"), v = d(
18
+ setup(m) {
19
+ const e = m, _ = g(), s = y(), l = k(z), c = d(() => (l == null ? void 0 : l.orientation) === "horizontal"), p = d(() => (l == null ? void 0 : l.theme) === "light"), o = l == null ? void 0 : l.showResponsiveNav, v = d(() => e.loading ? "working" : e.completed ? "circle-check" : "circle-info"), h = d(
20
20
  () => E("ll.stepper.currentStep", { num: e.step, total: String(l == null ? void 0 : l.stepCount.value) })
21
21
  );
22
- return k(() => {
22
+ return b(() => {
23
23
  l && l.registerStep(e.nested);
24
24
  }), (f, K) => (i(), r("li", {
25
25
  class: a(["stash-step tw-relative tw-flex tw-w-full", [
@@ -52,7 +52,7 @@ const S = { key: 1 }, I = /* @__PURE__ */ x({
52
52
  }, [
53
53
  (e.completed || e.error || e.loading) && !e.active ? (i(), B(T, {
54
54
  key: 0,
55
- name: m.value,
55
+ name: v.value,
56
56
  class: a({ "fx-spin": e.loading }),
57
57
  size: "dense"
58
58
  }, null, 8, ["name", "class"])) : (i(), r("span", S, w(e.step), 1))
@@ -92,7 +92,7 @@ const S = { key: 1 }, I = /* @__PURE__ */ x({
92
92
  key: 0,
93
93
  class: a(["stash-step__pagination-label tw-mb-0 tw-whitespace-nowrap tw-text-center tw-text-xs", p.value ? "tw-text-ice-700" : "tw-text-white"]),
94
94
  "data-test": "stash-step|pagination-label"
95
- }, w(v.value), 3)) : u("", !0)
95
+ }, w(h.value), 3)) : u("", !0)
96
96
  ], 2),
97
97
  c.value ? (i(), r("div", {
98
98
  key: 0,
@@ -116,19 +116,19 @@ const S = { key: 1 }, I = /* @__PURE__ */ x({
116
116
  ], 2)
117
117
  ], 2));
118
118
  }
119
- }), R = "_step_1h5eb_2", V = "_title_1h5eb_11", P = "_line_1h5eb_22", $ = "_circle_1h5eb_62", D = "_nested_1h5eb_18", H = {
119
+ }), R = "_step_73127_2", V = "_title_73127_11", P = "_line_73127_22", $ = "_circle_73127_62", D = "_nested_73127_18", H = {
120
120
  step: R,
121
121
  title: V,
122
- "nested-step--active": "_nested-step--active_1h5eb_18",
122
+ "nested-step--active": "_nested-step--active_73127_18",
123
123
  line: P,
124
- "line--expanded": "_line--expanded_1h5eb_27",
125
- "stash-step--singular": "_stash-step--singular_1h5eb_31",
124
+ "line--expanded": "_line--expanded_73127_27",
125
+ "stash-step--singular": "_stash-step--singular_73127_31",
126
126
  circle: $,
127
- "circle--todo": "_circle--todo_1h5eb_73",
128
- "circle--active": "_circle--active_1h5eb_79",
129
- "circle--loading": "_circle--loading_1h5eb_85",
130
- "circle--completed": "_circle--completed_1h5eb_92",
131
- "circle--error": "_circle--error_1h5eb_99",
127
+ "circle--todo": "_circle--todo_73127_75",
128
+ "circle--active": "_circle--active_73127_81",
129
+ "circle--loading": "_circle--loading_73127_87",
130
+ "circle--completed": "_circle--completed_73127_94",
131
+ "circle--error": "_circle--error_73127_101",
132
132
  nested: D
133
133
  }, J = {
134
134
  $style: H