daisy-ui-kit 1.0.7 → 2.0.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 (129) hide show
  1. package/components/Accordion.vue +16 -0
  2. package/components/Alert.vue +6 -8
  3. package/components/Artboard.vue +1 -1
  4. package/components/Avatar.vue +8 -23
  5. package/components/Badge.vue +5 -0
  6. package/components/BottomNav.vue +2 -4
  7. package/components/Button.vue +12 -4
  8. package/components/Card.vue +1 -1
  9. package/components/Chat.vue +27 -0
  10. package/components/ChatBubble.vue +34 -0
  11. package/components/ChatFooter.vue +5 -0
  12. package/components/ChatHeader.vue +5 -0
  13. package/components/ChatImage.vue +5 -0
  14. package/components/Checkbox.vue +12 -4
  15. package/components/Code.vue +2 -1
  16. package/components/CodeWrapper.vue +2 -2
  17. package/components/Collapse.vue +30 -8
  18. package/components/CountdownTimers.vue +8 -3
  19. package/components/Divider.vue +1 -1
  20. package/components/Drawer.vue +35 -11
  21. package/components/DrawerContent.vue +15 -0
  22. package/components/DrawerSide.vue +16 -0
  23. package/components/Dropdown.vue +56 -7
  24. package/components/DropdownContent.vue +3 -1
  25. package/components/DropdownTarget.vue +5 -0
  26. package/components/FileInput.vue +60 -0
  27. package/components/Flex.vue +3 -89
  28. package/components/FlexItem.vue +4 -90
  29. package/components/Footer.vue +1 -1
  30. package/components/FooterTitle.vue +1 -2
  31. package/components/Home/AlternatingFeatureSections.vue +10 -12
  32. package/components/Home/BitoviConsulting.vue +1 -1
  33. package/components/Home/BitoviOpenSource.vue +5 -5
  34. package/components/Home/Footer.vue +7 -6
  35. package/components/Home/GradientFeatureSections.vue +9 -9
  36. package/components/Home/Header.vue +10 -16
  37. package/components/Home/Hero.vue +4 -4
  38. package/components/Home/Testimonial.vue +1 -1
  39. package/components/{ButtonGroup.vue → Join.vue} +1 -1
  40. package/components/Link.vue +13 -6
  41. package/components/LoadingBall.vue +41 -0
  42. package/components/LoadingBars.vue +41 -0
  43. package/components/LoadingDots.vue +41 -0
  44. package/components/LoadingInfinity.vue +41 -0
  45. package/components/LoadingRing.vue +41 -0
  46. package/components/LoadingSpinner.vue +41 -0
  47. package/components/Mask.config.ts +2 -2
  48. package/components/Menu.vue +14 -6
  49. package/components/MenuItem.vue +11 -3
  50. package/components/MenuTitle.vue +1 -10
  51. package/components/Modal.vue +28 -7
  52. package/components/Progress.vue +1 -2
  53. package/components/Prose.vue +6 -8
  54. package/components/Radio.vue +14 -2
  55. package/components/RadioGroup.vue +5 -0
  56. package/components/Range.vue +14 -2
  57. package/components/RangeMeasure.vue +23 -11
  58. package/components/RangeMeasureTick.vue +19 -7
  59. package/components/Rating.vue +52 -36
  60. package/components/Select.vue +14 -14
  61. package/components/Step.vue +2 -2
  62. package/components/Tab.vue +3 -4
  63. package/components/TabContent.vue +1 -1
  64. package/components/Tabs.vue +2 -2
  65. package/components/Text.vue +4 -1
  66. package/components/TextArea.vue +13 -1
  67. package/components/TextInput.vue +5 -2
  68. package/components/Toggle.vue +13 -1
  69. package/components/content/Badge.ts +3 -0
  70. package/components/content/ColorBadge.vue +24 -0
  71. package/components/{DemoExample.vue → content/DemoExample.vue} +1 -1
  72. package/components/content/DemoExampleResponsive.vue +59 -0
  73. package/components/content/IframeRenderer.ts +53 -0
  74. package/components/content/Indent.vue +3 -0
  75. package/components/content/LocalLinks.vue +31 -0
  76. package/components/content/NotFound.vue +42 -0
  77. package/components/content/PageNext.vue +24 -0
  78. package/components/content/PagePrevious.vue +24 -0
  79. package/components/content/PrevNext.vue +40 -0
  80. package/components/content/ProseA.vue +19 -0
  81. package/components/content/ProseAlert.vue +11 -0
  82. package/components/content/ProseBlockquote.vue +5 -0
  83. package/components/content/ProseCode.vue +62 -0
  84. package/components/content/ProseCodeInline.vue +3 -0
  85. package/components/content/ProseEm.vue +5 -0
  86. package/components/content/ProseH1.vue +16 -0
  87. package/components/content/ProseH2.vue +16 -0
  88. package/components/content/ProseH3.vue +16 -0
  89. package/components/content/ProseH4.vue +16 -0
  90. package/components/content/ProseH5.vue +16 -0
  91. package/components/content/ProseH6.vue +16 -0
  92. package/components/content/ProseHr.vue +3 -0
  93. package/components/content/ProseImg.vue +34 -0
  94. package/components/content/ProseLi.vue +3 -0
  95. package/components/content/ProseOl.vue +5 -0
  96. package/components/content/ProseP.vue +3 -0
  97. package/components/content/ProseStrong.vue +5 -0
  98. package/components/content/ProseTable.vue +7 -0
  99. package/components/content/ProseTbody.vue +5 -0
  100. package/components/content/ProseTd.vue +5 -0
  101. package/components/content/ProseTh.vue +5 -0
  102. package/components/content/ProseThead.vue +5 -0
  103. package/components/content/ProseTr.vue +5 -0
  104. package/components/content/ProseUl.vue +5 -0
  105. package/components/content/Search.vue +160 -0
  106. package/components/content/Sidebar.vue +65 -0
  107. package/components/{SidebarMenuSection.vue → content/SidebarMenuSection.vue} +11 -6
  108. package/components/content/TableOfContents.vue +80 -0
  109. package/components/content/TypeBadge.vue +17 -0
  110. package/components/theme/Picker.vue +2 -2
  111. package/components/theme/Preview.vue +1 -1
  112. package/components/theme/theme-utils.ts +15 -4
  113. package/index.ts +105 -96
  114. package/package.json +27 -24
  115. package/components/-utils.ts +0 -41
  116. package/components/Button.config.ts +0 -26
  117. package/components/DemoElement.vue +0 -32
  118. package/components/DrawerLayout.vue +0 -37
  119. package/components/DrawerLayoutContent.vue +0 -19
  120. package/components/InputGroup.vue +0 -33
  121. package/components/MobileSidebar.vue +0 -88
  122. package/components/ModalWrapper.vue +0 -32
  123. package/components/Sidebar.vue +0 -89
  124. package/components/drawer-utils.ts +0 -10
  125. package/components/fixtures.ts +0 -62
  126. package/components/types.ts +0 -7
  127. /package/components/{CodePreview.vue → content/CodePreview.vue} +0 -0
  128. /package/components/{SigninForm.vue → content/SigninForm.vue} +0 -0
  129. /package/components/{UserMenu.vue → content/UserMenu.vue} +0 -0
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ import { provide } from 'vue'
3
+
4
+ const value = defineModel({
5
+ required: true,
6
+ local: true,
7
+ })
8
+
9
+ provide('accordion-value', value)
10
+ </script>
11
+
12
+ <template>
13
+ <div class="daisy-ui-kit-accordion">
14
+ <slot />
15
+ </div>
16
+ </template>
@@ -2,7 +2,7 @@
2
2
  import { computed } from 'vue'
