cisse-vue-ui 0.2.7 → 0.3.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 (110) hide show
  1. package/README.md +666 -664
  2. package/dist/{Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js → DatePicker.vue_vue_type_script_setup_true_lang-DgGJ5-8v.js} +372 -60
  3. package/dist/DatePicker.vue_vue_type_script_setup_true_lang-DgGJ5-8v.js.map +1 -0
  4. package/dist/{Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs → DatePicker.vue_vue_type_script_setup_true_lang-qux1VNn0.cjs} +371 -59
  5. package/dist/DatePicker.vue_vue_type_script_setup_true_lang-qux1VNn0.cjs.map +1 -0
  6. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-B9DsCY8M.js.map +1 -1
  7. package/dist/Dropdown.vue_vue_type_script_setup_true_lang-nMP2OxXp.cjs.map +1 -1
  8. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-Bnw5L-xO.cjs.map +1 -1
  9. package/dist/PageLayout.vue_vue_type_script_setup_true_lang-D8uD3-Fe.js.map +1 -1
  10. package/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-Y1wvT4aS.js → Popover.vue_vue_type_script_setup_true_lang-Q7gRZsT9.js} +203 -43
  11. package/dist/Popover.vue_vue_type_script_setup_true_lang-Q7gRZsT9.js.map +1 -0
  12. package/dist/{CollapsibleCard.vue_vue_type_script_setup_true_lang-ClNZxjzF.cjs → Popover.vue_vue_type_script_setup_true_lang-dvlDTcf1.cjs} +191 -31
  13. package/dist/Popover.vue_vue_type_script_setup_true_lang-dvlDTcf1.cjs.map +1 -0
  14. package/dist/{EmptyState.vue_vue_type_script_setup_true_lang-mlqLBP5W.cjs → Skeleton.vue_vue_type_script_setup_true_lang-D2S5g9s5.cjs} +325 -72
  15. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-D2S5g9s5.cjs.map +1 -0
  16. package/dist/{EmptyState.vue_vue_type_script_setup_true_lang-CrVvFwXA.js → Skeleton.vue_vue_type_script_setup_true_lang-DRC4EADS.js} +326 -73
  17. package/dist/Skeleton.vue_vue_type_script_setup_true_lang-DRC4EADS.js.map +1 -0
  18. package/dist/_plugin-vue_export-helper-1tPrXgE0.js +11 -0
  19. package/dist/_plugin-vue_export-helper-1tPrXgE0.js.map +1 -0
  20. package/dist/_plugin-vue_export-helper-DM9IkUGy.cjs +10 -0
  21. package/dist/_plugin-vue_export-helper-DM9IkUGy.cjs.map +1 -0
  22. package/dist/cisse-vue-ui.css +83 -0
  23. package/dist/components/core/Avatar.stories.d.ts +12 -0
  24. package/dist/components/core/Button.stories.d.ts +18 -0
  25. package/dist/components/core/CardComponent.stories.d.ts +10 -0
  26. package/dist/components/core/CollapsibleCard.stories.d.ts +10 -0
  27. package/dist/components/core/Dropdown.stories.d.ts +11 -0
  28. package/dist/components/core/Popover.stories.d.ts +10 -0
  29. package/dist/components/core/Popover.vue.d.ts +42 -0
  30. package/dist/components/core/StatusBadge.stories.d.ts +13 -0
  31. package/dist/components/core/Stepper.stories.d.ts +12 -0
  32. package/dist/components/core/Tabs.stories.d.ts +10 -0
  33. package/dist/components/core/Tooltip.stories.d.ts +10 -0
  34. package/dist/components/core/Tooltip.vue.d.ts +32 -0
  35. package/dist/components/core/index.cjs +15 -13
  36. package/dist/components/core/index.cjs.map +1 -1
  37. package/dist/components/core/index.d.ts +4 -0
  38. package/dist/components/core/index.js +4 -2
  39. package/dist/components/feedback/Alert.stories.d.ts +13 -0
  40. package/dist/components/feedback/EmptyState.stories.d.ts +13 -0
  41. package/dist/components/feedback/LoadingSpinner.stories.d.ts +11 -0
  42. package/dist/components/feedback/Modal.stories.d.ts +11 -0
  43. package/dist/components/feedback/PaginationControls.stories.d.ts +12 -0
  44. package/dist/components/feedback/Progress.stories.d.ts +14 -0
  45. package/dist/components/feedback/Progress.vue.d.ts +30 -0
  46. package/dist/components/feedback/Skeleton.stories.d.ts +15 -0
  47. package/dist/components/feedback/Skeleton.vue.d.ts +19 -0
  48. package/dist/components/feedback/Toast.stories.d.ts +12 -0
  49. package/dist/components/feedback/Toast.vue.d.ts +23 -0
  50. package/dist/components/feedback/ToastContainer.vue.d.ts +22 -0
  51. package/dist/components/feedback/index.cjs +12 -8
  52. package/dist/components/feedback/index.cjs.map +1 -1
  53. package/dist/components/feedback/index.d.ts +8 -0
  54. package/dist/components/feedback/index.js +6 -2
  55. package/dist/components/form/Checkbox.stories.d.ts +13 -0
  56. package/dist/components/form/DatePicker.stories.d.ts +15 -0
  57. package/dist/components/form/DatePicker.vue.d.ts +34 -0
  58. package/dist/components/form/FormGroup.stories.d.ts +10 -0
  59. package/dist/components/form/FormInput.stories.d.ts +12 -0
  60. package/dist/components/form/FormSelect.stories.d.ts +11 -0
  61. package/dist/components/form/Slider.stories.d.ts +13 -0
  62. package/dist/components/form/Slider.vue.d.ts +29 -0
  63. package/dist/components/form/Switch.stories.d.ts +11 -0
  64. package/dist/components/form/index.cjs +10 -8
  65. package/dist/components/form/index.cjs.map +1 -1
  66. package/dist/components/form/index.d.ts +2 -0
  67. package/dist/components/form/index.js +3 -1
  68. package/dist/components/index.cjs +37 -29
  69. package/dist/components/index.cjs.map +1 -1
  70. package/dist/components/index.js +12 -4
  71. package/dist/components/layout/BaseLayout.stories.d.ts +9 -0
  72. package/dist/components/layout/BaseLayout.vue.d.ts +1 -1
  73. package/dist/components/layout/PageLayout.stories.d.ts +10 -0
  74. package/dist/components/type/BadgeType.stories.d.ts +13 -0
  75. package/dist/components/type/BooleanType.stories.d.ts +12 -0
  76. package/dist/components/type/DateType.stories.d.ts +12 -0
  77. package/dist/components/type/NumberType.stories.d.ts +11 -0
  78. package/dist/components/type/TextType.stories.d.ts +10 -0
  79. package/dist/composables/index.cjs +7 -6
  80. package/dist/composables/index.cjs.map +1 -1
  81. package/dist/composables/index.d.ts +1 -0
  82. package/dist/composables/index.js +3 -2
  83. package/dist/composables/useToast.d.ts +30 -0
  84. package/dist/index-BQtfDfYo.js +59 -0
  85. package/dist/index-BQtfDfYo.js.map +1 -0
  86. package/dist/index-CzzlUYhY.cjs +58 -0
  87. package/dist/index-CzzlUYhY.cjs.map +1 -0
  88. package/dist/index.cjs +45 -36
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.js +15 -6
  91. package/dist/style.css +1 -1
  92. package/dist/useDropdown-DHFnd259.cjs.map +1 -1
  93. package/dist/useDropdown-iVu14E6s.js.map +1 -1
  94. package/dist/{useModal-Aq8hn152.js → useToast-DT9hFfpM.js} +49 -1
  95. package/dist/useToast-DT9hFfpM.js.map +1 -0
  96. package/dist/{useModal-DDF_ZS8C.cjs → useToast-nJXpFz_M.cjs} +49 -1
  97. package/dist/useToast-nJXpFz_M.cjs.map +1 -0
  98. package/package.json +21 -2
  99. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-ClNZxjzF.cjs.map +0 -1
  100. package/dist/CollapsibleCard.vue_vue_type_script_setup_true_lang-Y1wvT4aS.js.map +0 -1
  101. package/dist/EmptyState.vue_vue_type_script_setup_true_lang-CrVvFwXA.js.map +0 -1
  102. package/dist/EmptyState.vue_vue_type_script_setup_true_lang-mlqLBP5W.cjs.map +0 -1
  103. package/dist/Switch.vue_vue_type_script_setup_true_lang-dRPxDu8I.js.map +0 -1
  104. package/dist/Switch.vue_vue_type_script_setup_true_lang-wRTWorCd.cjs.map +0 -1
  105. package/dist/index-CCWZb44b.cjs +0 -50
  106. package/dist/index-CCWZb44b.cjs.map +0 -1
  107. package/dist/index-RD8wq3O6.js +0 -51
  108. package/dist/index-RD8wq3O6.js.map +0 -1
  109. package/dist/useModal-Aq8hn152.js.map +0 -1
  110. package/dist/useModal-DDF_ZS8C.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useDropdown-DHFnd259.cjs","sources":["../src/composables/useDropdown.ts"],"sourcesContent":["import { ref, computed, watch, onUnmounted, nextTick, type Ref, type ComputedRef } from 'vue'\n\nexport interface UseDropdownOptions {\n /** Whether teleport is enabled (affects position calculation) */\n teleport?: boolean\n /** Alignment for position calculation */\n align?: 'left' | 'right'\n /** Gap between trigger and dropdown in pixels */\n gap?: number\n /** Callback when dropdown opens */\n onOpen?: () => void\n /** Callback when dropdown closes */\n onClose?: () => void\n}\n\nexport interface UseDropdownReturn {\n /** Whether the dropdown is currently open */\n isOpen: Ref<boolean>\n /** Current highlighted index for keyboard navigation */\n highlightedIndex: Ref<number>\n /** Calculated position for teleported dropdown */\n dropdownPosition: Ref<{ top: number; left: number; right: number; width: number }>\n /** Computed style object for teleported dropdown */\n dropdownStyle: ComputedRef<Record<string, string>>\n /** Open the dropdown */\n open: () => void\n /** Close the dropdown */\n close: () => void\n /** Toggle the dropdown */\n toggle: () => void\n /** Update position (call after DOM changes) */\n updatePosition: () => void\n /** Handle keyboard navigation */\n handleKeydown: (event: KeyboardEvent, options: KeyboardNavigationOptions) => void\n /** Scroll to highlighted item */\n scrollToHighlighted: (dropdownEl: HTMLElement | null) => void\n}\n\nexport interface KeyboardNavigationOptions {\n /** Total number of items to navigate */\n itemCount: number\n /** Called when Enter is pressed on a highlighted item */\n onSelect?: (index: number) => void\n /** Called when the dropdown should open (Space/Enter/ArrowDown when closed) */\n onOpen?: () => void\n /** Whether to handle open keys (Space/Enter/ArrowDown) when closed */\n handleOpenKeys?: boolean\n}\n\nexport function useDropdown(\n triggerRef: Ref<HTMLElement | null | undefined>,\n dropdownRef: Ref<HTMLElement | null | undefined>,\n options: UseDropdownOptions = {}\n): UseDropdownReturn {\n const { teleport = true, align = 'left', gap = 8, onOpen, onClose } = options\n\n const isOpen = ref(false)\n const highlightedIndex = ref(-1)\n const dropdownPosition = ref({ top: 0, left: 0, right: 0, width: 0 })\n\n const updatePosition = () => {\n if (!triggerRef.value || !teleport) return\n const rect = triggerRef.value.getBoundingClientRect()\n dropdownPosition.value = {\n top: rect.bottom + window.scrollY + gap,\n left: rect.left + window.scrollX,\n right: window.innerWidth - rect.right - window.scrollX,\n width: rect.width,\n }\n }\n\n const open = () => {\n isOpen.value = true\n nextTick(updatePosition)\n onOpen?.()\n }\n\n const close = () => {\n isOpen.value = false\n highlightedIndex.value = -1\n onClose?.()\n }\n\n const toggle = () => {\n if (isOpen.value) {\n close()\n } else {\n open()\n }\n }\n\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Node\n const isInsideTrigger = triggerRef.value?.contains(target)\n const isInsideDropdown = dropdownRef.value?.contains(target)\n if (!isInsideTrigger && !isInsideDropdown) {\n close()\n }\n }\n\n const scrollToHighlighted = (dropdownEl: HTMLElement | null) => {\n nextTick(() => {\n if (dropdownEl) {\n const highlighted = dropdownEl.querySelector(\n `[data-index=\"${highlightedIndex.value}\"]`\n ) as HTMLElement\n if (highlighted) {\n highlighted.scrollIntoView({ block: 'nearest' })\n }\n }\n })\n }\n\n const handleKeydown = (event: KeyboardEvent, navOptions: KeyboardNavigationOptions) => {\n const { itemCount, onSelect, onOpen: onOpenNav, handleOpenKeys = false } = navOptions\n\n if (!isOpen.value) {\n if (handleOpenKeys && (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown')) {\n event.preventDefault()\n onOpenNav?.()\n open()\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n highlightedIndex.value = Math.min(highlightedIndex.value + 1, itemCount - 1)\n break\n case 'ArrowUp':\n event.preventDefault()\n highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)\n break\n case 'Enter':\n event.preventDefault()\n if (highlightedIndex.value >= 0) {\n onSelect?.(highlightedIndex.value)\n }\n break\n case 'Escape':\n event.preventDefault()\n close()\n break\n case 'Tab':\n close()\n break\n }\n }\n\n const dropdownStyle = computed(() => {\n if (!teleport) return {} as Record<string, string>\n return {\n position: 'absolute',\n top: `${dropdownPosition.value.top}px`,\n left: align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\n right: align === 'right' ? `${dropdownPosition.value.right}px` : 'auto',\n width: `${dropdownPosition.value.width}px`,\n } as Record<string, string>\n })\n\n // Event listener management\n watch(isOpen, (newValue) => {\n if (newValue) {\n document.addEventListener('click', handleClickOutside)\n window.addEventListener('scroll', updatePosition, true)\n window.addEventListener('resize', updatePosition)\n } else {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n }\n })\n\n onUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n })\n\n return {\n isOpen,\n highlightedIndex,\n dropdownPosition,\n dropdownStyle,\n open,\n close,\n toggle,\n updatePosition,\n handleKeydown,\n scrollToHighlighted,\n }\n}\n"],"names":["ref","nextTick","computed","watch","onUnmounted"],"mappings":"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"}
1
+ {"version":3,"file":"useDropdown-DHFnd259.cjs","sources":["../src/composables/useDropdown.ts"],"sourcesContent":["import { ref, computed, watch, onUnmounted, nextTick, type Ref, type ComputedRef } from 'vue'\r\n\r\nexport interface UseDropdownOptions {\r\n /** Whether teleport is enabled (affects position calculation) */\r\n teleport?: boolean\r\n /** Alignment for position calculation */\r\n align?: 'left' | 'right'\r\n /** Gap between trigger and dropdown in pixels */\r\n gap?: number\r\n /** Callback when dropdown opens */\r\n onOpen?: () => void\r\n /** Callback when dropdown closes */\r\n onClose?: () => void\r\n}\r\n\r\nexport interface UseDropdownReturn {\r\n /** Whether the dropdown is currently open */\r\n isOpen: Ref<boolean>\r\n /** Current highlighted index for keyboard navigation */\r\n highlightedIndex: Ref<number>\r\n /** Calculated position for teleported dropdown */\r\n dropdownPosition: Ref<{ top: number; left: number; right: number; width: number }>\r\n /** Computed style object for teleported dropdown */\r\n dropdownStyle: ComputedRef<Record<string, string>>\r\n /** Open the dropdown */\r\n open: () => void\r\n /** Close the dropdown */\r\n close: () => void\r\n /** Toggle the dropdown */\r\n toggle: () => void\r\n /** Update position (call after DOM changes) */\r\n updatePosition: () => void\r\n /** Handle keyboard navigation */\r\n handleKeydown: (event: KeyboardEvent, options: KeyboardNavigationOptions) => void\r\n /** Scroll to highlighted item */\r\n scrollToHighlighted: (dropdownEl: HTMLElement | null) => void\r\n}\r\n\r\nexport interface KeyboardNavigationOptions {\r\n /** Total number of items to navigate */\r\n itemCount: number\r\n /** Called when Enter is pressed on a highlighted item */\r\n onSelect?: (index: number) => void\r\n /** Called when the dropdown should open (Space/Enter/ArrowDown when closed) */\r\n onOpen?: () => void\r\n /** Whether to handle open keys (Space/Enter/ArrowDown) when closed */\r\n handleOpenKeys?: boolean\r\n}\r\n\r\nexport function useDropdown(\r\n triggerRef: Ref<HTMLElement | null | undefined>,\r\n dropdownRef: Ref<HTMLElement | null | undefined>,\r\n options: UseDropdownOptions = {}\r\n): UseDropdownReturn {\r\n const { teleport = true, align = 'left', gap = 8, onOpen, onClose } = options\r\n\r\n const isOpen = ref(false)\r\n const highlightedIndex = ref(-1)\r\n const dropdownPosition = ref({ top: 0, left: 0, right: 0, width: 0 })\r\n\r\n const updatePosition = () => {\r\n if (!triggerRef.value || !teleport) return\r\n const rect = triggerRef.value.getBoundingClientRect()\r\n dropdownPosition.value = {\r\n top: rect.bottom + window.scrollY + gap,\r\n left: rect.left + window.scrollX,\r\n right: window.innerWidth - rect.right - window.scrollX,\r\n width: rect.width,\r\n }\r\n }\r\n\r\n const open = () => {\r\n isOpen.value = true\r\n nextTick(updatePosition)\r\n onOpen?.()\r\n }\r\n\r\n const close = () => {\r\n isOpen.value = false\r\n highlightedIndex.value = -1\r\n onClose?.()\r\n }\r\n\r\n const toggle = () => {\r\n if (isOpen.value) {\r\n close()\r\n } else {\r\n open()\r\n }\r\n }\r\n\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as Node\r\n const isInsideTrigger = triggerRef.value?.contains(target)\r\n const isInsideDropdown = dropdownRef.value?.contains(target)\r\n if (!isInsideTrigger && !isInsideDropdown) {\r\n close()\r\n }\r\n }\r\n\r\n const scrollToHighlighted = (dropdownEl: HTMLElement | null) => {\r\n nextTick(() => {\r\n if (dropdownEl) {\r\n const highlighted = dropdownEl.querySelector(\r\n `[data-index=\"${highlightedIndex.value}\"]`\r\n ) as HTMLElement\r\n if (highlighted) {\r\n highlighted.scrollIntoView({ block: 'nearest' })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const handleKeydown = (event: KeyboardEvent, navOptions: KeyboardNavigationOptions) => {\r\n const { itemCount, onSelect, onOpen: onOpenNav, handleOpenKeys = false } = navOptions\r\n\r\n if (!isOpen.value) {\r\n if (handleOpenKeys && (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown')) {\r\n event.preventDefault()\r\n onOpenNav?.()\r\n open()\r\n }\r\n return\r\n }\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n event.preventDefault()\r\n highlightedIndex.value = Math.min(highlightedIndex.value + 1, itemCount - 1)\r\n break\r\n case 'ArrowUp':\r\n event.preventDefault()\r\n highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)\r\n break\r\n case 'Enter':\r\n event.preventDefault()\r\n if (highlightedIndex.value >= 0) {\r\n onSelect?.(highlightedIndex.value)\r\n }\r\n break\r\n case 'Escape':\r\n event.preventDefault()\r\n close()\r\n break\r\n case 'Tab':\r\n close()\r\n break\r\n }\r\n }\r\n\r\n const dropdownStyle = computed(() => {\r\n if (!teleport) return {} as Record<string, string>\r\n return {\r\n position: 'absolute',\r\n top: `${dropdownPosition.value.top}px`,\r\n left: align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\r\n right: align === 'right' ? `${dropdownPosition.value.right}px` : 'auto',\r\n width: `${dropdownPosition.value.width}px`,\r\n } as Record<string, string>\r\n })\r\n\r\n // Event listener management\r\n watch(isOpen, (newValue) => {\r\n if (newValue) {\r\n document.addEventListener('click', handleClickOutside)\r\n window.addEventListener('scroll', updatePosition, true)\r\n window.addEventListener('resize', updatePosition)\r\n } else {\r\n document.removeEventListener('click', handleClickOutside)\r\n window.removeEventListener('scroll', updatePosition, true)\r\n window.removeEventListener('resize', updatePosition)\r\n }\r\n })\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside)\r\n window.removeEventListener('scroll', updatePosition, true)\r\n window.removeEventListener('resize', updatePosition)\r\n })\r\n\r\n return {\r\n isOpen,\r\n highlightedIndex,\r\n dropdownPosition,\r\n dropdownStyle,\r\n open,\r\n close,\r\n toggle,\r\n updatePosition,\r\n handleKeydown,\r\n scrollToHighlighted,\r\n }\r\n}\r\n"],"names":["ref","nextTick","computed","watch","onUnmounted"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDropdown-iVu14E6s.js","sources":["../src/composables/useDropdown.ts"],"sourcesContent":["import { ref, computed, watch, onUnmounted, nextTick, type Ref, type ComputedRef } from 'vue'\n\nexport interface UseDropdownOptions {\n /** Whether teleport is enabled (affects position calculation) */\n teleport?: boolean\n /** Alignment for position calculation */\n align?: 'left' | 'right'\n /** Gap between trigger and dropdown in pixels */\n gap?: number\n /** Callback when dropdown opens */\n onOpen?: () => void\n /** Callback when dropdown closes */\n onClose?: () => void\n}\n\nexport interface UseDropdownReturn {\n /** Whether the dropdown is currently open */\n isOpen: Ref<boolean>\n /** Current highlighted index for keyboard navigation */\n highlightedIndex: Ref<number>\n /** Calculated position for teleported dropdown */\n dropdownPosition: Ref<{ top: number; left: number; right: number; width: number }>\n /** Computed style object for teleported dropdown */\n dropdownStyle: ComputedRef<Record<string, string>>\n /** Open the dropdown */\n open: () => void\n /** Close the dropdown */\n close: () => void\n /** Toggle the dropdown */\n toggle: () => void\n /** Update position (call after DOM changes) */\n updatePosition: () => void\n /** Handle keyboard navigation */\n handleKeydown: (event: KeyboardEvent, options: KeyboardNavigationOptions) => void\n /** Scroll to highlighted item */\n scrollToHighlighted: (dropdownEl: HTMLElement | null) => void\n}\n\nexport interface KeyboardNavigationOptions {\n /** Total number of items to navigate */\n itemCount: number\n /** Called when Enter is pressed on a highlighted item */\n onSelect?: (index: number) => void\n /** Called when the dropdown should open (Space/Enter/ArrowDown when closed) */\n onOpen?: () => void\n /** Whether to handle open keys (Space/Enter/ArrowDown) when closed */\n handleOpenKeys?: boolean\n}\n\nexport function useDropdown(\n triggerRef: Ref<HTMLElement | null | undefined>,\n dropdownRef: Ref<HTMLElement | null | undefined>,\n options: UseDropdownOptions = {}\n): UseDropdownReturn {\n const { teleport = true, align = 'left', gap = 8, onOpen, onClose } = options\n\n const isOpen = ref(false)\n const highlightedIndex = ref(-1)\n const dropdownPosition = ref({ top: 0, left: 0, right: 0, width: 0 })\n\n const updatePosition = () => {\n if (!triggerRef.value || !teleport) return\n const rect = triggerRef.value.getBoundingClientRect()\n dropdownPosition.value = {\n top: rect.bottom + window.scrollY + gap,\n left: rect.left + window.scrollX,\n right: window.innerWidth - rect.right - window.scrollX,\n width: rect.width,\n }\n }\n\n const open = () => {\n isOpen.value = true\n nextTick(updatePosition)\n onOpen?.()\n }\n\n const close = () => {\n isOpen.value = false\n highlightedIndex.value = -1\n onClose?.()\n }\n\n const toggle = () => {\n if (isOpen.value) {\n close()\n } else {\n open()\n }\n }\n\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Node\n const isInsideTrigger = triggerRef.value?.contains(target)\n const isInsideDropdown = dropdownRef.value?.contains(target)\n if (!isInsideTrigger && !isInsideDropdown) {\n close()\n }\n }\n\n const scrollToHighlighted = (dropdownEl: HTMLElement | null) => {\n nextTick(() => {\n if (dropdownEl) {\n const highlighted = dropdownEl.querySelector(\n `[data-index=\"${highlightedIndex.value}\"]`\n ) as HTMLElement\n if (highlighted) {\n highlighted.scrollIntoView({ block: 'nearest' })\n }\n }\n })\n }\n\n const handleKeydown = (event: KeyboardEvent, navOptions: KeyboardNavigationOptions) => {\n const { itemCount, onSelect, onOpen: onOpenNav, handleOpenKeys = false } = navOptions\n\n if (!isOpen.value) {\n if (handleOpenKeys && (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown')) {\n event.preventDefault()\n onOpenNav?.()\n open()\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n highlightedIndex.value = Math.min(highlightedIndex.value + 1, itemCount - 1)\n break\n case 'ArrowUp':\n event.preventDefault()\n highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)\n break\n case 'Enter':\n event.preventDefault()\n if (highlightedIndex.value >= 0) {\n onSelect?.(highlightedIndex.value)\n }\n break\n case 'Escape':\n event.preventDefault()\n close()\n break\n case 'Tab':\n close()\n break\n }\n }\n\n const dropdownStyle = computed(() => {\n if (!teleport) return {} as Record<string, string>\n return {\n position: 'absolute',\n top: `${dropdownPosition.value.top}px`,\n left: align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\n right: align === 'right' ? `${dropdownPosition.value.right}px` : 'auto',\n width: `${dropdownPosition.value.width}px`,\n } as Record<string, string>\n })\n\n // Event listener management\n watch(isOpen, (newValue) => {\n if (newValue) {\n document.addEventListener('click', handleClickOutside)\n window.addEventListener('scroll', updatePosition, true)\n window.addEventListener('resize', updatePosition)\n } else {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n }\n })\n\n onUnmounted(() => {\n document.removeEventListener('click', handleClickOutside)\n window.removeEventListener('scroll', updatePosition, true)\n window.removeEventListener('resize', updatePosition)\n })\n\n return {\n isOpen,\n highlightedIndex,\n dropdownPosition,\n dropdownStyle,\n open,\n close,\n toggle,\n updatePosition,\n handleKeydown,\n scrollToHighlighted,\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useDropdown-iVu14E6s.js","sources":["../src/composables/useDropdown.ts"],"sourcesContent":["import { ref, computed, watch, onUnmounted, nextTick, type Ref, type ComputedRef } from 'vue'\r\n\r\nexport interface UseDropdownOptions {\r\n /** Whether teleport is enabled (affects position calculation) */\r\n teleport?: boolean\r\n /** Alignment for position calculation */\r\n align?: 'left' | 'right'\r\n /** Gap between trigger and dropdown in pixels */\r\n gap?: number\r\n /** Callback when dropdown opens */\r\n onOpen?: () => void\r\n /** Callback when dropdown closes */\r\n onClose?: () => void\r\n}\r\n\r\nexport interface UseDropdownReturn {\r\n /** Whether the dropdown is currently open */\r\n isOpen: Ref<boolean>\r\n /** Current highlighted index for keyboard navigation */\r\n highlightedIndex: Ref<number>\r\n /** Calculated position for teleported dropdown */\r\n dropdownPosition: Ref<{ top: number; left: number; right: number; width: number }>\r\n /** Computed style object for teleported dropdown */\r\n dropdownStyle: ComputedRef<Record<string, string>>\r\n /** Open the dropdown */\r\n open: () => void\r\n /** Close the dropdown */\r\n close: () => void\r\n /** Toggle the dropdown */\r\n toggle: () => void\r\n /** Update position (call after DOM changes) */\r\n updatePosition: () => void\r\n /** Handle keyboard navigation */\r\n handleKeydown: (event: KeyboardEvent, options: KeyboardNavigationOptions) => void\r\n /** Scroll to highlighted item */\r\n scrollToHighlighted: (dropdownEl: HTMLElement | null) => void\r\n}\r\n\r\nexport interface KeyboardNavigationOptions {\r\n /** Total number of items to navigate */\r\n itemCount: number\r\n /** Called when Enter is pressed on a highlighted item */\r\n onSelect?: (index: number) => void\r\n /** Called when the dropdown should open (Space/Enter/ArrowDown when closed) */\r\n onOpen?: () => void\r\n /** Whether to handle open keys (Space/Enter/ArrowDown) when closed */\r\n handleOpenKeys?: boolean\r\n}\r\n\r\nexport function useDropdown(\r\n triggerRef: Ref<HTMLElement | null | undefined>,\r\n dropdownRef: Ref<HTMLElement | null | undefined>,\r\n options: UseDropdownOptions = {}\r\n): UseDropdownReturn {\r\n const { teleport = true, align = 'left', gap = 8, onOpen, onClose } = options\r\n\r\n const isOpen = ref(false)\r\n const highlightedIndex = ref(-1)\r\n const dropdownPosition = ref({ top: 0, left: 0, right: 0, width: 0 })\r\n\r\n const updatePosition = () => {\r\n if (!triggerRef.value || !teleport) return\r\n const rect = triggerRef.value.getBoundingClientRect()\r\n dropdownPosition.value = {\r\n top: rect.bottom + window.scrollY + gap,\r\n left: rect.left + window.scrollX,\r\n right: window.innerWidth - rect.right - window.scrollX,\r\n width: rect.width,\r\n }\r\n }\r\n\r\n const open = () => {\r\n isOpen.value = true\r\n nextTick(updatePosition)\r\n onOpen?.()\r\n }\r\n\r\n const close = () => {\r\n isOpen.value = false\r\n highlightedIndex.value = -1\r\n onClose?.()\r\n }\r\n\r\n const toggle = () => {\r\n if (isOpen.value) {\r\n close()\r\n } else {\r\n open()\r\n }\r\n }\r\n\r\n const handleClickOutside = (event: MouseEvent) => {\r\n const target = event.target as Node\r\n const isInsideTrigger = triggerRef.value?.contains(target)\r\n const isInsideDropdown = dropdownRef.value?.contains(target)\r\n if (!isInsideTrigger && !isInsideDropdown) {\r\n close()\r\n }\r\n }\r\n\r\n const scrollToHighlighted = (dropdownEl: HTMLElement | null) => {\r\n nextTick(() => {\r\n if (dropdownEl) {\r\n const highlighted = dropdownEl.querySelector(\r\n `[data-index=\"${highlightedIndex.value}\"]`\r\n ) as HTMLElement\r\n if (highlighted) {\r\n highlighted.scrollIntoView({ block: 'nearest' })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const handleKeydown = (event: KeyboardEvent, navOptions: KeyboardNavigationOptions) => {\r\n const { itemCount, onSelect, onOpen: onOpenNav, handleOpenKeys = false } = navOptions\r\n\r\n if (!isOpen.value) {\r\n if (handleOpenKeys && (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown')) {\r\n event.preventDefault()\r\n onOpenNav?.()\r\n open()\r\n }\r\n return\r\n }\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n event.preventDefault()\r\n highlightedIndex.value = Math.min(highlightedIndex.value + 1, itemCount - 1)\r\n break\r\n case 'ArrowUp':\r\n event.preventDefault()\r\n highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)\r\n break\r\n case 'Enter':\r\n event.preventDefault()\r\n if (highlightedIndex.value >= 0) {\r\n onSelect?.(highlightedIndex.value)\r\n }\r\n break\r\n case 'Escape':\r\n event.preventDefault()\r\n close()\r\n break\r\n case 'Tab':\r\n close()\r\n break\r\n }\r\n }\r\n\r\n const dropdownStyle = computed(() => {\r\n if (!teleport) return {} as Record<string, string>\r\n return {\r\n position: 'absolute',\r\n top: `${dropdownPosition.value.top}px`,\r\n left: align === 'right' ? 'auto' : `${dropdownPosition.value.left}px`,\r\n right: align === 'right' ? `${dropdownPosition.value.right}px` : 'auto',\r\n width: `${dropdownPosition.value.width}px`,\r\n } as Record<string, string>\r\n })\r\n\r\n // Event listener management\r\n watch(isOpen, (newValue) => {\r\n if (newValue) {\r\n document.addEventListener('click', handleClickOutside)\r\n window.addEventListener('scroll', updatePosition, true)\r\n window.addEventListener('resize', updatePosition)\r\n } else {\r\n document.removeEventListener('click', handleClickOutside)\r\n window.removeEventListener('scroll', updatePosition, true)\r\n window.removeEventListener('resize', updatePosition)\r\n }\r\n })\r\n\r\n onUnmounted(() => {\r\n document.removeEventListener('click', handleClickOutside)\r\n window.removeEventListener('scroll', updatePosition, true)\r\n window.removeEventListener('resize', updatePosition)\r\n })\r\n\r\n return {\r\n isOpen,\r\n highlightedIndex,\r\n dropdownPosition,\r\n dropdownStyle,\r\n open,\r\n close,\r\n toggle,\r\n updatePosition,\r\n handleKeydown,\r\n scrollToHighlighted,\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -159,11 +159,59 @@ function useModal(options) {
159
159
  function useModals(modals) {
160
160
  return modals;
161
161
  }
162
+ const toasts = ref([]);
163
+ let toastId = 0;
164
+ function useToast() {
165
+ const add = (options) => {
166
+ const id = `toast-${++toastId}`;
167
+ const toast = {
168
+ id,
169
+ message: options.message,
170
+ type: options.type || "info",
171
+ title: options.title,
172
+ duration: options.duration ?? 5e3
173
+ };
174
+ toasts.value.push(toast);
175
+ return id;
176
+ };
177
+ const remove = (id) => {
178
+ const index = toasts.value.findIndex((t) => t.id === id);
179
+ if (index > -1) {
180
+ toasts.value.splice(index, 1);
181
+ }
182
+ };
183
+ const clear = () => {
184
+ toasts.value = [];
185
+ };
186
+ const success = (message, title) => {
187
+ return add({ message, title, type: "success" });
188
+ };
189
+ const error = (message, title) => {
190
+ return add({ message, title, type: "error" });
191
+ };
192
+ const warning = (message, title) => {
193
+ return add({ message, title, type: "warning" });
194
+ };
195
+ const info = (message, title) => {
196
+ return add({ message, title, type: "info" });
197
+ };
198
+ return {
199
+ toasts,
200
+ add,
201
+ remove,
202
+ clear,
203
+ success,
204
+ error,
205
+ warning,
206
+ info
207
+ };
208
+ }
162
209
  export {
163
210
  useDarkMode as a,
164
211
  useExportCSV as b,
165
212
  useModal as c,
166
213
  useModals as d,
214
+ useToast as e,
167
215
  useNotifications as u
168
216
  };
169
- //# sourceMappingURL=useModal-Aq8hn152.js.map
217
+ //# sourceMappingURL=useToast-DT9hFfpM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToast-DT9hFfpM.js","sources":["../src/composables/useNotifications.ts","../src/composables/useDarkMode.ts","../src/composables/useExportCSV.ts","../src/composables/useModal.ts","../src/composables/useToast.ts"],"sourcesContent":["import { ref, readonly } from 'vue'\nimport type { Notification, NotificationType, NotificationOptions } from '@/types'\n\n/**\n * Composable for managing notifications/toasts\n * Standalone implementation without Pinia dependency\n */\nexport function useNotifications() {\n const notifications = ref<Notification[]>([])\n let idCounter = 0\n\n const generateId = () => `notification-${++idCounter}-${Date.now()}`\n\n const notify = (\n type: NotificationType,\n message: string,\n options: NotificationOptions = {},\n ): string => {\n const id = generateId()\n const notification: Notification = {\n id,\n type,\n message,\n title: options.title ?? null,\n duration: options.duration ?? 5000,\n }\n\n notifications.value.push(notification)\n\n if (notification.duration && notification.duration > 0) {\n setTimeout(() => remove(id), notification.duration)\n }\n\n return id\n }\n\n const success = (message: string, options?: NotificationOptions) =>\n notify('success', message, options)\n\n const warning = (message: string, options?: NotificationOptions) =>\n notify('warning', message, options)\n\n const error = (message: string, options?: NotificationOptions) =>\n notify('error', message, options)\n\n const info = (message: string, options?: NotificationOptions) =>\n notify('info', message, options)\n\n const remove = (id: string) => {\n notifications.value = notifications.value.filter((n) => n.id !== id)\n }\n\n const clear = () => {\n notifications.value = []\n }\n\n return {\n notifications: readonly(notifications),\n notify,\n success,\n warning,\n error,\n info,\n remove,\n clear,\n }\n}\n","import { ref, watch, onMounted } from 'vue'\n\nexport interface DarkModeOptions {\n selector?: string\n attribute?: string\n storageKey?: string\n defaultValue?: boolean\n}\n\n/**\n * Composable for managing dark mode state\n */\nexport function useDarkMode(options: DarkModeOptions = {}) {\n const {\n selector = 'html',\n attribute = 'class',\n storageKey = 'dark-mode',\n defaultValue = false,\n } = options\n\n const isDark = ref(defaultValue)\n\n const getInitialValue = (): boolean => {\n if (typeof window === 'undefined') return defaultValue\n\n const stored = localStorage.getItem(storageKey)\n if (stored !== null) {\n return stored === 'true'\n }\n\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n }\n\n const updateDOM = (dark: boolean) => {\n if (typeof document === 'undefined') return\n\n const element = document.querySelector(selector)\n if (!element) return\n\n if (attribute === 'class') {\n element.classList.toggle('dark', dark)\n } else {\n element.setAttribute(attribute, dark ? 'dark' : 'light')\n }\n }\n\n const toggle = () => {\n isDark.value = !isDark.value\n }\n\n const set = (value: boolean) => {\n isDark.value = value\n }\n\n watch(isDark, (newValue) => {\n updateDOM(newValue)\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(storageKey, String(newValue))\n }\n })\n\n onMounted(() => {\n isDark.value = getInitialValue()\n updateDOM(isDark.value)\n })\n\n return {\n isDark,\n toggle,\n set,\n }\n}\n","/**\n * Composable for exporting data to CSV format\n */\nexport function useExportCSV() {\n const escapeCSV = (value: unknown): string => {\n if (value === null || value === undefined) return ''\n const str = String(value)\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n }\n\n const exportToCSV = <T extends Record<string, unknown>>(\n data: T[],\n columns: { key: keyof T; label: string }[],\n filename: string = 'export.csv',\n ) => {\n if (!data || data.length === 0) {\n console.warn('No data to export')\n return\n }\n\n // Create header row\n const headers = columns.map((col) => escapeCSV(col.label)).join(',')\n\n // Create data rows\n const rows = data.map((item) =>\n columns.map((col) => escapeCSV(item[col.key])).join(','),\n )\n\n // Combine header and rows\n const csv = [headers, ...rows].join('\\n')\n\n // Create and trigger download\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.setAttribute('href', url)\n link.setAttribute('download', filename)\n link.style.visibility = 'hidden'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n }\n\n return {\n exportToCSV,\n escapeCSV,\n }\n}\n","import { ref, type Ref } from 'vue'\r\n\r\nexport interface UseModalReturn<T = unknown> {\r\n /** Whether the modal is currently open */\r\n isOpen: Ref<boolean>\r\n /** Data associated with the modal (e.g., item being edited) */\r\n data: Ref<T | null>\r\n /** Open the modal, optionally with data */\r\n open: (newData?: T) => void\r\n /** Close the modal and clear data */\r\n close: () => void\r\n /** Toggle the modal state */\r\n toggle: () => void\r\n}\r\n\r\n/**\r\n * Composable for managing modal state\r\n *\r\n * @example\r\n * ```ts\r\n * // Simple modal\r\n * const createModal = useModal()\r\n * createModal.open()\r\n * createModal.close()\r\n *\r\n * // Modal with data (e.g., for editing)\r\n * const editModal = useModal<User>()\r\n * editModal.open(selectedUser)\r\n * // Access editModal.data.value in modal\r\n *\r\n * // With onClose callback\r\n * const deleteModal = useModal<Item>({ onClose: () => refetch() })\r\n * ```\r\n */\r\nexport function useModal<T = unknown>(options?: {\r\n /** Initial open state */\r\n initialOpen?: boolean\r\n /** Initial data */\r\n initialData?: T | null\r\n /** Callback when modal opens */\r\n onOpen?: (data: T | null) => void\r\n /** Callback when modal closes */\r\n onClose?: () => void\r\n}): UseModalReturn<T> {\r\n const {\r\n initialOpen = false,\r\n initialData = null,\r\n onOpen,\r\n onClose,\r\n } = options ?? {}\r\n\r\n const isOpen = ref(initialOpen)\r\n const data = ref<T | null>(initialData) as Ref<T | null>\r\n\r\n const open = (newData?: T) => {\r\n data.value = newData ?? null\r\n isOpen.value = true\r\n onOpen?.(data.value)\r\n }\r\n\r\n const close = () => {\r\n isOpen.value = false\r\n data.value = null\r\n onClose?.()\r\n }\r\n\r\n const toggle = () => {\r\n if (isOpen.value) {\r\n close()\r\n } else {\r\n open()\r\n }\r\n }\r\n\r\n return {\r\n isOpen,\r\n data,\r\n open,\r\n close,\r\n toggle,\r\n }\r\n}\r\n\r\n/**\r\n * Create multiple related modals at once\r\n * Useful when a page has several modals (create, edit, delete, etc.)\r\n *\r\n * @example\r\n * ```ts\r\n * const modals = useModals({\r\n * create: useModal(),\r\n * edit: useModal<User>(),\r\n * delete: useModal<User>(),\r\n * })\r\n *\r\n * modals.create.open()\r\n * modals.edit.open(user)\r\n * modals.delete.close()\r\n * ```\r\n */\r\nexport function useModals<T extends Record<string, UseModalReturn<unknown>>>(\r\n modals: T\r\n): T {\r\n return modals\r\n}\r\n","import { ref } from 'vue'\r\nimport type { ToastType } from '@/components/feedback/Toast.vue'\r\nimport type { ToastItem } from '@/components/feedback/ToastContainer.vue'\r\n\r\nexport interface ToastOptions {\r\n message: string\r\n type?: ToastType\r\n title?: string\r\n duration?: number\r\n}\r\n\r\nconst toasts = ref<ToastItem[]>([])\r\n\r\nlet toastId = 0\r\n\r\nexport function useToast() {\r\n const add = (options: ToastOptions): string => {\r\n const id = `toast-${++toastId}`\r\n const toast: ToastItem = {\r\n id,\r\n message: options.message,\r\n type: options.type || 'info',\r\n title: options.title,\r\n duration: options.duration ?? 5000,\r\n }\r\n toasts.value.push(toast)\r\n return id\r\n }\r\n\r\n const remove = (id: string) => {\r\n const index = toasts.value.findIndex((t) => t.id === id)\r\n if (index > -1) {\r\n toasts.value.splice(index, 1)\r\n }\r\n }\r\n\r\n const clear = () => {\r\n toasts.value = []\r\n }\r\n\r\n const success = (message: string, title?: string) => {\r\n return add({ message, title, type: 'success' })\r\n }\r\n\r\n const error = (message: string, title?: string) => {\r\n return add({ message, title, type: 'error' })\r\n }\r\n\r\n const warning = (message: string, title?: string) => {\r\n return add({ message, title, type: 'warning' })\r\n }\r\n\r\n const info = (message: string, title?: string) => {\r\n return add({ message, title, type: 'info' })\r\n }\r\n\r\n return {\r\n toasts,\r\n add,\r\n remove,\r\n clear,\r\n success,\r\n error,\r\n warning,\r\n info,\r\n }\r\n}\r\n"],"names":[],"mappings":";AAOO,SAAS,mBAAmB;AACjC,QAAM,gBAAgB,IAAoB,EAAE;AAC5C,MAAI,YAAY;AAEhB,QAAM,aAAa,MAAM,gBAAgB,EAAE,SAAS,IAAI,KAAK,KAAK;AAElE,QAAM,SAAS,CACb,MACA,SACA,UAA+B,CAAA,MACpB;AACX,UAAM,KAAK,WAAA;AACX,UAAM,eAA6B;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,UAAU,QAAQ,YAAY;AAAA,IAAA;AAGhC,kBAAc,MAAM,KAAK,YAAY;AAErC,QAAI,aAAa,YAAY,aAAa,WAAW,GAAG;AACtD,iBAAW,MAAM,OAAO,EAAE,GAAG,aAAa,QAAQ;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,CAAC,SAAiB,YAChC,OAAO,WAAW,SAAS,OAAO;AAEpC,QAAM,UAAU,CAAC,SAAiB,YAChC,OAAO,WAAW,SAAS,OAAO;AAEpC,QAAM,QAAQ,CAAC,SAAiB,YAC9B,OAAO,SAAS,SAAS,OAAO;AAElC,QAAM,OAAO,CAAC,SAAiB,YAC7B,OAAO,QAAQ,SAAS,OAAO;AAEjC,QAAM,SAAS,CAAC,OAAe;AAC7B,kBAAc,QAAQ,cAAc,MAAM,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,EACrE;AAEA,QAAM,QAAQ,MAAM;AAClB,kBAAc,QAAQ,CAAA;AAAA,EACxB;AAEA,SAAO;AAAA,IACL,eAAe,SAAS,aAAa;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;ACtDO,SAAS,YAAY,UAA2B,IAAI;AACzD,QAAM;AAAA,IACJ,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,eAAe;AAAA,EAAA,IACb;AAEJ,QAAM,SAAS,IAAI,YAAY;AAE/B,QAAM,kBAAkB,MAAe;AACrC,QAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,UAAM,SAAS,aAAa,QAAQ,UAAU;AAC9C,QAAI,WAAW,MAAM;AACnB,aAAO,WAAW;AAAA,IACpB;AAEA,WAAO,OAAO,WAAW,8BAA8B,EAAE;AAAA,EAC3D;AAEA,QAAM,YAAY,CAAC,SAAkB;AACnC,QAAI,OAAO,aAAa,YAAa;AAErC,UAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,QAAI,CAAC,QAAS;AAEd,QAAI,cAAc,SAAS;AACzB,cAAQ,UAAU,OAAO,QAAQ,IAAI;AAAA,IACvC,OAAO;AACL,cAAQ,aAAa,WAAW,OAAO,SAAS,OAAO;AAAA,IACzD;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACnB,WAAO,QAAQ,CAAC,OAAO;AAAA,EACzB;AAEA,QAAM,MAAM,CAAC,UAAmB;AAC9B,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,QAAQ,CAAC,aAAa;AAC1B,cAAU,QAAQ;AAClB,QAAI,OAAO,iBAAiB,aAAa;AACvC,mBAAa,QAAQ,YAAY,OAAO,QAAQ,CAAC;AAAA,IACnD;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,WAAO,QAAQ,gBAAA;AACf,cAAU,OAAO,KAAK;AAAA,EACxB,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;ACpEO,SAAS,eAAe;AAC7B,QAAM,YAAY,CAAC,UAA2B;AAC5C,QAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,UAAM,MAAM,OAAO,KAAK;AACxB,QAAI,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,IAAI,GAAG;AAChE,aAAO,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,CAClB,MACA,SACA,WAAmB,iBAChB;AACH,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,cAAQ,KAAK,mBAAmB;AAChC;AAAA,IACF;AAGA,UAAM,UAAU,QAAQ,IAAI,CAAC,QAAQ,UAAU,IAAI,KAAK,CAAC,EAAE,KAAK,GAAG;AAGnE,UAAM,OAAO,KAAK;AAAA,MAAI,CAAC,SACrB,QAAQ,IAAI,CAAC,QAAQ,UAAU,KAAK,IAAI,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG;AAAA,IAAA;AAIzD,UAAM,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,KAAK,IAAI;AAGxC,UAAM,OAAO,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,2BAA2B;AAChE,UAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,aAAa,QAAQ,GAAG;AAC7B,SAAK,aAAa,YAAY,QAAQ;AACtC,SAAK,MAAM,aAAa;AACxB,aAAS,KAAK,YAAY,IAAI;AAC9B,SAAK,MAAA;AACL,aAAS,KAAK,YAAY,IAAI;AAC9B,QAAI,gBAAgB,GAAG;AAAA,EACzB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EAAA;AAEJ;ACjBO,SAAS,SAAsB,SAShB;AACpB,QAAM;AAAA,IACJ,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EAAA,IACE,WAAW,CAAA;AAEf,QAAM,SAAS,IAAI,WAAW;AAC9B,QAAM,OAAO,IAAc,WAAW;AAEtC,QAAM,OAAO,CAAC,YAAgB;AAC5B,SAAK,QAAQ,WAAW;AACxB,WAAO,QAAQ;AACf,qCAAS,KAAK;AAAA,EAChB;AAEA,QAAM,QAAQ,MAAM;AAClB,WAAO,QAAQ;AACf,SAAK,QAAQ;AACb;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACnB,QAAI,OAAO,OAAO;AAChB,YAAA;AAAA,IACF,OAAO;AACL,WAAA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAmBO,SAAS,UACd,QACG;AACH,SAAO;AACT;AC7FA,MAAM,SAAS,IAAiB,EAAE;AAElC,IAAI,UAAU;AAEP,SAAS,WAAW;AACzB,QAAM,MAAM,CAAC,YAAkC;AAC7C,UAAM,KAAK,SAAS,EAAE,OAAO;AAC7B,UAAM,QAAmB;AAAA,MACvB;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,MAAM,QAAQ,QAAQ;AAAA,MACtB,OAAO,QAAQ;AAAA,MACf,UAAU,QAAQ,YAAY;AAAA,IAAA;AAEhC,WAAO,MAAM,KAAK,KAAK;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,OAAe;AAC7B,UAAM,QAAQ,OAAO,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE;AACvD,QAAI,QAAQ,IAAI;AACd,aAAO,MAAM,OAAO,OAAO,CAAC;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AAClB,WAAO,QAAQ,CAAA;AAAA,EACjB;AAEA,QAAM,UAAU,CAAC,SAAiB,UAAmB;AACnD,WAAO,IAAI,EAAE,SAAS,OAAO,MAAM,WAAW;AAAA,EAChD;AAEA,QAAM,QAAQ,CAAC,SAAiB,UAAmB;AACjD,WAAO,IAAI,EAAE,SAAS,OAAO,MAAM,SAAS;AAAA,EAC9C;AAEA,QAAM,UAAU,CAAC,SAAiB,UAAmB;AACnD,WAAO,IAAI,EAAE,SAAS,OAAO,MAAM,WAAW;AAAA,EAChD;AAEA,QAAM,OAAO,CAAC,SAAiB,UAAmB;AAChD,WAAO,IAAI,EAAE,SAAS,OAAO,MAAM,QAAQ;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;"}
@@ -160,9 +160,57 @@ function useModal(options) {
160
160
  function useModals(modals) {
161
161
  return modals;
162
162
  }
163
+ const toasts = vue.ref([]);
164
+ let toastId = 0;
165
+ function useToast() {
166
+ const add = (options) => {
167
+ const id = `toast-${++toastId}`;
168
+ const toast = {
169
+ id,
170
+ message: options.message,
171
+ type: options.type || "info",
172
+ title: options.title,
173
+ duration: options.duration ?? 5e3
174
+ };
175
+ toasts.value.push(toast);
176
+ return id;
177
+ };
178
+ const remove = (id) => {
179
+ const index = toasts.value.findIndex((t) => t.id === id);
180
+ if (index > -1) {
181
+ toasts.value.splice(index, 1);
182
+ }
183
+ };
184
+ const clear = () => {
185
+ toasts.value = [];
186
+ };
187
+ const success = (message, title) => {
188
+ return add({ message, title, type: "success" });
189
+ };
190
+ const error = (message, title) => {
191
+ return add({ message, title, type: "error" });
192
+ };
193
+ const warning = (message, title) => {
194
+ return add({ message, title, type: "warning" });
195
+ };
196
+ const info = (message, title) => {
197
+ return add({ message, title, type: "info" });
198
+ };
199
+ return {
200
+ toasts,
201
+ add,
202
+ remove,
203
+ clear,
204
+ success,
205
+ error,
206
+ warning,
207
+ info
208
+ };
209
+ }
163
210
  exports.useDarkMode = useDarkMode;
164
211
  exports.useExportCSV = useExportCSV;
165
212
  exports.useModal = useModal;
166
213
  exports.useModals = useModals;
167
214
  exports.useNotifications = useNotifications;
168
- //# sourceMappingURL=useModal-DDF_ZS8C.cjs.map
215
+ exports.useToast = useToast;
216
+ //# sourceMappingURL=useToast-nJXpFz_M.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useToast-nJXpFz_M.cjs","sources":["../src/composables/useNotifications.ts","../src/composables/useDarkMode.ts","../src/composables/useExportCSV.ts","../src/composables/useModal.ts","../src/composables/useToast.ts"],"sourcesContent":["import { ref, readonly } from 'vue'\nimport type { Notification, NotificationType, NotificationOptions } from '@/types'\n\n/**\n * Composable for managing notifications/toasts\n * Standalone implementation without Pinia dependency\n */\nexport function useNotifications() {\n const notifications = ref<Notification[]>([])\n let idCounter = 0\n\n const generateId = () => `notification-${++idCounter}-${Date.now()}`\n\n const notify = (\n type: NotificationType,\n message: string,\n options: NotificationOptions = {},\n ): string => {\n const id = generateId()\n const notification: Notification = {\n id,\n type,\n message,\n title: options.title ?? null,\n duration: options.duration ?? 5000,\n }\n\n notifications.value.push(notification)\n\n if (notification.duration && notification.duration > 0) {\n setTimeout(() => remove(id), notification.duration)\n }\n\n return id\n }\n\n const success = (message: string, options?: NotificationOptions) =>\n notify('success', message, options)\n\n const warning = (message: string, options?: NotificationOptions) =>\n notify('warning', message, options)\n\n const error = (message: string, options?: NotificationOptions) =>\n notify('error', message, options)\n\n const info = (message: string, options?: NotificationOptions) =>\n notify('info', message, options)\n\n const remove = (id: string) => {\n notifications.value = notifications.value.filter((n) => n.id !== id)\n }\n\n const clear = () => {\n notifications.value = []\n }\n\n return {\n notifications: readonly(notifications),\n notify,\n success,\n warning,\n error,\n info,\n remove,\n clear,\n }\n}\n","import { ref, watch, onMounted } from 'vue'\n\nexport interface DarkModeOptions {\n selector?: string\n attribute?: string\n storageKey?: string\n defaultValue?: boolean\n}\n\n/**\n * Composable for managing dark mode state\n */\nexport function useDarkMode(options: DarkModeOptions = {}) {\n const {\n selector = 'html',\n attribute = 'class',\n storageKey = 'dark-mode',\n defaultValue = false,\n } = options\n\n const isDark = ref(defaultValue)\n\n const getInitialValue = (): boolean => {\n if (typeof window === 'undefined') return defaultValue\n\n const stored = localStorage.getItem(storageKey)\n if (stored !== null) {\n return stored === 'true'\n }\n\n return window.matchMedia('(prefers-color-scheme: dark)').matches\n }\n\n const updateDOM = (dark: boolean) => {\n if (typeof document === 'undefined') return\n\n const element = document.querySelector(selector)\n if (!element) return\n\n if (attribute === 'class') {\n element.classList.toggle('dark', dark)\n } else {\n element.setAttribute(attribute, dark ? 'dark' : 'light')\n }\n }\n\n const toggle = () => {\n isDark.value = !isDark.value\n }\n\n const set = (value: boolean) => {\n isDark.value = value\n }\n\n watch(isDark, (newValue) => {\n updateDOM(newValue)\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(storageKey, String(newValue))\n }\n })\n\n onMounted(() => {\n isDark.value = getInitialValue()\n updateDOM(isDark.value)\n })\n\n return {\n isDark,\n toggle,\n set,\n }\n}\n","/**\n * Composable for exporting data to CSV format\n */\nexport function useExportCSV() {\n const escapeCSV = (value: unknown): string => {\n if (value === null || value === undefined) return ''\n const str = String(value)\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n }\n\n const exportToCSV = <T extends Record<string, unknown>>(\n data: T[],\n columns: { key: keyof T; label: string }[],\n filename: string = 'export.csv',\n ) => {\n if (!data || data.length === 0) {\n console.warn('No data to export')\n return\n }\n\n // Create header row\n const headers = columns.map((col) => escapeCSV(col.label)).join(',')\n\n // Create data rows\n const rows = data.map((item) =>\n columns.map((col) => escapeCSV(item[col.key])).join(','),\n )\n\n // Combine header and rows\n const csv = [headers, ...rows].join('\\n')\n\n // Create and trigger download\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.setAttribute('href', url)\n link.setAttribute('download', filename)\n link.style.visibility = 'hidden'\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n URL.revokeObjectURL(url)\n }\n\n return {\n exportToCSV,\n escapeCSV,\n }\n}\n","import { ref, type Ref } from 'vue'\r\n\r\nexport interface UseModalReturn<T = unknown> {\r\n /** Whether the modal is currently open */\r\n isOpen: Ref<boolean>\r\n /** Data associated with the modal (e.g., item being edited) */\r\n data: Ref<T | null>\r\n /** Open the modal, optionally with data */\r\n open: (newData?: T) => void\r\n /** Close the modal and clear data */\r\n close: () => void\r\n /** Toggle the modal state */\r\n toggle: () => void\r\n}\r\n\r\n/**\r\n * Composable for managing modal state\r\n *\r\n * @example\r\n * ```ts\r\n * // Simple modal\r\n * const createModal = useModal()\r\n * createModal.open()\r\n * createModal.close()\r\n *\r\n * // Modal with data (e.g., for editing)\r\n * const editModal = useModal<User>()\r\n * editModal.open(selectedUser)\r\n * // Access editModal.data.value in modal\r\n *\r\n * // With onClose callback\r\n * const deleteModal = useModal<Item>({ onClose: () => refetch() })\r\n * ```\r\n */\r\nexport function useModal<T = unknown>(options?: {\r\n /** Initial open state */\r\n initialOpen?: boolean\r\n /** Initial data */\r\n initialData?: T | null\r\n /** Callback when modal opens */\r\n onOpen?: (data: T | null) => void\r\n /** Callback when modal closes */\r\n onClose?: () => void\r\n}): UseModalReturn<T> {\r\n const {\r\n initialOpen = false,\r\n initialData = null,\r\n onOpen,\r\n onClose,\r\n } = options ?? {}\r\n\r\n const isOpen = ref(initialOpen)\r\n const data = ref<T | null>(initialData) as Ref<T | null>\r\n\r\n const open = (newData?: T) => {\r\n data.value = newData ?? null\r\n isOpen.value = true\r\n onOpen?.(data.value)\r\n }\r\n\r\n const close = () => {\r\n isOpen.value = false\r\n data.value = null\r\n onClose?.()\r\n }\r\n\r\n const toggle = () => {\r\n if (isOpen.value) {\r\n close()\r\n } else {\r\n open()\r\n }\r\n }\r\n\r\n return {\r\n isOpen,\r\n data,\r\n open,\r\n close,\r\n toggle,\r\n }\r\n}\r\n\r\n/**\r\n * Create multiple related modals at once\r\n * Useful when a page has several modals (create, edit, delete, etc.)\r\n *\r\n * @example\r\n * ```ts\r\n * const modals = useModals({\r\n * create: useModal(),\r\n * edit: useModal<User>(),\r\n * delete: useModal<User>(),\r\n * })\r\n *\r\n * modals.create.open()\r\n * modals.edit.open(user)\r\n * modals.delete.close()\r\n * ```\r\n */\r\nexport function useModals<T extends Record<string, UseModalReturn<unknown>>>(\r\n modals: T\r\n): T {\r\n return modals\r\n}\r\n","import { ref } from 'vue'\r\nimport type { ToastType } from '@/components/feedback/Toast.vue'\r\nimport type { ToastItem } from '@/components/feedback/ToastContainer.vue'\r\n\r\nexport interface ToastOptions {\r\n message: string\r\n type?: ToastType\r\n title?: string\r\n duration?: number\r\n}\r\n\r\nconst toasts = ref<ToastItem[]>([])\r\n\r\nlet toastId = 0\r\n\r\nexport function useToast() {\r\n const add = (options: ToastOptions): string => {\r\n const id = `toast-${++toastId}`\r\n const toast: ToastItem = {\r\n id,\r\n message: options.message,\r\n type: options.type || 'info',\r\n title: options.title,\r\n duration: options.duration ?? 5000,\r\n }\r\n toasts.value.push(toast)\r\n return id\r\n }\r\n\r\n const remove = (id: string) => {\r\n const index = toasts.value.findIndex((t) => t.id === id)\r\n if (index > -1) {\r\n toasts.value.splice(index, 1)\r\n }\r\n }\r\n\r\n const clear = () => {\r\n toasts.value = []\r\n }\r\n\r\n const success = (message: string, title?: string) => {\r\n return add({ message, title, type: 'success' })\r\n }\r\n\r\n const error = (message: string, title?: string) => {\r\n return add({ message, title, type: 'error' })\r\n }\r\n\r\n const warning = (message: string, title?: string) => {\r\n return add({ message, title, type: 'warning' })\r\n }\r\n\r\n const info = (message: string, title?: string) => {\r\n return add({ message, title, type: 'info' })\r\n }\r\n\r\n return {\r\n toasts,\r\n add,\r\n remove,\r\n clear,\r\n success,\r\n error,\r\n warning,\r\n info,\r\n }\r\n}\r\n"],"names":["ref","readonly","watch","onMounted"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cisse-vue-ui",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
4
4
  "description": "Vue 3 + TypeScript + Tailwind CSS v4 component library",
5
5
  "author": "Cisse",
6
6
  "license": "MIT",
@@ -101,7 +101,10 @@
101
101
  "test": "vitest",
102
102
  "type-check": "vue-tsc --noEmit",
103
103
  "lint": "eslint . --fix",
104
- "prepublishOnly": "bun run build"
104
+ "prepublishOnly": "bun run build",
105
+ "storybook": "storybook dev -p 6006",
106
+ "build-storybook": "storybook build",
107
+ "deploy-storybook": "storybook build && gh-pages -d storybook-static"
105
108
  },
106
109
  "peerDependencies": {
107
110
  "@iconify/vue": ">=4.0.0",
@@ -117,14 +120,25 @@
117
120
  }
118
121
  },
119
122
  "devDependencies": {
123
+ "@chromatic-com/storybook": "^4.1.3",
120
124
  "@iconify/vue": "^5.0.0",
125
+ "@storybook/addon-a11y": "^10.1.6",
126
+ "@storybook/addon-docs": "^10.1.6",
127
+ "@storybook/addon-vitest": "^10.1.6",
128
+ "@storybook/vue3-vite": "^10.1.6",
121
129
  "@tailwindcss/vite": "^4.1.17",
122
130
  "@types/node": "^22.19.1",
123
131
  "@vitejs/plugin-vue": "^6.0.2",
132
+ "@vitest/browser": "^3.2.4",
133
+ "@vitest/coverage-v8": "^3.2.4",
124
134
  "@vue/tsconfig": "^0.8.1",
125
135
  "ajv": "^8.17.1",
126
136
  "eslint": "^9.39.1",
137
+ "eslint-plugin-storybook": "^10.1.6",
138
+ "gh-pages": "^6.3.0",
127
139
  "pinia": "^3.0.4",
140
+ "playwright": "^1.57.0",
141
+ "storybook": "^10.1.6",
128
142
  "tailwindcss": "^4.1.17",
129
143
  "typescript": "~5.9.3",
130
144
  "vite": "^6.4.1",
@@ -137,5 +151,10 @@
137
151
  "dependencies": {
138
152
  "@vueuse/core": "^14.1.0",
139
153
  "tailwind-merge": "^3.4.0"
154
+ },
155
+ "eslintConfig": {
156
+ "extends": [
157
+ "plugin:storybook/recommended"
158
+ ]
140
159
  }
141
160
  }