cisse-vue-ui 0.2.4 → 0.2.6

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 (60) hide show
  1. package/README.md +67 -1
  2. package/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-Do0H2ZOe.cjs → CollapsibleCard.vue_vue_type_script_setup_true_lang-CPV2dtkO.cjs} +67 -125
  3. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-CPV2dtkO.cjs.map +1 -0
  4. package/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-Cymj-zkh.js → CollapsibleCard.vue_vue_type_script_setup_true_lang-D0eSGYea.js} +68 -126
  5. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-D0eSGYea.js.map +1 -0
  6. package/dist/{Dropdown.vue_vue_type_script_setup_true_lang-D65uMijW.js → Dropdown.vue_vue_type_script_setup_true_lang-B9DsCY8M.js} +26 -74
  7. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-B9DsCY8M.js.map +1 -0
  8. package/dist/{Dropdown.vue_vue_type_script_setup_true_lang-H6wsySqi.cjs → Dropdown.vue_vue_type_script_setup_true_lang-nMP2OxXp.cjs} +25 -73
  9. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-nMP2OxXp.cjs.map +1 -0
  10. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-DoawksKc.cjs → PageLayout.vue_vue_type_script_setup_true_lang-Bnw5L-xO.cjs} +2 -2
  11. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-DoawksKc.cjs.map → PageLayout.vue_vue_type_script_setup_true_lang-Bnw5L-xO.cjs.map} +1 -1
  12. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-J1I-WjM-.js → PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js} +2 -2
  13. package/dist/{PageLayout.vue_vue_type_script_setup_true_lang-J1I-WjM-.js.map → PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js.map} +1 -1
  14. package/dist/{Switch.vue_vue_type_script_setup_true_lang-C2_5u-HL.js → Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js} +33 -103
  15. package/dist/Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js.map +1 -0
  16. package/dist/{Switch.vue_vue_type_script_setup_true_lang-V-FtNcTd.cjs → Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs} +32 -102
  17. package/dist/Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs.map +1 -0
  18. package/dist/components/core/Dropdown.vue.d.ts +2 -4
  19. package/dist/components/core/index.cjs +2 -2
  20. package/dist/components/core/index.js +2 -2
  21. package/dist/components/form/index.cjs +1 -1
  22. package/dist/components/form/index.js +1 -1
  23. package/dist/components/index.cjs +4 -4
  24. package/dist/components/index.js +4 -4
  25. package/dist/components/layout/index.cjs +1 -1
  26. package/dist/components/layout/index.js +1 -1
  27. package/dist/composables/index.cjs +8 -4
  28. package/dist/composables/index.cjs.map +1 -1
  29. package/dist/composables/index.d.ts +2 -0
  30. package/dist/composables/index.js +5 -1
  31. package/dist/composables/index.js.map +1 -1
  32. package/dist/composables/useDropdown.d.ts +51 -0
  33. package/dist/composables/useModal.d.ts +60 -0
  34. package/dist/{index-D_7WQIhA.cjs → index-C-qIi_nO.cjs} +5 -5
  35. package/dist/index-C-qIi_nO.cjs.map +1 -0
  36. package/dist/{index-CNQJxtkC.js → index-DUVvDjHF.js} +5 -5
  37. package/dist/index-DUVvDjHF.js.map +1 -0
  38. package/dist/index.cjs +13 -9
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.js +10 -6
  41. package/dist/index.js.map +1 -1
  42. package/dist/useDropdown-DHFnd259.cjs +130 -0
  43. package/dist/useDropdown-DHFnd259.cjs.map +1 -0
  44. package/dist/useDropdown-iVu14E6s.js +131 -0
  45. package/dist/useDropdown-iVu14E6s.js.map +1 -0
  46. package/dist/{useExportCSV-B9o9lJ3D.js → useModal-Aq8hn152.js} +40 -1
  47. package/dist/useModal-Aq8hn152.js.map +1 -0
  48. package/dist/{useExportCSV-BPC_hd25.cjs → useModal-DDF_ZS8C.cjs} +40 -1
  49. package/dist/useModal-DDF_ZS8C.cjs.map +1 -0
  50. package/package.json +1 -1
  51. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-Cymj-zkh.js.map +0 -1
  52. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-Do0H2ZOe.cjs.map +0 -1
  53. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-D65uMijW.js.map +0 -1
  54. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-H6wsySqi.cjs.map +0 -1
  55. package/dist/Switch.vue_vue_type_script_setup_true_lang-C2_5u-HL.js.map +0 -1
  56. package/dist/Switch.vue_vue_type_script_setup_true_lang-V-FtNcTd.cjs.map +0 -1
  57. package/dist/index-CNQJxtkC.js.map +0 -1
  58. package/dist/index-D_7WQIhA.cjs.map +0 -1
  59. package/dist/useExportCSV-B9o9lJ3D.js.map +0 -1
  60. package/dist/useExportCSV-BPC_hd25.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PageLayout.vue_vue_type_script_setup_true_lang-J1I-WjM-.js","sources":["../src/components/layout/BaseLayout.vue","../src/components/layout/PageLayout.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { ref, computed, resolveComponent } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport MenuItem from '@/components/core/MenuItem.vue'\nimport Dropdown from '@/components/core/Dropdown.vue'\nimport type { MenuItemProps } from '@/types'\n\nexport interface UserMenuItem {\n label: string\n icon?: string\n link?: string\n action?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n /** Menu items for the sidebar */\n menuItems?: MenuItemProps[]\n /** App/brand name displayed in sidebar */\n appName?: string\n /** App icon (iconify icon name) */\n appIcon?: string\n /** Whether sidebar is open (v-model:sidebarOpen) */\n sidebarOpen?: boolean\n /** Whether dark mode is enabled (v-model:dark) */\n dark?: boolean\n /** Show dark mode toggle in header */\n showDarkToggle?: boolean\n /** Primary color class for sidebar background */\n sidebarClass?: string\n /** Current route path for menu active state (pass useRoute().path) */\n currentPath?: string\n /** User display name */\n userName?: string\n /** User avatar (initials or image URL) */\n userAvatar?: string\n /** User menu items (dropdown) */\n userMenuItems?: UserMenuItem[]\n }>(),\n {\n menuItems: () => [],\n appName: 'App',\n appIcon: 'lucide:box',\n sidebarOpen: true,\n dark: false,\n showDarkToggle: true,\n sidebarClass: 'bg-[#172b4c] dark:bg-slate-950',\n currentPath: undefined,\n userName: undefined,\n userAvatar: undefined,\n userMenuItems: () => [],\n },\n)\n\nconst emit = defineEmits<{\n 'update:sidebarOpen': [value: boolean]\n 'update:dark': [value: boolean]\n}>()\n\nconst internalSidebarOpen = ref(props.sidebarOpen)\nconst internalDark = ref(props.dark)\n\nconst sidebarOpenModel = computed({\n get: () => props.sidebarOpen ?? internalSidebarOpen.value,\n set: (value: boolean) => {\n internalSidebarOpen.value = value\n emit('update:sidebarOpen', value)\n },\n})\n\nconst darkModel = computed({\n get: () => props.dark ?? internalDark.value,\n set: (value: boolean) => {\n internalDark.value = value\n emit('update:dark', value)\n },\n})\n\nconst toggleSidebar = () => {\n sidebarOpenModel.value = !sidebarOpenModel.value\n}\n\nconst toggleDark = () => {\n darkModel.value = !darkModel.value\n}\n\n// Try to resolve RouterView\nconst routerViewComponent = computed(() => {\n try {\n const RouterView = resolveComponent('RouterView')\n if (typeof RouterView !== 'string') {\n return RouterView\n }\n } catch {\n // RouterView not available\n }\n return null\n})\n\n// Try to resolve RouterLink\nconst routerLinkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst getLinkProps = (link: string) => {\n if (routerLinkComponent.value === 'a') {\n return { href: link }\n }\n return { to: link }\n}\n\nconst handleUserMenuClick = (item: UserMenuItem) => {\n if (item.action) {\n item.action()\n }\n}\n</script>\n\n<template>\n <div class=\"font-inter relative flex h-dvh bg-gray-100 dark:bg-slate-900\">\n <!-- Backdrop for mobile -->\n <div\n v-if=\"sidebarOpenModel\"\n class=\"absolute z-40 h-full w-full bg-slate-950/20 lg:hidden dark:bg-white/20\"\n @click=\"sidebarOpenModel = false\"\n ></div>\n\n <!-- Sidebar -->\n <aside\n :class=\"[\n sidebarOpenModel ? 'lg:w-60' : '-translate-x-76 lg:w-16 lg:translate-x-0',\n sidebarClass,\n ]\"\n class=\"@container absolute z-50 flex h-full w-76 flex-col justify-between gap-10 transition-all duration-1000 ease-in-out lg:relative\"\n >\n <!-- Sidebar Header -->\n <div class=\"flex h-16 items-center px-3\">\n <div class=\"flex flex-1 items-center justify-center gap-3\">\n <slot name=\"logo\">\n <div\n class=\"bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-lg bg-white/20\"\n >\n <Icon class=\"size-5 text-white\" :icon=\"appIcon\" />\n </div>\n <span\n :class=\"sidebarOpenModel ? 'block' : 'hidden'\"\n class=\"font-outfit flex-1 text-lg font-semibold text-white\"\n >\n {{ appName }}\n </span>\n </slot>\n </div>\n\n <button\n class=\"rounded-lg bg-white/10 p-1 transition hover:bg-white/20 lg:hidden\"\n @click=\"toggleSidebar\"\n >\n <Icon class=\"size-6 text-white\" icon=\"lucide:menu\" />\n </button>\n </div>\n\n <!-- Menu Items -->\n <div\n :class=\"sidebarOpenModel ? 'items-start' : 'items-center'\"\n class=\"flex flex-1 flex-col gap-8 px-2 lg:justify-center\"\n >\n <slot name=\"menu\" :current-path=\"currentPath\">\n <MenuItem\n v-for=\"(item, index) in menuItems\"\n :key=\"index\"\n :menu-item=\"item\"\n :expanded=\"sidebarOpenModel\"\n :current-path=\"currentPath\"\n />\n </slot>\n </div>\n\n <!-- Sidebar Footer -->\n <div class=\"flex flex-col gap-3 px-3 pb-3\">\n <slot name=\"sidebar-footer\" />\n </div>\n </aside>\n\n <!-- Main Content Area -->\n <div class=\"flex flex-1 flex-col\">\n <!-- Header -->\n <header\n class=\"flex h-16 items-center justify-between border-b border-slate-200 bg-white px-4 dark:border-slate-800 dark:bg-slate-950\"\n >\n <div>\n <button\n class=\"rounded-lg bg-gray-100 p-1 transition hover:bg-gray-200 dark:bg-gray-900 dark:hover:bg-gray-800\"\n @click=\"toggleSidebar\"\n >\n <Icon\n class=\"size-6 text-gray-900 hover:text-gray-800 dark:text-gray-100\"\n icon=\"lucide:menu\"\n />\n </button>\n </div>\n\n <div class=\"flex-1\">\n <slot name=\"header-center\" />\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot name=\"header-actions\" />\n\n <button\n v-if=\"showDarkToggle\"\n class=\"flex items-center justify-center rounded-lg bg-gray-100 p-2 transition hover:bg-gray-200 dark:bg-gray-900 dark:hover:bg-gray-800\"\n @click=\"toggleDark\"\n >\n <Icon\n :icon=\"darkModel ? 'lucide:sun' : 'lucide:moon'\"\n class=\"size-5 text-gray-900 dark:text-gray-100\"\n />\n </button>\n\n <!-- User Menu -->\n <Dropdown v-if=\"userName || userAvatar\" align=\"right\">\n <template #trigger>\n <button\n class=\"flex items-center gap-2 rounded-lg p-1.5 transition hover:bg-gray-100 dark:hover:bg-gray-800\"\n >\n <div\n class=\"flex size-8 items-center justify-center rounded-full bg-primary text-sm font-medium text-white\"\n >\n {{ userAvatar || '?' }}\n </div>\n <span class=\"hidden text-sm font-medium text-gray-700 dark:text-gray-300 md:block\">\n {{ userName }}\n </span>\n <Icon icon=\"lucide:chevron-down\" class=\"size-4 text-gray-500\" />\n </button>\n </template>\n\n <template #default=\"{ close }\">\n <div class=\"min-w-48 py-1\">\n <component\n :is=\"item.link ? routerLinkComponent : 'button'\"\n v-for=\"item in userMenuItems\"\n :key=\"item.label\"\n v-bind=\"item.link ? getLinkProps(item.link) : {}\"\n class=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800\"\n @click=\"handleUserMenuClick(item); close()\"\n >\n <Icon v-if=\"item.icon\" :icon=\"item.icon\" class=\"size-4\" />\n {{ item.label }}\n </component>\n </div>\n </template>\n </Dropdown>\n </div>\n </header>\n\n <!-- Page Content -->\n <div class=\"flex flex-1 flex-col overflow-y-auto\">\n <main class=\"container mx-auto flex flex-1 flex-col gap-5 p-5\">\n <slot>\n <component :is=\"routerViewComponent\" v-if=\"routerViewComponent\" />\n </slot>\n </main>\n </div>\n </div>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { computed, resolveComponent } from 'vue'\n\nexport interface Breadcrumb {\n label: string\n link: string\n}\n\ndefineProps<{\n /** Page title */\n title?: string\n /** Page description */\n description?: string\n /** Breadcrumb navigation items */\n breadcrumbs?: Breadcrumb[]\n}>()\n\n// Try to resolve RouterLink\nconst linkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst getLinkProps = (link: string) => {\n if (linkComponent.value === 'a') {\n return { href: link }\n }\n return { to: link }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-4\">\n <!-- Breadcrumbs -->\n <nav v-if=\"breadcrumbs && breadcrumbs.length > 0\" aria-label=\"Breadcrumb\">\n <ol class=\"flex items-center\">\n <li\n v-for=\"(breadcrumb, index) in breadcrumbs\"\n :key=\"index\"\n class=\"flex items-center\"\n >\n <span\n v-if=\"index > 0\"\n class=\"mx-3 text-sm font-semibold text-gray-400 dark:text-gray-600\"\n >\n /\n </span>\n\n <slot name=\"breadcrumb\" :breadcrumb=\"breadcrumb\" :index=\"index\" :isLast=\"index === breadcrumbs.length - 1\">\n <component\n :is=\"linkComponent\"\n v-bind=\"getLinkProps(breadcrumb.link)\"\n :class=\"[\n 'text-sm transition-colors',\n index < breadcrumbs.length - 1\n ? 'font-semibold text-gray-900 hover:text-primary/90 hover:underline dark:text-gray-100'\n : 'text-gray-400 dark:text-gray-600',\n ]\"\n >\n {{ breadcrumb.label }}\n </component>\n </slot>\n </li>\n </ol>\n </nav>\n\n <!-- Page Header -->\n <div class=\"flex flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <div class=\"flex flex-col gap-1\">\n <h1\n v-if=\"title\"\n class=\"text-2xl font-bold text-gray-900 dark:text-gray-100\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <p\n v-if=\"description\"\n class=\"text-sm text-gray-600 dark:text-gray-400\"\n >\n <slot name=\"description\">{{ description }}</slot>\n </p>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Page Content -->\n <div class=\"flex-1\">\n <slot />\n </div>\n </div>\n</template>\n"],"names":["_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","_renderSlot","_hoisted_4","_createVNode","_unref","_Fragment","_renderList","_createBlock","MenuItem","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","Dropdown","_hoisted_10","_toDisplayString","_withCtx","_resolveDynamicComponent","_mergeProps","_createTextVNode"],"mappings":"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"}