3
3
 
4
4
  const props: any = defineProps<{
5
- state?: string
5
+ type?: string
6
6
  info?: boolean
7
7
  success?: boolean
8
8
  warning?: boolean
@@ -10,18 +10,16 @@ const props: any = defineProps<{
10
10
  }>()
11
11
  const classes = computed(() => {
12
12
  return {
13
- 'alert-info': props.info || props.state === 'info',
14
- 'alert-success': props.success || props.state === 'success',
15
- 'alert-warning': props.warning || props.state === 'warning',
16
- 'alert-error': props.error || props.state === 'error',
13
+ 'alert-info': props.info || props.type === 'info',
14
+ 'alert-success': props.success || props.type === 'success',
15
+ 'alert-warning': props.warning || props.type === 'warning',
16
+ 'alert-error': props.error || props.type === 'error',
17
17
  }
18
18
  })
19
19
  </script>
20
20
 
21
21
  <template>
22
22
  <div class="alert" :class="classes">
23
- <div>
24
- <slot />
25
- </div>
23
+ <slot />
26
24
  </div>
27
25
  </template>
@@ -2,9 +2,9 @@
2
2
  import { computed } from 'vue'
3
3
 
4
4
  const props = defineProps<{
5
- size?: string
6
5
  demo?: boolean
7
6
  horizontal?: boolean
7
+ size?: string
8
8
  phone1?: boolean
9
9
  phone2?: boolean
10
10
  phone3?: boolean
@@ -4,11 +4,8 @@ import { makeMaskClasses, maskProps } from './Mask.config'
4
4
  import Mask from './Mask.vue'
5
5
 
6
6
  const props = defineProps({
7
- text: { type: String },
8
- charCount: { type: Number, default: 1 },
9
7
  backgroundColor: { type: String, default: '#BBB' },
10
- size: { type: [Number, String], default: 6 },
11
- innerClass: String,
8
+ maskClasses: String,
12
9
  showStatus: Boolean,
13
10
  online: Boolean,
14
11
  ...maskProps,
@@ -20,54 +17,42 @@ const classes = computed(() => {
20
17
  }
21
18
  })
22
19
 
23
- const maskClasses = makeMaskClasses(props)
20
+ const internalMaskClasses = makeMaskClasses(props)
24
21
  const allMaskClasses = computed(() => {
25
- const classes = maskClasses.value
22
+ const classes = internalMaskClasses.value
26
23
  const hasMaskClass = Object.values(classes).find(v => v)
27
24
  // Enable rounded-btn class only if no mask classes are applied
28
25
  Object.assign(classes, { 'rounded-btn': !hasMaskClass })
29
26
  return classes
30
27
  })
31
28
 
32
- const initials = computed(() => {
33
- const parts = (props.text || '').split(' ')
34
- return parts.reduce((chars: string, part: string) => {
35
- if (props.charCount === chars.length)
36
- return chars
37
-
38
- const char = part.slice(0, 1).toUpperCase()
39
-
40
- return chars + char
41
- }, '')
42
- })
43
-
44
29
  const color = computed(() => {
45
30
  return `#${contrastingColor(props.backgroundColor.replace('#', ''))}`
46
31
  })
47
32
 
48
- function contrastingColor(color) {
33
+ function contrastingColor(color: any) {
49
34
  return (luma(color) >= 155) ? '000' : 'fff'
50
35
  }
51
36
  // color can be a hx string or an array of RGB values 0-255
52
- function luma(color) {
37
+ function luma(color: any) {
53
38
  const rgb = (typeof color === 'string') ? hexToRGBArray(color) : color
54
39
  return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]) // SMPTE C, Rec. 709 weightings
55
40
  }
56
- function hexToRGBArray(color) {
41
+ function hexToRGBArray(color: any) {
57
42
  if (color.length === 3)
58
43
  color = color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2)
59
44
  else if (color.length !== 6)
60
45
  throw new Error(`Invalid hex color: ${color}`)
61
46
  const rgb = []
62
47
  for (let i = 0; i <= 2; i++)
63
- rgb[i] = parseInt(color.substr(i * 2, 2), 16)
48
+ rgb[i] = Number.parseInt(color.substr(i * 2, 2), 16)
64
49
  return rgb
65
50
  }
66
51
  </script>
67
52
 
68
53
  <template>
69
54
  <div class="avatar" :class="classes">
70
- <Mask :style="{ backgroundColor, color }" class="w-full h-full avatar-mask aspect-square" :class="[allMaskClasses, innerClass]">
55
+ <Mask :style="{ backgroundColor, color }" class="w-full h-full avatar-mask aspect-square" :class="[allMaskClasses, maskClasses]">
71
56
  <slot />
72
57
  </Mask>
73
58
  </div>
@@ -11,6 +11,7 @@ const props = defineProps<{
11
11
  xs?: boolean
12
12
 
13
13
  color?: string
14
+ neutral?: boolean
14
15
  primary?: boolean
15
16
  secondary?: boolean
16
17
  accent?: boolean
@@ -18,15 +19,19 @@ const props = defineProps<{
18
19
  success?: boolean
19
20
  warning?: boolean
20
21
  error?: boolean
22
+
23
+ ghost?: boolean
21
24
  }>()
22
25
 
23
26
  const classes = computed(() => {
24
27
  return {
25
28
  'badge-outline': props.outline,
29
+ 'badge-ghost': props.ghost,
26
30
  'badge-lg': props.lg || props.size === 'lg',
27
31
  'badge-md': props.md || props.size === 'md',
28
32
  'badge-sm': props.sm || props.size === 'sm',
29
33
  'badge-xs': props.xs || props.size === 'xs',
34
+ 'badge-neutral': props.neutral || props.color === 'neutral',
30
35
  'badge-primary': props.primary || props.color === 'primary',
31
36
  'badge-secondary': props.secondary || props.color === 'secondary',
32
37
  'badge-accent': props.accent || props.color === 'accent',
@@ -1,15 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
3
 
4
- interface Props {
4
+ const props = defineProps<{
5
5
  size?: string
6
6
  lg?: boolean
7
7
  md?: boolean
8
8
  sm?: boolean
9
9
  xs?: boolean
10
- }
11
-
12
- const props = defineProps<Props>()
10
+ }>()
13
11
  const classes = computed(() => {
14
12
  return {
15
13
  'btm-nav-lg': props.lg || props.size === 'lg',
@@ -1,8 +1,7 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue'
3
-
4
2
  interface Props {
5
3
  is?: string
4
+ join?: boolean
6
5
 
7
6
  color?: string
8
7
  neutral?: boolean
@@ -26,7 +25,6 @@ interface Props {
26
25
  wide?: boolean
27
26
  block?: boolean
28
27
 
29
- loading?: boolean
30
28
  noAnimation?: boolean
31
29
  active?: boolean
32
30
 
@@ -43,6 +41,8 @@ const props = defineProps<Props>()
43
41
 
44
42
  const classes = computed(() => {
45
43
  return {
44
+ 'join-item': props.join,
45
+
46
46
  'btn-primary': !props.disabled && (props.primary || props.color === 'primary'),
47
47
  'btn-secondary': !props.disabled && (props.secondary || props.color === 'secondary'),
48
48
  'btn-neutral': !props.disabled && (props.neutral || props.color === 'neutral'),
@@ -52,6 +52,15 @@ const classes = computed(() => {
52
52
  'btn-warning': !props.disabled && (props.warning || props.color === 'warning'),
53
53
  'btn-error': !props.disabled && (props.error || props.color === 'error'),
54
54
 
55
+ 'text-primary': !props.disabled && (props.primary || props.color === 'primary') && props.link,
56
+ 'text-secondary': !props.disabled && (props.secondary || props.color === 'secondary') && props.link,
57
+ 'text-neutral': !props.disabled && (props.neutral || props.color === 'neutral') && props.link,
58
+ 'text-accent': !props.disabled && (props.accent || props.color === 'accent') && props.link,
59
+ 'text-info': !props.disabled && (props.info || props.color === 'info') && props.link,
60
+ 'text-success': !props.disabled && (props.success || props.color === 'success') && props.link,
61
+ 'text-warning': !props.disabled && (props.warning || props.color === 'warning') && props.link,
62
+ 'text-error': !props.disabled && (props.error || props.color === 'error') && props.link,
63
+
55
64
  'glass': !props.disabled && props.glass,
56
65
 
57
66
  'btn-circle': props.circle || props.shape === 'circle',
@@ -69,7 +78,6 @@ const classes = computed(() => {
69
78
  'btn-link': !props.disabled && props.link,
70
79
  'btn-disabled': props.disabled,
71
80
 
72
- 'loading': props.loading,
73
81
  'no-animation': props.noAnimation,
74
82
  'btn-active': !props.disabled && props.active,
75
83
  }
@@ -2,8 +2,8 @@
2
2
  import { computed } from 'vue'
3
3
 
4
4
  interface Props {
5
- bordered?: boolean
6
5
  is?: string | Object | Function
6
+ bordered?: boolean
7
7
  compact?: boolean
8
8
  side?: boolean
9
9
  imageFull?: boolean
@@ -0,0 +1,27 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ const props = withDefaults(defineProps<{
5
+ pre?: boolean
6
+ align?: string
7
+ start?: boolean
8
+ end?: boolean
9
+ }>(), {
10
+ align: 'start',
11
+ })
12
+
13
+ const classes = computed(() => {
14
+ const { align, start, end } = props
15
+ return {
16
+ 'chat-start': start || (!end && align === 'start'),
17
+ 'chat-end': end || align === 'end',
18
+ 'whitespace-pre': props.pre,
19
+ }
20
+ })
21
+ </script>
22
+
23
+ <template>
24
+ <div class="chat" :class="classes">
25
+ <slot />
26
+ </div>
27
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ color?: string
6
+ neutral?: boolean
7
+ primary?: boolean
8
+ secondary?: boolean
9
+ accent?: boolean
10
+ info?: boolean
11
+ success?: boolean
12
+ warning?: boolean
13
+ error?: boolean
14
+ }>()
15
+
16
+ const classes = computed(() => {
17
+ return {
18
+ 'badge-neutral': props.neutral || props.color === 'neutral',
19
+ 'badge-primary': props.primary || props.color === 'primary',
20
+ 'badge-secondary': props.secondary || props.color === 'secondary',
21
+ 'badge-accent': props.accent || props.color === 'accent',
22
+ 'badge-info': props.info || props.color === 'info',
23
+ 'badge-success': props.success || props.color === 'success',
24
+ 'badge-warning': props.warning || props.color === 'warning',
25
+ 'badge-error': props.error || props.color === 'error',
26
+ }
27
+ })
28
+ </script>
29
+
30
+ <template>
31
+ <div class="chat-bubble" :class="classes">
32
+ <slot />
33
+ </div>
34
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="chat-footer">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="chat-header">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <Avatar class="chat-image">
3
+ <slot />
4
+ </Avatar>
5
+ </template>
@@ -8,21 +8,29 @@ const props = defineProps<{
8
8
  primary?: boolean
9
9
  secondary?: boolean
10
10
  accent?: boolean
11
+ success?: boolean
12
+ warning?: boolean
13
+ info?: boolean
14
+ error?: boolean
11
15
 
12
16
  size?: string
13
- xs?: boolean
14
- sm?: boolean
15
- md?: boolean
16
17
  lg?: boolean
18
+ md?: boolean
19
+ sm?: boolean
20
+ xs?: boolean
17
21
  }>()
18
22
  const emit = defineEmits(['update:modelValue'])
19
23
 
20
24
  const classes = computed(() => {
21
- const { color, primary, secondary, accent, size, xs, sm, md, lg } = props
25
+ const { color, primary, secondary, accent, success, warning, info, error, size, xs, sm, md, lg } = props
22
26
  return {
23
27
  'checkbox-primary': primary || color === 'primary',
24
28
  'checkbox-secondary': secondary || color === 'secondary',
25
29
  'checkbox-accent': accent || color === 'accent',
30
+ 'checkbox-success': success || color === 'success',
31
+ 'checkbox-warning': warning || color === 'warning',
32
+ 'checkbox-info': info || color === 'info',
33
+ 'checkbox-error': error || color === 'error',
26
34
  'checkbox-xs': xs || size === 'xs',
27
35
  'checkbox-sm': sm || size === 'sm',
28
36
  'checkbox-md': md || size === 'md',
@@ -17,7 +17,8 @@ export default {
17
17
 
18
18
  const source = ref(null)
19
19
  const lang = computed(() => {
20
- if (props.language === 'vue') return 'html'
20
+ if (props.language === 'vue')
21
+ return 'html'
21
22
  else return props.language
22
23
  })
23
24
  const languageClass = computed(() => `language-${lang.value}`)
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <Collapse arrow toggle class="mb-8 rounded-b-xl bg-neutral/20 not-prose">
2
+ <Collapse arrow toggle class="mb-8 rounded-b-xl rounded-t-none bg-base-200 p-0">
3
3
  <CollapseTitle class="text-lg font-medium">
4
4
  Code Example
5
5
  </CollapseTitle>
6
- <CollapseContent>
6
+ <CollapseContent class="overflow-hidden">
7
7
  <slot />
8
8
  </CollapseContent>
9
9
  </Collapse>
@@ -7,27 +7,49 @@ interface Props {
7
7
  plus?: boolean
8
8
 
9
9
  open?: boolean
10
- closed?: boolean
10
+ close?: boolean
11
11
 
12
12
  toggle?: boolean
13
+
14
+ // for accordion
15
+ value?: any
13
16
  }
14
17
  const props = defineProps<Props>()
15
18
 
19
+ const accordionValue: any = inject('accordion-value', ref('no-accordion-value'))
20
+ const useAccordion = computed(() => {
21
+ return accordionValue.value !== 'no-accordion-value'
22
+ })
23
+ const isAccordionSelected = computed(
24
+ () => {
25
+ if (!useAccordion.value)
26
+ return false
27
+ return accordionValue.value === props.value
28
+ })
29
+
30
+ function handleClick() {
31
+ if (useAccordion.value)
32
+ accordionValue.value = props.value
33
+ }
34
+
16
35
  const classes = computed(() => {
17
- const { style, arrow, plus, open, closed } = props
36
+ const { style, arrow, plus, open, close } = props
18
37
  return {
19
38
  'collapse-arrow': arrow || style === 'arrow',
20
- 'carousel-plus': plus || style === 'plus',
21
- 'carousel-open': open && !closed,
22
- 'carousel-closed': closed,
39
+ 'collapse-plus': plus || style === 'plus',
40
+ 'collapse-open': (open && !close) || isAccordionSelected.value,
41
+ 'collapse-close': close,
23
42
  }
24
43
  })
25
44
  </script>
26
45
 
27
46
  <template>
28
- <div tabindex="0" class="collapse" :class="classes">
29
- <input v-if="toggle" type="checkbox">
30
-
47
+ <div tabindex="0" class="collapse" :class="classes" @click="handleClick">
48
+ <input
49
+ v-if="toggle || useAccordion"
50
+ :type="useAccordion ? 'radio' : 'checkbox'"
51
+ :checked="isAccordionSelected"
52
+ >
31
53
  <slot />
32
54
  </div>
33
55
  </template>
@@ -5,9 +5,9 @@ import { computed } from 'vue'
5
5
  interface Props {
6
6
  durationInSeconds?: number
7
7
  untilDate?: Date
8
- interval?: number
9
8
  }
10
- const props = defineProps<Props>()
9
+ const props = withDefaults(defineProps<Props>(), { durationInSeconds: 0 })
10
+ const emit = defineEmits(['done'])
11
11
 
12
12
  const calculateDate = () => props.untilDate || new Date(Date.now() + props.durationInSeconds * 1000)
13
13
  const targetDate = ref<Date>(calculateDate())
@@ -20,7 +20,7 @@ watch(
20
20
  )
21
21
 
22
22
  // The target date
23
- const calcTimeLeft = () => {
23
+ function calcTimeLeft() {
24
24
  const remaining = targetDate.value.getTime() - Date.now()
25
25
  return remaining < 0 ? 0 : remaining
26
26
  }
@@ -29,6 +29,11 @@ useIntervalFn(() => {
29
29
  timeLeft.value = calcTimeLeft()
30
30
  }, 1000)
31
31
 
32
+ watch(timeLeft, () => {
33
+ if (timeLeft.value === 0)
34
+ emit('done')
35
+ })
36
+
32
37
  const totalSeconds = computed(() => Math.round(timeLeft.value / 1000))
33
38
  const totalMinutes = computed(() => Math.floor(timeLeft.value / 1000 / 60))
34
39
  const totalHours = computed(() => Math.floor(timeLeft.value / 1000 / 60 / 60))
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, withDefaults } from 'vue'
2
+ import { computed } from 'vue'
3
3
 
4
4
  interface Props {
5
5
  horizontal?: boolean
@@ -1,20 +1,44 @@
1
1
  <script setup lang="ts">
2
- import { inject } from 'vue'
3
- import { drawerStateKey } from './drawer-utils'
2
+ import { createDrawerState } from '../utils/drawer-utils'
4
3
 
5
- interface DrawerState {
6
- isDrawerOpen: boolean
7
- openDrawer: () => void
8
- closeDrawer: () => void
9
- toggleDrawer: () => void
10
- }
4
+ const props = withDefaults(defineProps<{
5
+ open?: boolean
6
+ name?: string
7
+ end?: boolean
8
+ }>(), {
9
+ name: 'drawer',
10
+ })
11
+ const emit = defineEmits(['update:open'])
11
12
 
12
- const drawerState: DrawerState | undefined = inject(drawerStateKey)
13
+ // sync `open` prop with drawerState.isDrawerOpen
14
+ const drawerState = createDrawerState(props.name)
15
+ watch(
16
+ () => props.open,
17
+ (value) => {
18
+ if (drawerState.isDrawerOpen !== value)
19
+ drawerState.isDrawerOpen = value
20
+ },
21
+ { immediate: true },
22
+ )
23
+ watch(
24
+ () => drawerState.isDrawerOpen,
25
+ (value) => {
26
+ if (props.open !== value)
27
+ emit('update:open', value)
28
+ },
29
+ { immediate: true },
30
+ )
31
+
32
+ const classes = computed(() => {
33
+ return {
34
+ 'drawer-end': props.end,
35
+ }
36
+ })
13
37
  </script>
14
38
 
15
39
  <template>
16
- <div class="drawer-side">
17
- <div class="drawer-overlay" @click="() => drawerState?.closeDrawer()" />
40
+ <div class="drawer" :class="classes">
41
+ <input :id="name" v-model="drawerState.isDrawerOpen" type="checkbox" class="drawer-toggle">
18
42
  <slot v-bind="drawerState" />
19
43
  </div>
20
44
  </template>
@@ -0,0 +1,15 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(defineProps<{
3
+ name?: string
4
+ }>(), {
5
+ name: 'drawer',
6
+ })
7
+
8
+ const drawerState = createDrawerState(props.name)
9
+ </script>
10
+
11
+ <template>
12
+ <div class="drawer-content">
13
+ <slot v-bind="drawerState" />
14
+ </div>
15
+ </template>
@@ -0,0 +1,16 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(defineProps<{
3
+ name?: string
4
+ }>(), {
5
+ name: 'drawer',
6
+ })
7
+
8
+ const drawerState = createDrawerState(props.name)
9
+ </script>
10
+
11
+ <template>
12
+ <div class="drawer-side">
13
+ <div class="drawer-overlay" @click="() => drawerState?.closeDrawer()" />
14
+ <slot v-bind="drawerState" />
15
+ </div>
16
+ </template>
@@ -1,30 +1,79 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
+ import { onClickOutside, syncRefs, useElementHover } from '@vueuse/core'
3
4
 
4
5
  interface Props {
6
+ position?: string
5
7
  top?: boolean
8
+ bottom?: boolean
6
9
  right?: boolean
7
10
  left?: boolean
11
+
8
12
  end?: boolean
9
13
  hover?: boolean
14
+ delayEnter?: number
15
+ delayLeave?: number
10
16
  open?: boolean
17
+ closeOnClickOutside?: boolean
11
18
  }
19
+ const props = withDefaults(defineProps<Props>(), {
20
+ position: 'bottom',
21
+ end: false,
22
+ hover: false,
23
+ delayEnter: 0,
24
+ delayLeave: 300,
25
+ closeOnClickOutside: false,
26
+ })
27
+ const isOpen = defineModel<boolean>('open', {
28
+ local: true,
29
+ default: false,
30
+ })
12
31
 
13
- const props = defineProps<Props>()
14
32
  const classes = computed(() => {
15
33
  return {
16
- 'dropdown-top': props.top,
17
- 'dropdown-left': props.left,
18
- 'dropdown-right': props.right,
34
+ 'dropdown-top': props.top || props.position === 'top',
35
+ 'dropdown-bottom': props.bottom || props.position === 'bottom',
36
+ 'dropdown-left': props.left || props.position === 'left',
37
+ 'dropdown-right': props.right || props.position === 'right',
19
38
  'dropdown-end': props.end,
20
39
  'dropdown-hover': props.hover,
21
- 'dropdown-open': props.open,
22
40
  }
23
41
  })
42
+
43
+ // const isOpen = ref(props.open)
44
+ watch(() => props.open, value => isOpen.value = value)
45
+
46
+ const dropdown = ref()
47
+ const isHovered = ref(false)
48
+
49
+ onMounted(() => {
50
+ // Close when clicking outside the element
51
+ onClickOutside(dropdown, () => {
52
+ if (props.closeOnClickOutside)
53
+ isOpen.value = false
54
+ })
55
+
56
+ // Sync with top-level isHovered ref. For SSR compatibility.
57
+ const hover = useElementHover(dropdown, {
58
+ delayLeave: props.delayLeave,
59
+ delayEnter: props.delayEnter,
60
+ })
61
+ syncRefs(hover, isHovered)
62
+ })
63
+
64
+ const shouldBeOpen = computed(() => {
65
+ return isOpen.value || (props.hover && isHovered.value)
66
+ })
67
+
68
+ function handleClick(ev: MouseEvent) {
69
+ ev.preventDefault()
70
+ if (ev.target === dropdown.value.children[0])
71
+ isOpen.value = !isOpen.value
72
+ }
24
73
  </script>
25
74
 
26
75
  <template>
27
- <div class="dropdown" :class="classes">
76
+ <details ref="dropdown" class="dropdown" :open="shouldBeOpen" :class="classes" @click="handleClick">
28
77
  <slot />
29
- </div>
78
+ </details>
30
79
  </template>
@@ -1,3 +1,5 @@
1
1
  <template>
2
- <div class="dropdown-content"><slot /></div>
2
+ <div class="dropdown-content">
3
+ <slot />
4
+ </div>
3
5
  </template>