1
+ {"version":3,"file":"PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js","sources":["../src/components/layout/BaseLayout.vue","../src/components/layout/PageLayout.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport { ref, computed, resolveComponent } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport MenuItem from '@/components/core/MenuItem.vue'\nimport Dropdown from '@/components/core/Dropdown.vue'\nimport type { MenuItemProps } from '@/types'\n\nexport interface UserMenuItem {\n label: string\n icon?: string\n link?: string\n action?: () => void\n}\n\nconst props = withDefaults(\n defineProps<{\n /** Menu items for the sidebar */\n menuItems?: MenuItemProps[]\n /** App/brand name displayed in sidebar */\n appName?: string\n /** App icon (iconify icon name) */\n appIcon?: string\n /** Whether sidebar is open (v-model:sidebarOpen) */\n sidebarOpen?: boolean\n /** Whether dark mode is enabled (v-model:dark) */\n dark?: boolean\n /** Show dark mode toggle in header */\n showDarkToggle?: boolean\n /** Primary color class for sidebar background */\n sidebarClass?: string\n /** Current route path for menu active state (pass useRoute().path) */\n currentPath?: string\n /** User display name */\n userName?: string\n /** User avatar (initials or image URL) */\n userAvatar?: string\n /** User menu items (dropdown) */\n userMenuItems?: UserMenuItem[]\n }>(),\n {\n menuItems: () => [],\n appName: 'App',\n appIcon: 'lucide:box',\n sidebarOpen: true,\n dark: false,\n showDarkToggle: true,\n sidebarClass: 'bg-[#172b4c] dark:bg-slate-950',\n currentPath: undefined,\n userName: undefined,\n userAvatar: undefined,\n userMenuItems: () => [],\n },\n)\n\nconst emit = defineEmits<{\n 'update:sidebarOpen': [value: boolean]\n 'update:dark': [value: boolean]\n}>()\n\nconst internalSidebarOpen = ref(props.sidebarOpen)\nconst internalDark = ref(props.dark)\n\nconst sidebarOpenModel = computed({\n get: () => props.sidebarOpen ?? internalSidebarOpen.value,\n set: (value: boolean) => {\n internalSidebarOpen.value = value\n emit('update:sidebarOpen', value)\n },\n})\n\nconst darkModel = computed({\n get: () => props.dark ?? internalDark.value,\n set: (value: boolean) => {\n internalDark.value = value\n emit('update:dark', value)\n },\n})\n\nconst toggleSidebar = () => {\n sidebarOpenModel.value = !sidebarOpenModel.value\n}\n\nconst toggleDark = () => {\n darkModel.value = !darkModel.value\n}\n\n// Try to resolve RouterView\nconst routerViewComponent = computed(() => {\n try {\n const RouterView = resolveComponent('RouterView')\n if (typeof RouterView !== 'string') {\n return RouterView\n }\n } catch {\n // RouterView not available\n }\n return null\n})\n\n// Try to resolve RouterLink\nconst routerLinkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst getLinkProps = (link: string) => {\n if (routerLinkComponent.value === 'a') {\n return { href: link }\n }\n return { to: link }\n}\n\nconst handleUserMenuClick = (item: UserMenuItem) => {\n if (item.action) {\n item.action()\n }\n}\n</script>\n\n<template>\n <div class=\"font-inter relative flex h-dvh bg-gray-100 dark:bg-slate-900\">\n <!-- Backdrop for mobile -->\n <div\n v-if=\"sidebarOpenModel\"\n class=\"absolute z-40 h-full w-full bg-slate-950/20 lg:hidden dark:bg-white/20\"\n @click=\"sidebarOpenModel = false\"\n ></div>\n\n <!-- Sidebar -->\n <aside\n :class=\"[\n sidebarOpenModel ? 'lg:w-60' : '-translate-x-76 lg:w-16 lg:translate-x-0',\n sidebarClass,\n ]\"\n class=\"@container absolute z-50 flex h-full w-76 flex-col justify-between gap-10 transition-all duration-1000 ease-in-out lg:relative\"\n >\n <!-- Sidebar Header -->\n <div class=\"flex h-16 items-center px-3\">\n <div class=\"flex flex-1 items-center justify-center gap-3\">\n <slot name=\"logo\">\n <div\n class=\"bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-lg bg-white/20\"\n >\n <Icon class=\"size-5 text-white\" :icon=\"appIcon\" />\n </div>\n <span\n :class=\"sidebarOpenModel ? 'block' : 'hidden'\"\n class=\"font-outfit flex-1 text-lg font-semibold text-white\"\n >\n {{ appName }}\n </span>\n </slot>\n </div>\n\n <button\n class=\"rounded-lg bg-white/10 p-1 transition hover:bg-white/20 lg:hidden\"\n @click=\"toggleSidebar\"\n >\n <Icon class=\"size-6 text-white\" icon=\"lucide:menu\" />\n </button>\n </div>\n\n <!-- Menu Items -->\n <div\n :class=\"sidebarOpenModel ? 'items-start' : 'items-center'\"\n class=\"flex flex-1 flex-col gap-8 px-2 lg:justify-center\"\n >\n <slot name=\"menu\" :current-path=\"currentPath\">\n <MenuItem\n v-for=\"(item, index) in menuItems\"\n :key=\"index\"\n :menu-item=\"item\"\n :expanded=\"sidebarOpenModel\"\n :current-path=\"currentPath\"\n />\n </slot>\n </div>\n\n <!-- Sidebar Footer -->\n <div class=\"flex flex-col gap-3 px-3 pb-3\">\n <slot name=\"sidebar-footer\" />\n </div>\n </aside>\n\n <!-- Main Content Area -->\n <div class=\"flex flex-1 flex-col\">\n <!-- Header -->\n <header\n class=\"flex h-16 items-center justify-between border-b border-slate-200 bg-white px-4 dark:border-slate-800 dark:bg-slate-950\"\n >\n <div>\n <button\n class=\"rounded-lg bg-gray-100 p-1 transition hover:bg-gray-200 dark:bg-gray-900 dark:hover:bg-gray-800\"\n @click=\"toggleSidebar\"\n >\n <Icon\n class=\"size-6 text-gray-900 hover:text-gray-800 dark:text-gray-100\"\n icon=\"lucide:menu\"\n />\n </button>\n </div>\n\n <div class=\"flex-1\">\n <slot name=\"header-center\" />\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot name=\"header-actions\" />\n\n <button\n v-if=\"showDarkToggle\"\n class=\"flex items-center justify-center rounded-lg bg-gray-100 p-2 transition hover:bg-gray-200 dark:bg-gray-900 dark:hover:bg-gray-800\"\n @click=\"toggleDark\"\n >\n <Icon\n :icon=\"darkModel ? 'lucide:sun' : 'lucide:moon'\"\n class=\"size-5 text-gray-900 dark:text-gray-100\"\n />\n </button>\n\n <!-- User Menu -->\n <Dropdown v-if=\"userName || userAvatar\" align=\"right\">\n <template #trigger>\n <button\n class=\"flex items-center gap-2 rounded-lg p-1.5 transition hover:bg-gray-100 dark:hover:bg-gray-800\"\n >\n <div\n class=\"flex size-8 items-center justify-center rounded-full bg-primary text-sm font-medium text-white\"\n >\n {{ userAvatar || '?' }}\n </div>\n <span class=\"hidden text-sm font-medium text-gray-700 dark:text-gray-300 md:block\">\n {{ userName }}\n </span>\n <Icon icon=\"lucide:chevron-down\" class=\"size-4 text-gray-500\" />\n </button>\n </template>\n\n <template #default=\"{ close }\">\n <div class=\"min-w-48 py-1\">\n <component\n :is=\"item.link ? routerLinkComponent : 'button'\"\n v-for=\"item in userMenuItems\"\n :key=\"item.label\"\n v-bind=\"item.link ? getLinkProps(item.link) : {}\"\n class=\"flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800\"\n @click=\"handleUserMenuClick(item); close()\"\n >\n <Icon v-if=\"item.icon\" :icon=\"item.icon\" class=\"size-4\" />\n {{ item.label }}\n </component>\n </div>\n </template>\n </Dropdown>\n </div>\n </header>\n\n <!-- Page Content -->\n <div class=\"flex flex-1 flex-col overflow-y-auto\">\n <main class=\"container mx-auto flex flex-1 flex-col gap-5 p-5\">\n <slot>\n <component :is=\"routerViewComponent\" v-if=\"routerViewComponent\" />\n </slot>\n </main>\n </div>\n </div>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { computed, resolveComponent } from 'vue'\n\nexport interface Breadcrumb {\n label: string\n link: string\n}\n\ndefineProps<{\n /** Page title */\n title?: string\n /** Page description */\n description?: string\n /** Breadcrumb navigation items */\n breadcrumbs?: Breadcrumb[]\n}>()\n\n// Try to resolve RouterLink\nconst linkComponent = computed(() => {\n try {\n const RouterLink = resolveComponent('RouterLink')\n if (typeof RouterLink !== 'string') {\n return RouterLink\n }\n } catch {\n // RouterLink not available\n }\n return 'a'\n})\n\nconst getLinkProps = (link: string) => {\n if (linkComponent.value === 'a') {\n return { href: link }\n }\n return { to: link }\n}\n</script>\n\n<template>\n <div class=\"flex flex-col gap-4\">\n <!-- Breadcrumbs -->\n <nav v-if=\"breadcrumbs && breadcrumbs.length > 0\" aria-label=\"Breadcrumb\">\n <ol class=\"flex items-center\">\n <li\n v-for=\"(breadcrumb, index) in breadcrumbs\"\n :key=\"index\"\n class=\"flex items-center\"\n >\n <span\n v-if=\"index > 0\"\n class=\"mx-3 text-sm font-semibold text-gray-400 dark:text-gray-600\"\n >\n /\n </span>\n\n <slot name=\"breadcrumb\" :breadcrumb=\"breadcrumb\" :index=\"index\" :isLast=\"index === breadcrumbs.length - 1\">\n <component\n :is=\"linkComponent\"\n v-bind=\"getLinkProps(breadcrumb.link)\"\n :class=\"[\n 'text-sm transition-colors',\n index < breadcrumbs.length - 1\n ? 'font-semibold text-gray-900 hover:text-primary/90 hover:underline dark:text-gray-100'\n : 'text-gray-400 dark:text-gray-600',\n ]\"\n >\n {{ breadcrumb.label }}\n </component>\n </slot>\n </li>\n </ol>\n </nav>\n\n <!-- Page Header -->\n <div class=\"flex flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <div class=\"flex flex-col gap-1\">\n <h1\n v-if=\"title\"\n class=\"text-2xl font-bold text-gray-900 dark:text-gray-100\"\n >\n <slot name=\"title\">{{ title }}</slot>\n </h1>\n\n <p\n v-if=\"description\"\n class=\"text-sm text-gray-600 dark:text-gray-400\"\n >\n <slot name=\"description\">{{ description }}</slot>\n </p>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <slot name=\"actions\" />\n </div>\n </div>\n\n <!-- Page Content -->\n <div class=\"flex-1\">\n <slot />\n </div>\n </div>\n</template>\n"],"names":["_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_normalizeClass","_hoisted_2","_hoisted_3","_renderSlot","_hoisted_4","_createVNode","_unref","_Fragment","_renderList","_createBlock","MenuItem","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","Dropdown","_hoisted_10","_toDisplayString","_withCtx","_resolveDynamicComponent","_mergeProps","_createTextVNode"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { defineComponent, createElementBlock, openBlock, renderSlot, createTextVNode, toDisplayString, mergeModels, useModel, withDirectives, mergeProps, vModelDynamic, ref, computed, watch, onUnmounted, createElementVNode, createBlock, normalizeClass, createVNode, unref, Teleport, Transition, withCtx, createCommentVNode, normalizeStyle, vModelText, Fragment, renderList, nextTick } from "vue";
1
+ import { defineComponent, createElementBlock, openBlock, renderSlot, createTextVNode, toDisplayString, mergeModels, useModel, withDirectives, mergeProps, vModelDynamic, ref, computed, watch, createElementVNode, createBlock, normalizeClass, createVNode, unref, Teleport, Transition, withCtx, createCommentVNode, normalizeStyle, vModelText, Fragment, renderList, nextTick } from "vue";
2
2
  import { Icon } from "@iconify/vue";
3
+ import { u as useDropdown } from "./useDropdown-iVu14E6s.js";
3
4
  const _hoisted_1$6 = ["data-error", "for"];
4
5
  const _sfc_main$6 = /* @__PURE__ */ defineComponent({
5
6
  __name: "FormLabel",
@@ -114,13 +115,25 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
114
115
  setup(__props) {
115
116
  const props = __props;
116
117
  const modelValue = useModel(__props, "modelValue");
117
- const isOpen = ref(false);
118
118
  const searchQuery = ref("");
119
- const highlightedIndex = ref(-1);
120
119
  const triggerRef = ref();
121
120
  const dropdownRef = ref();
122
121
  const searchInputRef = ref();
123
- const dropdownPosition = ref({ top: 0, left: 0, width: 0 });
122
+ const {
123
+ isOpen,
124
+ highlightedIndex,
125
+ dropdownStyle,
126
+ open: openDropdown,
127
+ close,
128
+ handleKeydown: baseHandleKeydown,
129
+ scrollToHighlighted
130
+ } = useDropdown(triggerRef, dropdownRef, {
131
+ teleport: props.teleport,
132
+ gap: 4,
133
+ onClose: () => {
134
+ searchQuery.value = "";
135
+ }
136
+ });
124
137
  const visibleOptions = computed(() => {
125
138
  return (props.options ?? []).filter((opt) => !opt.hidden);
126
139
  });
@@ -142,35 +155,20 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
142
155
  }
143
156
  return props.placeholder || "Select...";
144
157
  });
145
- const updatePosition = () => {
146
- if (!triggerRef.value || !props.teleport) return;
147
- const rect = triggerRef.value.getBoundingClientRect();
148
- dropdownPosition.value = {
149
- top: rect.bottom + window.scrollY + 4,
150
- left: rect.left + window.scrollX,
151
- width: rect.width
152
- };
153
- };
154
158
  const open = () => {
155
159
  if (props.disabled) return;
156
- isOpen.value = true;
157
160
  searchQuery.value = "";
158
161
  highlightedIndex.value = filteredOptions.value.findIndex(
159
162
  (opt) => opt.value === modelValue.value
160
163
  );
164
+ openDropdown();
161
165
  nextTick(() => {
162
166
  var _a;
163
- updatePosition();
164
167
  if (props.searchable) {
165
168
  (_a = searchInputRef.value) == null ? void 0 : _a.focus();
166
169
  }
167
170
  });
168
171
  };
169
- const close = () => {
170
- isOpen.value = false;
171
- searchQuery.value = "";
172
- highlightedIndex.value = -1;
173
- };
174
172
  const toggle = () => {
175
173
  if (isOpen.value) {
176
174
  close();
@@ -183,91 +181,23 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
183
181
  close();
184
182
  };
185
183
  const handleKeydown = (event) => {
186
- if (!isOpen.value) {
187
- if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
188
- event.preventDefault();
189
- open();
190
- }
191
- return;
192
- }
193
- switch (event.key) {
194
- case "ArrowDown":
195
- event.preventDefault();
196
- highlightedIndex.value = Math.min(
197
- highlightedIndex.value + 1,
198
- filteredOptions.value.length - 1
199
- );
200
- scrollToHighlighted();
201
- break;
202
- case "ArrowUp":
203
- event.preventDefault();
204
- highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0);
205
- scrollToHighlighted();
206
- break;
207
- case "Enter":
208
- event.preventDefault();
209
- if (highlightedIndex.value >= 0 && filteredOptions.value[highlightedIndex.value]) {
210
- selectOption(filteredOptions.value[highlightedIndex.value]);
211
- }
212
- break;
213
- case "Escape":
214
- event.preventDefault();
215
- close();
216
- break;
217
- case "Tab":
218
- close();
219
- break;
220
- }
221
- };
222
- const scrollToHighlighted = () => {
223
- nextTick(() => {
224
- if (dropdownRef.value) {
225
- const highlighted = dropdownRef.value.querySelector(
226
- `[data-index="${highlightedIndex.value}"]`
227
- );
228
- if (highlighted) {
229
- highlighted.scrollIntoView({ block: "nearest" });
184
+ baseHandleKeydown(event, {
185
+ itemCount: filteredOptions.value.length,
186
+ onSelect: (index) => {
187
+ if (filteredOptions.value[index]) {
188
+ selectOption(filteredOptions.value[index]);
230
189
  }
231
- }
190
+ },
191
+ onOpen: open,
192
+ handleOpenKeys: true
232
193
  });
233
- };
234
- const handleClickOutside = (event) => {
235
- var _a, _b;
236
- const target = event.target;
237
- const isInsideTrigger = (_a = triggerRef.value) == null ? void 0 : _a.contains(target);
238
- const isInsideDropdown = (_b = dropdownRef.value) == null ? void 0 : _b.contains(target);
239
- if (!isInsideTrigger && !isInsideDropdown) {
240
- close();
194
+ if (isOpen.value) {
195
+ scrollToHighlighted(dropdownRef.value ?? null);
241
196
  }
242
197
  };
243
- watch(isOpen, (newValue) => {
244
- if (newValue) {
245
- document.addEventListener("click", handleClickOutside);
246
- window.addEventListener("scroll", updatePosition, true);
247
- window.addEventListener("resize", updatePosition);
248
- } else {
249
- document.removeEventListener("click", handleClickOutside);
250
- window.removeEventListener("scroll", updatePosition, true);
251
- window.removeEventListener("resize", updatePosition);
252
- }
253
- });
254
198
  watch(searchQuery, () => {
255
199
  highlightedIndex.value = 0;
256
200
  });
257
- onUnmounted(() => {
258
- document.removeEventListener("click", handleClickOutside);
259
- window.removeEventListener("scroll", updatePosition, true);
260
- window.removeEventListener("resize", updatePosition);
261
- });
262
- const dropdownStyle = computed(() => {
263
- if (!props.teleport) return {};
264
- return {
265
- position: "absolute",
266
- top: `${dropdownPosition.value.top}px`,
267
- left: `${dropdownPosition.value.left}px`,
268
- width: `${dropdownPosition.value.width}px`
269
- };
270
- });
271
201
  const triggerClasses = computed(() => {
272
202
  const base = "flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm text-left transition";
273
203
  const state = props.disabled ? "cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-500" : isOpen.value ? "border-primary ring-2 ring-primary/20 bg-white dark:bg-gray-900" : "border-gray-300 bg-white hover:border-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600";
@@ -294,7 +224,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
294
224
  ]),
295
225
  createVNode(unref(Icon), {
296
226
  icon: "lucide:chevron-down",
297
- class: normalizeClass(["size-4 shrink-0 text-gray-400 transition-transform", isOpen.value && "rotate-180"])
227
+ class: normalizeClass(["size-4 shrink-0 text-gray-400 transition-transform", unref(isOpen) && "rotate-180"])
298
228
  }, null, 8, ["class"])
299
229
  ], 42, _hoisted_2$2),
300
230
  (openBlock(), createBlock(Teleport, {
@@ -310,11 +240,11 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
310
240
  "leave-to-class": "opacity-0 scale-95"
311
241
  }, {
312
242
  default: withCtx(() => [
313
- isOpen.value ? (openBlock(), createElementBlock("div", {
243
+ unref(isOpen) ? (openBlock(), createElementBlock("div", {
314
244
  key: 0,
315
245
  ref_key: "dropdownRef",
316
246
  ref: dropdownRef,
317
- style: normalizeStyle(dropdownStyle.value),
247
+ style: normalizeStyle(unref(dropdownStyle)),
318
248
  class: normalizeClass([
319
249
  "z-[9999] max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800",
320
250
  !__props.teleport && "absolute mt-1 w-full"
@@ -348,7 +278,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
348
278
  "data-index": index,
349
279
  class: normalizeClass([
350
280
  "flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors",
351
- highlightedIndex.value === index && "bg-gray-100 dark:bg-gray-700",
281
+ unref(highlightedIndex) === index && "bg-gray-100 dark:bg-gray-700",
352
282
  modelValue.value === option.value && "bg-primary/10 text-primary",
353
283
  modelValue.value !== option.value && "text-gray-700 dark:text-gray-200"
354
284
  ]),
@@ -570,4 +500,4 @@ export {
570
500
  _sfc_main$1 as e,
571
501
  _sfc_main as f
572
502
  };
573
- //# sourceMappingURL=Switch.vue_vue_type_script_setup_true_lang-C2_5u-HL.js.map
503
+ //# sourceMappingURL=Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js","sources":["../src/components/form/FormLabel.vue","../src/components/form/FormHelp.vue","../src/components/form/FormInput.vue","../src/components/form/FormSelect.vue","../src/components/form/FormGroup.vue","../src/components/form/SearchInput.vue","../src/components/form/Switch.vue"],"sourcesContent":["<script lang=\"ts\" setup>\ndefineProps<{\n error?: string | boolean\n htmlFor?: string\n}>()\n</script>\n\n<template>\n <label\n :data-error=\"!!error\"\n :for=\"htmlFor ?? ''\"\n class=\"block text-sm font-medium text-gray-700 data-[error=true]:text-red-500 dark:text-gray-300\"\n >\n <slot />\n </label>\n</template>\n","<script lang=\"ts\" setup>\ndefineProps<{\n error?: boolean\n text?: string\n}>()\n</script>\n\n<template>\n <p\n :data-error=\"error\"\n class=\"mt-2 text-sm text-gray-400 peer-invalid:visible data-[error=true]:text-red-500 dark:text-gray-500\"\n >\n <slot>{{ text ?? '' }}</slot>\n </p>\n</template>\n","<script lang=\"ts\" setup>\nimport type { InputProps } from '@/types'\n\nwithDefaults(defineProps<InputProps>(), {\n type: 'text',\n})\n\nconst modelValue = defineModel<string>()\n</script>\n\n<template>\n <input\n :id=\"id ?? name ?? ''\"\n v-model=\"modelValue\"\n :disabled=\"disabled\"\n :name=\"name ?? ''\"\n :placeholder=\"placeholder\"\n :type=\"type\"\n class=\"mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-800 placeholder-gray-400 shadow-sm invalid:border-pink-500 invalid:text-pink-600 focus:border-primary focus:ring-1 focus:ring-primary focus:outline-none focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:placeholder-gray-500 dark:focus:border-primary dark:focus:ring-primary dark:disabled:border-gray-800 dark:disabled:bg-gray-950 dark:disabled:text-gray-500\"\n v-bind=\"$attrs\"\n />\n</template>\n","<script lang=\"ts\" setup>\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport type { SelectProps, SelectOption } from '@/types'\nimport { useDropdown } from '@/composables/useDropdown'\n\nconst props = withDefaults(\n defineProps<\n SelectProps & {\n /** Use teleport to body to avoid overflow clipping */\n teleport?: boolean\n /** Show search input in dropdown */\n searchable?: boolean\n /** Text shown when no results match search */\n noResultsText?: string\n /** Custom class for the trigger button */\n triggerClass?: string\n }\n >(),\n {\n teleport: true,\n searchable: false,\n noResultsText: 'No results found',\n },\n)\n\nconst modelValue = defineModel<string | number | boolean | null>()\n\nconst searchQuery = ref('')\nconst triggerRef = ref<HTMLElement>()\nconst dropdownRef = ref<HTMLElement>()\nconst searchInputRef = ref<HTMLInputElement>()\n\nconst {\n isOpen,\n highlightedIndex,\n dropdownStyle,\n open: openDropdown,\n close,\n handleKeydown: baseHandleKeydown,\n scrollToHighlighted,\n} = useDropdown(triggerRef, dropdownRef, {\n teleport: props.teleport,\n gap: 4,\n onClose: () => {\n searchQuery.value = ''\n },\n})\n\nconst visibleOptions = computed(() => {\n return (props.options ?? []).filter((opt) => !opt.hidden)\n})\n\nconst filteredOptions = computed(() => {\n if (!props.searchable || !searchQuery.value) {\n return visibleOptions.value\n }\n const query = searchQuery.value.toLowerCase()\n return visibleOptions.value.filter((opt) =>\n opt.label.toLowerCase().includes(query) ||\n String(opt.value).toLowerCase().includes(query)\n )\n})\n\nconst selectedOption = computed(() => {\n return visibleOptions.value.find((opt) => opt.value === modelValue.value)\n})\n\nconst displayValue = computed(() => {\n if (selectedOption.value) {\n return selectedOption.value.label\n }\n return props.placeholder || 'Select...'\n})\n\nconst open = () => {\n if (props.disabled) return\n searchQuery.value = ''\n highlightedIndex.value = filteredOptions.value.findIndex(\n (opt) => opt.value === modelValue.value\n )\n openDropdown()\n nextTick(() => {\n if (props.searchable) {\n searchInputRef.value?.focus()\n }\n })\n}\n\nconst toggle = () => {\n if (isOpen.value) {\n close()\n } else {\n open()\n }\n}\n\nconst selectOption = (option: SelectOption) => {\n modelValue.value = option.value\n close()\n}\n\nconst handleKeydown = (event: KeyboardEvent) => {\n baseHandleKeydown(event, {\n itemCount: filteredOptions.value.length,\n onSelect: (index) => {\n if (filteredOptions.value[index]) {\n selectOption(filteredOptions.value[index])\n }\n },\n onOpen: open,\n handleOpenKeys: true,\n })\n if (isOpen.value) {\n scrollToHighlighted(dropdownRef.value ?? null)\n }\n}\n\nwatch(searchQuery, () => {\n highlightedIndex.value = 0\n})\n\nconst triggerClasses = computed(() => {\n const base = 'flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm text-left transition'\n const state = props.disabled\n ? 'cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-500'\n : isOpen.value\n ? 'border-primary ring-2 ring-primary/20 bg-white dark:bg-gray-900'\n : 'border-gray-300 bg-white hover:border-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600'\n const text = selectedOption.value\n ? 'text-gray-800 dark:text-gray-200'\n : 'text-gray-400 dark:text-gray-500'\n return [base, state, text, props.triggerClass]\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <!-- Trigger -->\n <button\n ref=\"triggerRef\"\n type=\"button\"\n :id=\"id ?? name ?? undefined\"\n :disabled=\"disabled\"\n :class=\"triggerClasses\"\n @click=\"toggle\"\n @keydown=\"handleKeydown\"\n >\n <slot name=\"selected\" :option=\"selectedOption\" :placeholder=\"placeholder\">\n <span class=\"flex-1 truncate\">{{ displayValue }}</span>\n </slot>\n <Icon\n icon=\"lucide:chevron-down\"\n :class=\"['size-4 shrink-0 text-gray-400 transition-transform', isOpen && 'rotate-180']\"\n />\n </button>\n\n <!-- Dropdown -->\n <Teleport to=\"body\" :disabled=\"!teleport\">\n <Transition\n enter-active-class=\"transition duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n :style=\"dropdownStyle\"\n :class=\"[\n 'z-[9999] max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800',\n !teleport && 'absolute mt-1 w-full',\n ]\"\n >\n <!-- Search input -->\n <div v-if=\"searchable\" class=\"sticky top-0 border-b border-gray-200 bg-white p-2 dark:border-gray-700 dark:bg-gray-800\">\n <div class=\"flex items-center gap-2 rounded-md border border-gray-300 bg-gray-50 px-2 py-1.5 dark:border-gray-600 dark:bg-gray-900\">\n <Icon icon=\"lucide:search\" class=\"size-4 text-gray-400\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"flex-1 bg-transparent text-sm outline-none dark:text-white\"\n placeholder=\"Search...\"\n @keydown=\"handleKeydown\"\n />\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-sm text-gray-500 dark:text-gray-400\"\n >\n {{ noResultsText }}\n </div>\n\n <!-- Options -->\n <div class=\"py-1\">\n <button\n v-for=\"(option, index) in filteredOptions\"\n :key=\"String(option.value)\"\n type=\"button\"\n :data-index=\"index\"\n :class=\"[\n 'flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors',\n highlightedIndex === index && 'bg-gray-100 dark:bg-gray-700',\n modelValue === option.value && 'bg-primary/10 text-primary',\n modelValue !== option.value && 'text-gray-700 dark:text-gray-200',\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"highlightedIndex = index\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"modelValue === option.value\" :index=\"index\">\n <Icon\n v-if=\"modelValue === option.value\"\n icon=\"lucide:check\"\n class=\"size-4 shrink-0 text-primary\"\n />\n <span v-else class=\"size-4 shrink-0\" />\n <span class=\"flex-1\">{{ option.label }}</span>\n </slot>\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { computed } from 'vue'\nimport type { FormGroupProps } from '@/types'\nimport FormLabel from './FormLabel.vue'\nimport FormInput from './FormInput.vue'\nimport FormSelect from './FormSelect.vue'\nimport FormHelp from './FormHelp.vue'\n\nconst props = withDefaults(defineProps<FormGroupProps>(), {\n cols: 6,\n})\n\nconst modelValue = defineModel()\n\nconst computedCols = computed(() => `span ${props.cols} / span ${props.cols}`)\n\nconst isSelect = computed(() => props.select === true)\n</script>\n\n<template>\n <div :style=\"{ gridColumn: computedCols }\" :hidden=\"hidden\">\n <slot>\n <slot name=\"label\">\n <FormLabel v-if=\"label\" :html-for=\"id ?? name\" :error=\"error\">\n {{ label }}\n </FormLabel>\n </slot>\n\n <slot name=\"input\">\n <FormSelect\n v-if=\"isSelect\"\n v-model=\"modelValue\"\n v-bind=\"{ ...$attrs, ...$props }\"\n />\n <FormInput\n v-else\n v-model=\"modelValue\"\n v-bind=\"{ ...$attrs, ...$props }\"\n />\n </slot>\n\n <FormHelp v-if=\"error && typeof error === 'string'\" :error=\"true\">\n {{ error }}\n </FormHelp>\n </slot>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { Icon } from '@iconify/vue'\n\nconst {\n placeholder = 'Search...',\n icon = 'lucide:search',\n disabled = false,\n} = defineProps<{\n modelValue?: string\n placeholder?: string\n icon?: string\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', target.value)\n}\n</script>\n\n<template>\n <div class=\"relative\">\n <Icon :icon=\"icon\" class=\"absolute top-1/2 left-3 h-5 w-5 -translate-y-1/2 text-gray-400\" />\n <input\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :value=\"modelValue\"\n class=\"focus:border-primary focus:ring-primary w-full rounded-lg border border-gray-300 bg-white py-2 pr-4 pl-10 text-gray-900 placeholder-gray-400 transition-colors disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500\"\n type=\"text\"\n @input=\"handleInput\"\n />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n\nconst props = withDefaults(\n defineProps<{\n /** v-model value */\n modelValue?: boolean\n /** Label text */\n label?: string\n /** Description text */\n description?: string\n /** Disabled state */\n disabled?: boolean\n /** Size variant */\n size?: 'sm' | 'md' | 'lg'\n }>(),\n {\n modelValue: false,\n size: 'md',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n}>()\n\nconst toggle = () => {\n if (props.disabled) return\n emit('update:modelValue', !props.modelValue)\n}\n\nconst switchSizes = {\n sm: 'h-5 w-9',\n md: 'h-6 w-11',\n lg: 'h-7 w-14',\n}\n\nconst dotSizes = {\n sm: 'size-3',\n md: 'size-4',\n lg: 'size-5',\n}\n\nconst translateSizes = {\n sm: 'translate-x-5',\n md: 'translate-x-6',\n lg: 'translate-x-8',\n}\n</script>\n\n<template>\n <label\n class=\"inline-flex items-start gap-3\"\n :class=\"disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'\"\n >\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :disabled=\"disabled\"\n :class=\"[\n 'relative inline-flex shrink-0 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2',\n switchSizes[size],\n modelValue ? 'bg-primary' : 'bg-gray-300 dark:bg-gray-600',\n ]\"\n @click=\"toggle\"\n >\n <span\n :class=\"[\n 'inline-block transform rounded-full bg-white shadow-sm transition-transform',\n dotSizes[size],\n modelValue ? translateSizes[size] : 'translate-x-1',\n ]\"\n />\n </button>\n <div v-if=\"label || description\" class=\"flex flex-col\">\n <span v-if=\"label\" class=\"text-sm font-medium text-gray-900 dark:text-white\">\n {{ label }}\n </span>\n <span v-if=\"description\" class=\"text-sm text-gray-500 dark:text-gray-400\">\n {{ description }}\n </span>\n </div>\n </label>\n</template>\n"],"names":["_createElementBlock","_renderSlot","_useModel","_withDirectives","_openBlock","_mergeProps","$attrs","_hoisted_1","_createElementVNode","_hoisted_3","_toDisplayString","_createVNode","_unref","_createBlock","_Teleport","_Transition","_normalizeClass","_hoisted_4","_Fragment","_renderList","FormLabel","FormSelect","$props","FormInput","FormHelp"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  const vue = require("vue");
3
3
  const vue$1 = require("@iconify/vue");
4
+ const useDropdown = require("./useDropdown-DHFnd259.cjs");
4
5
  const _hoisted_1$6 = ["data-error", "for"];
5
6
  const _sfc_main$6 = /* @__PURE__ */ vue.defineComponent({
6
7
  __name: "FormLabel",
@@ -115,13 +116,25 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
115
116
  setup(__props) {
116
117
  const props = __props;
117
118
  const modelValue = vue.useModel(__props, "modelValue");
118
- const isOpen = vue.ref(false);
119
119
  const searchQuery = vue.ref("");
120
- const highlightedIndex = vue.ref(-1);
121
120
  const triggerRef = vue.ref();
122
121
  const dropdownRef = vue.ref();
123
122
  const searchInputRef = vue.ref();
124
- const dropdownPosition = vue.ref({ top: 0, left: 0, width: 0 });
123
+ const {
124
+ isOpen,
125
+ highlightedIndex,
126
+ dropdownStyle,
127
+ open: openDropdown,
128
+ close,
129
+ handleKeydown: baseHandleKeydown,
130
+ scrollToHighlighted
131
+ } = useDropdown.useDropdown(triggerRef, dropdownRef, {
132
+ teleport: props.teleport,
133
+ gap: 4,
134
+ onClose: () => {
135
+ searchQuery.value = "";
136
+ }
137
+ });
125
138
  const visibleOptions = vue.computed(() => {
126
139
  return (props.options ?? []).filter((opt) => !opt.hidden);
127
140
  });
@@ -143,35 +156,20 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
143
156
  }
144
157
  return props.placeholder || "Select...";
145
158
  });
146
- const updatePosition = () => {
147
- if (!triggerRef.value || !props.teleport) return;
148
- const rect = triggerRef.value.getBoundingClientRect();
149
- dropdownPosition.value = {
150
- top: rect.bottom + window.scrollY + 4,
151
- left: rect.left + window.scrollX,
152
- width: rect.width
153
- };
154
- };
155
159
  const open = () => {
156
160
  if (props.disabled) return;
157
- isOpen.value = true;
158
161
  searchQuery.value = "";
159
162
  highlightedIndex.value = filteredOptions.value.findIndex(
160
163
  (opt) => opt.value === modelValue.value
161
164
  );
165
+ openDropdown();
162
166
  vue.nextTick(() => {
163
167
  var _a;
164
- updatePosition();
165
168
  if (props.searchable) {
166
169
  (_a = searchInputRef.value) == null ? void 0 : _a.focus();
167
170
  }
168
171
  });
169
172
  };
170
- const close = () => {
171
- isOpen.value = false;
172
- searchQuery.value = "";
173
- highlightedIndex.value = -1;
174
- };
175
173
  const toggle = () => {
176
174
  if (isOpen.value) {
177
175
  close();
@@ -184,91 +182,23 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
184
182
  close();
185
183
  };
186
184
  const handleKeydown = (event) => {
187
- if (!isOpen.value) {
188
- if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
189
- event.preventDefault();
190
- open();
191
- }
192
- return;
193
- }
194
- switch (event.key) {
195
- case "ArrowDown":
196
- event.preventDefault();
197
- highlightedIndex.value = Math.min(
198
- highlightedIndex.value + 1,
199
- filteredOptions.value.length - 1
200
- );
201
- scrollToHighlighted();
202
- break;
203
- case "ArrowUp":
204
- event.preventDefault();
205
- highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0);
206
- scrollToHighlighted();
207
- break;
208
- case "Enter":
209
- event.preventDefault();
210
- if (highlightedIndex.value >= 0 && filteredOptions.value[highlightedIndex.value]) {
211
- selectOption(filteredOptions.value[highlightedIndex.value]);
212
- }
213
- break;
214
- case "Escape":
215
- event.preventDefault();
216
- close();
217
- break;
218
- case "Tab":
219
- close();
220
- break;
221
- }
222
- };
223
- const scrollToHighlighted = () => {
224
- vue.nextTick(() => {
225
- if (dropdownRef.value) {
226
- const highlighted = dropdownRef.value.querySelector(
227
- `[data-index="${highlightedIndex.value}"]`
228
- );
229
- if (highlighted) {
230
- highlighted.scrollIntoView({ block: "nearest" });
185
+ baseHandleKeydown(event, {
186
+ itemCount: filteredOptions.value.length,
187
+ onSelect: (index) => {
188
+ if (filteredOptions.value[index]) {
189
+ selectOption(filteredOptions.value[index]);
231
190
  }
232
- }
191
+ },
192
+ onOpen: open,
193
+ handleOpenKeys: true
233
194
  });
234
- };
235
- const handleClickOutside = (event) => {
236
- var _a, _b;
237
- const target = event.target;
238
- const isInsideTrigger = (_a = triggerRef.value) == null ? void 0 : _a.contains(target);
239
- const isInsideDropdown = (_b = dropdownRef.value) == null ? void 0 : _b.contains(target);
240
- if (!isInsideTrigger && !isInsideDropdown) {
241
- close();
195
+ if (isOpen.value) {
196
+ scrollToHighlighted(dropdownRef.value ?? null);
242
197
  }
243
198
  };
244
- vue.watch(isOpen, (newValue) => {
245
- if (newValue) {
246
- document.addEventListener("click", handleClickOutside);
247
- window.addEventListener("scroll", updatePosition, true);
248
- window.addEventListener("resize", updatePosition);
249
- } else {
250
- document.removeEventListener("click", handleClickOutside);
251
- window.removeEventListener("scroll", updatePosition, true);
252
- window.removeEventListener("resize", updatePosition);
253
- }
254
- });
255
199
  vue.watch(searchQuery, () => {
256
200
  highlightedIndex.value = 0;
257
201
  });
258
- vue.onUnmounted(() => {
259
- document.removeEventListener("click", handleClickOutside);
260
- window.removeEventListener("scroll", updatePosition, true);
261
- window.removeEventListener("resize", updatePosition);
262
- });
263
- const dropdownStyle = vue.computed(() => {
264
- if (!props.teleport) return {};
265
- return {
266
- position: "absolute",
267
- top: `${dropdownPosition.value.top}px`,
268
- left: `${dropdownPosition.value.left}px`,
269
- width: `${dropdownPosition.value.width}px`
270
- };
271
- });
272
202
  const triggerClasses = vue.computed(() => {
273
203
  const base = "flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm text-left transition";
274
204
  const state = props.disabled ? "cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-500" : isOpen.value ? "border-primary ring-2 ring-primary/20 bg-white dark:bg-gray-900" : "border-gray-300 bg-white hover:border-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600";
@@ -295,7 +225,7 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
295
225
  ]),
296
226
  vue.createVNode(vue.unref(vue$1.Icon), {
297
227
  icon: "lucide:chevron-down",
298
- class: vue.normalizeClass(["size-4 shrink-0 text-gray-400 transition-transform", isOpen.value && "rotate-180"])
228
+ class: vue.normalizeClass(["size-4 shrink-0 text-gray-400 transition-transform", vue.unref(isOpen) && "rotate-180"])
299
229
  }, null, 8, ["class"])
300
230
  ], 42, _hoisted_2$2),
301
231
  (vue.openBlock(), vue.createBlock(vue.Teleport, {
@@ -311,11 +241,11 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
311
241
  "leave-to-class": "opacity-0 scale-95"
312
242
  }, {
313
243
  default: vue.withCtx(() => [
314
- isOpen.value ? (vue.openBlock(), vue.createElementBlock("div", {
244
+ vue.unref(isOpen) ? (vue.openBlock(), vue.createElementBlock("div", {
315
245
  key: 0,
316
246
  ref_key: "dropdownRef",
317
247
  ref: dropdownRef,
318
- style: vue.normalizeStyle(dropdownStyle.value),
248
+ style: vue.normalizeStyle(vue.unref(dropdownStyle)),
319
249
  class: vue.normalizeClass([
320
250
  "z-[9999] max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800",
321
251
  !__props.teleport && "absolute mt-1 w-full"
@@ -349,7 +279,7 @@ const _sfc_main$3 = /* @__PURE__ */ vue.defineComponent({
349
279
  "data-index": index,
350
280
  class: vue.normalizeClass([
351
281
  "flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors",
352
- highlightedIndex.value === index && "bg-gray-100 dark:bg-gray-700",
282
+ vue.unref(highlightedIndex) === index && "bg-gray-100 dark:bg-gray-700",
353
283
  modelValue.value === option.value && "bg-primary/10 text-primary",
354
284
  modelValue.value !== option.value && "text-gray-700 dark:text-gray-200"
355
285
  ]),
@@ -569,4 +499,4 @@ exports._sfc_main$3 = _sfc_main$3;
569
499
  exports._sfc_main$4 = _sfc_main$2;
570
500
  exports._sfc_main$5 = _sfc_main$1;
571
501
  exports._sfc_main$6 = _sfc_main;
572
- //# sourceMappingURL=Switch.vue_vue_type_script_setup_true_lang-V-FtNcTd.cjs.map
502
+ //# sourceMappingURL=Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs","sources":["../src/components/form/FormLabel.vue","../src/components/form/FormHelp.vue","../src/components/form/FormInput.vue","../src/components/form/FormSelect.vue","../src/components/form/FormGroup.vue","../src/components/form/SearchInput.vue","../src/components/form/Switch.vue"],"sourcesContent":["<script lang=\"ts\" setup>\ndefineProps<{\n error?: string | boolean\n htmlFor?: string\n}>()\n</script>\n\n<template>\n <label\n :data-error=\"!!error\"\n :for=\"htmlFor ?? ''\"\n class=\"block text-sm font-medium text-gray-700 data-[error=true]:text-red-500 dark:text-gray-300\"\n >\n <slot />\n </label>\n</template>\n","<script lang=\"ts\" setup>\ndefineProps<{\n error?: boolean\n text?: string\n}>()\n</script>\n\n<template>\n <p\n :data-error=\"error\"\n class=\"mt-2 text-sm text-gray-400 peer-invalid:visible data-[error=true]:text-red-500 dark:text-gray-500\"\n >\n <slot>{{ text ?? '' }}</slot>\n </p>\n</template>\n","<script lang=\"ts\" setup>\nimport type { InputProps } from '@/types'\n\nwithDefaults(defineProps<InputProps>(), {\n type: 'text',\n})\n\nconst modelValue = defineModel<string>()\n</script>\n\n<template>\n <input\n :id=\"id ?? name ?? ''\"\n v-model=\"modelValue\"\n :disabled=\"disabled\"\n :name=\"name ?? ''\"\n :placeholder=\"placeholder\"\n :type=\"type\"\n class=\"mt-1 block w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-800 placeholder-gray-400 shadow-sm invalid:border-pink-500 invalid:text-pink-600 focus:border-primary focus:ring-1 focus:ring-primary focus:outline-none focus:invalid:border-pink-500 focus:invalid:ring-pink-500 disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-500 disabled:shadow-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:placeholder-gray-500 dark:focus:border-primary dark:focus:ring-primary dark:disabled:border-gray-800 dark:disabled:bg-gray-950 dark:disabled:text-gray-500\"\n v-bind=\"$attrs\"\n />\n</template>\n","<script lang=\"ts\" setup>\nimport { computed, ref, watch, nextTick } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport type { SelectProps, SelectOption } from '@/types'\nimport { useDropdown } from '@/composables/useDropdown'\n\nconst props = withDefaults(\n defineProps<\n SelectProps & {\n /** Use teleport to body to avoid overflow clipping */\n teleport?: boolean\n /** Show search input in dropdown */\n searchable?: boolean\n /** Text shown when no results match search */\n noResultsText?: string\n /** Custom class for the trigger button */\n triggerClass?: string\n }\n >(),\n {\n teleport: true,\n searchable: false,\n noResultsText: 'No results found',\n },\n)\n\nconst modelValue = defineModel<string | number | boolean | null>()\n\nconst searchQuery = ref('')\nconst triggerRef = ref<HTMLElement>()\nconst dropdownRef = ref<HTMLElement>()\nconst searchInputRef = ref<HTMLInputElement>()\n\nconst {\n isOpen,\n highlightedIndex,\n dropdownStyle,\n open: openDropdown,\n close,\n handleKeydown: baseHandleKeydown,\n scrollToHighlighted,\n} = useDropdown(triggerRef, dropdownRef, {\n teleport: props.teleport,\n gap: 4,\n onClose: () => {\n searchQuery.value = ''\n },\n})\n\nconst visibleOptions = computed(() => {\n return (props.options ?? []).filter((opt) => !opt.hidden)\n})\n\nconst filteredOptions = computed(() => {\n if (!props.searchable || !searchQuery.value) {\n return visibleOptions.value\n }\n const query = searchQuery.value.toLowerCase()\n return visibleOptions.value.filter((opt) =>\n opt.label.toLowerCase().includes(query) ||\n String(opt.value).toLowerCase().includes(query)\n )\n})\n\nconst selectedOption = computed(() => {\n return visibleOptions.value.find((opt) => opt.value === modelValue.value)\n})\n\nconst displayValue = computed(() => {\n if (selectedOption.value) {\n return selectedOption.value.label\n }\n return props.placeholder || 'Select...'\n})\n\nconst open = () => {\n if (props.disabled) return\n searchQuery.value = ''\n highlightedIndex.value = filteredOptions.value.findIndex(\n (opt) => opt.value === modelValue.value\n )\n openDropdown()\n nextTick(() => {\n if (props.searchable) {\n searchInputRef.value?.focus()\n }\n })\n}\n\nconst toggle = () => {\n if (isOpen.value) {\n close()\n } else {\n open()\n }\n}\n\nconst selectOption = (option: SelectOption) => {\n modelValue.value = option.value\n close()\n}\n\nconst handleKeydown = (event: KeyboardEvent) => {\n baseHandleKeydown(event, {\n itemCount: filteredOptions.value.length,\n onSelect: (index) => {\n if (filteredOptions.value[index]) {\n selectOption(filteredOptions.value[index])\n }\n },\n onOpen: open,\n handleOpenKeys: true,\n })\n if (isOpen.value) {\n scrollToHighlighted(dropdownRef.value ?? null)\n }\n}\n\nwatch(searchQuery, () => {\n highlightedIndex.value = 0\n})\n\nconst triggerClasses = computed(() => {\n const base = 'flex w-full items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm text-left transition'\n const state = props.disabled\n ? 'cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-500'\n : isOpen.value\n ? 'border-primary ring-2 ring-primary/20 bg-white dark:bg-gray-900'\n : 'border-gray-300 bg-white hover:border-gray-400 dark:border-gray-700 dark:bg-gray-900 dark:hover:border-gray-600'\n const text = selectedOption.value\n ? 'text-gray-800 dark:text-gray-200'\n : 'text-gray-400 dark:text-gray-500'\n return [base, state, text, props.triggerClass]\n})\n</script>\n\n<template>\n <div class=\"relative\">\n <!-- Trigger -->\n <button\n ref=\"triggerRef\"\n type=\"button\"\n :id=\"id ?? name ?? undefined\"\n :disabled=\"disabled\"\n :class=\"triggerClasses\"\n @click=\"toggle\"\n @keydown=\"handleKeydown\"\n >\n <slot name=\"selected\" :option=\"selectedOption\" :placeholder=\"placeholder\">\n <span class=\"flex-1 truncate\">{{ displayValue }}</span>\n </slot>\n <Icon\n icon=\"lucide:chevron-down\"\n :class=\"['size-4 shrink-0 text-gray-400 transition-transform', isOpen && 'rotate-180']\"\n />\n </button>\n\n <!-- Dropdown -->\n <Teleport to=\"body\" :disabled=\"!teleport\">\n <Transition\n enter-active-class=\"transition duration-100 ease-out\"\n enter-from-class=\"opacity-0 scale-95\"\n enter-to-class=\"opacity-100 scale-100\"\n leave-active-class=\"transition duration-75 ease-in\"\n leave-from-class=\"opacity-100 scale-100\"\n leave-to-class=\"opacity-0 scale-95\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n :style=\"dropdownStyle\"\n :class=\"[\n 'z-[9999] max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800',\n !teleport && 'absolute mt-1 w-full',\n ]\"\n >\n <!-- Search input -->\n <div v-if=\"searchable\" class=\"sticky top-0 border-b border-gray-200 bg-white p-2 dark:border-gray-700 dark:bg-gray-800\">\n <div class=\"flex items-center gap-2 rounded-md border border-gray-300 bg-gray-50 px-2 py-1.5 dark:border-gray-600 dark:bg-gray-900\">\n <Icon icon=\"lucide:search\" class=\"size-4 text-gray-400\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"flex-1 bg-transparent text-sm outline-none dark:text-white\"\n placeholder=\"Search...\"\n @keydown=\"handleKeydown\"\n />\n </div>\n </div>\n\n <!-- Empty state -->\n <div\n v-if=\"filteredOptions.length === 0\"\n class=\"px-4 py-3 text-sm text-gray-500 dark:text-gray-400\"\n >\n {{ noResultsText }}\n </div>\n\n <!-- Options -->\n <div class=\"py-1\">\n <button\n v-for=\"(option, index) in filteredOptions\"\n :key=\"String(option.value)\"\n type=\"button\"\n :data-index=\"index\"\n :class=\"[\n 'flex w-full items-center gap-2 px-3 py-2 text-left text-sm transition-colors',\n highlightedIndex === index && 'bg-gray-100 dark:bg-gray-700',\n modelValue === option.value && 'bg-primary/10 text-primary',\n modelValue !== option.value && 'text-gray-700 dark:text-gray-200',\n ]\"\n @click=\"selectOption(option)\"\n @mouseenter=\"highlightedIndex = index\"\n >\n <slot name=\"option\" :option=\"option\" :selected=\"modelValue === option.value\" :index=\"index\">\n <Icon\n v-if=\"modelValue === option.value\"\n icon=\"lucide:check\"\n class=\"size-4 shrink-0 text-primary\"\n />\n <span v-else class=\"size-4 shrink-0\" />\n <span class=\"flex-1\">{{ option.label }}</span>\n </slot>\n </button>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { computed } from 'vue'\nimport type { FormGroupProps } from '@/types'\nimport FormLabel from './FormLabel.vue'\nimport FormInput from './FormInput.vue'\nimport FormSelect from './FormSelect.vue'\nimport FormHelp from './FormHelp.vue'\n\nconst props = withDefaults(defineProps<FormGroupProps>(), {\n cols: 6,\n})\n\nconst modelValue = defineModel()\n\nconst computedCols = computed(() => `span ${props.cols} / span ${props.cols}`)\n\nconst isSelect = computed(() => props.select === true)\n</script>\n\n<template>\n <div :style=\"{ gridColumn: computedCols }\" :hidden=\"hidden\">\n <slot>\n <slot name=\"label\">\n <FormLabel v-if=\"label\" :html-for=\"id ?? name\" :error=\"error\">\n {{ label }}\n </FormLabel>\n </slot>\n\n <slot name=\"input\">\n <FormSelect\n v-if=\"isSelect\"\n v-model=\"modelValue\"\n v-bind=\"{ ...$attrs, ...$props }\"\n />\n <FormInput\n v-else\n v-model=\"modelValue\"\n v-bind=\"{ ...$attrs, ...$props }\"\n />\n </slot>\n\n <FormHelp v-if=\"error && typeof error === 'string'\" :error=\"true\">\n {{ error }}\n </FormHelp>\n </slot>\n </div>\n</template>\n","<script lang=\"ts\" setup>\nimport { Icon } from '@iconify/vue'\n\nconst {\n placeholder = 'Search...',\n icon = 'lucide:search',\n disabled = false,\n} = defineProps<{\n modelValue?: string\n placeholder?: string\n icon?: string\n disabled?: boolean\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst handleInput = (event: Event) => {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', target.value)\n}\n</script>\n\n<template>\n <div class=\"relative\">\n <Icon :icon=\"icon\" class=\"absolute top-1/2 left-3 h-5 w-5 -translate-y-1/2 text-gray-400\" />\n <input\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :value=\"modelValue\"\n class=\"focus:border-primary focus:ring-primary w-full rounded-lg border border-gray-300 bg-white py-2 pr-4 pl-10 text-gray-900 placeholder-gray-400 transition-colors disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100 dark:placeholder-gray-500\"\n type=\"text\"\n @input=\"handleInput\"\n />\n </div>\n</template>\n","<script lang=\"ts\" setup>\n\nconst props = withDefaults(\n defineProps<{\n /** v-model value */\n modelValue?: boolean\n /** Label text */\n label?: string\n /** Description text */\n description?: string\n /** Disabled state */\n disabled?: boolean\n /** Size variant */\n size?: 'sm' | 'md' | 'lg'\n }>(),\n {\n modelValue: false,\n size: 'md',\n },\n)\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n}>()\n\nconst toggle = () => {\n if (props.disabled) return\n emit('update:modelValue', !props.modelValue)\n}\n\nconst switchSizes = {\n sm: 'h-5 w-9',\n md: 'h-6 w-11',\n lg: 'h-7 w-14',\n}\n\nconst dotSizes = {\n sm: 'size-3',\n md: 'size-4',\n lg: 'size-5',\n}\n\nconst translateSizes = {\n sm: 'translate-x-5',\n md: 'translate-x-6',\n lg: 'translate-x-8',\n}\n</script>\n\n<template>\n <label\n class=\"inline-flex items-start gap-3\"\n :class=\"disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'\"\n >\n <button\n type=\"button\"\n role=\"switch\"\n :aria-checked=\"modelValue\"\n :disabled=\"disabled\"\n :class=\"[\n 'relative inline-flex shrink-0 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2',\n switchSizes[size],\n modelValue ? 'bg-primary' : 'bg-gray-300 dark:bg-gray-600',\n ]\"\n @click=\"toggle\"\n >\n <span\n :class=\"[\n 'inline-block transform rounded-full bg-white shadow-sm transition-transform',\n dotSizes[size],\n modelValue ? translateSizes[size] : 'translate-x-1',\n ]\"\n />\n </button>\n <div v-if=\"label || description\" class=\"flex flex-col\">\n <span v-if=\"label\" class=\"text-sm font-medium text-gray-900 dark:text-white\">\n {{ label }}\n </span>\n <span v-if=\"description\" class=\"text-sm text-gray-500 dark:text-gray-400\">\n {{ description }}\n </span>\n </div>\n </label>\n</template>\n"],"names":["_createElementBlock","_renderSlot","_useModel","_withDirectives","_openBlock","_mergeProps","$attrs","_hoisted_1","ref","useDropdown","computed","nextTick","watch","_createElementVNode","_hoisted_3","_toDisplayString","_createVNode","_unref","Icon","_createBlock","_Teleport","_Transition","_normalizeClass","_hoisted_4","_Fragment","_renderList","FormLabel","FormSelect","$props","FormInput","FormHelp"],"mappings":"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"}