daisy-ui-kit 5.0.0-pre.27 → 5.0.0-pre.29

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.
@@ -1,61 +1,201 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
3
 
4
- const props = defineProps<{
5
- modelValue?: number | string
6
- min?: number | string
7
- max?: number | string
8
- step?: number | string
9
- disabled?: boolean
10
-
11
- color?: string
12
- neutral?: boolean
13
- primary?: boolean
14
- secondary?: boolean
15
- accent?: boolean
16
- success?: boolean
17
- warning?: boolean
18
- info?: boolean
19
- error?: boolean
20
-
21
- size?: 'xl' | 'lg' | 'md' | 'sm' | 'xs'
22
- xl?: boolean
23
- lg?: boolean
24
- md?: boolean
25
- sm?: boolean
26
- xs?: boolean
27
- }>()
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ modelValue?: number | [number, number]
7
+ min?: number
8
+ max?: number
9
+ step?: number
10
+ disabled?: boolean
11
+
12
+ color?: string
13
+ neutral?: boolean
14
+ primary?: boolean
15
+ secondary?: boolean
16
+ accent?: boolean
17
+ success?: boolean
18
+ warning?: boolean
19
+ info?: boolean
20
+ error?: boolean
21
+
22
+ size?: 'xl' | 'lg' | 'md' | 'sm' | 'xs'
23
+ xl?: boolean
24
+ lg?: boolean
25
+ md?: boolean
26
+ sm?: boolean
27
+ xs?: boolean
28
+ }>(),
29
+ {
30
+ min: 0,
31
+ max: 100,
32
+ step: 1,
33
+ },
34
+ )
28
35
  const emit = defineEmits(['update:modelValue'])
29
36
 
30
- const currentValue = computed({
31
- get: () => props.modelValue,
32
- set: val => emit('update:modelValue', val),
37
+ const isRange = computed(() => {
38
+ return Array.isArray(props.modelValue)
39
+ })
40
+
41
+ const singleValue = computed({
42
+ get: () => (isRange.value ? 0 : (props.modelValue as number)),
43
+ set: val => emit('update:modelValue', Number(val)),
44
+ })
45
+
46
+ const lowValue = computed({
47
+ get: () => (isRange.value ? (props.modelValue as [number, number])[0] : 0),
48
+ set: val => {
49
+ const v = Number(val)
50
+ const high = (props.modelValue as [number, number])[1]
51
+ emit('update:modelValue', [Math.min(v, high), high])
52
+ },
53
+ })
54
+
55
+ const highValue = computed({
56
+ get: () => (isRange.value ? (props.modelValue as [number, number])[1] : 100),
57
+ set: val => {
58
+ const v = Number(val)
59
+ const low = (props.modelValue as [number, number])[0]
60
+ emit('update:modelValue', [low, Math.max(v, low)])
61
+ },
62
+ })
63
+
64
+ const rangeClasses = computed(() => ({
65
+ 'range-neutral': props.neutral || props.color === 'neutral',
66
+ 'range-primary': props.primary || props.color === 'primary',
67
+ 'range-secondary': props.secondary || props.color === 'secondary',
68
+ 'range-accent': props.accent || props.color === 'accent',
69
+ 'range-success': props.success || props.color === 'success',
70
+ 'range-info': props.info || props.color === 'info',
71
+ 'range-warning': props.warning || props.color === 'warning',
72
+ 'range-error': props.error || props.color === 'error',
73
+ 'range-xl': props.xl || props.size === 'xl',
74
+ 'range-lg': props.lg || props.size === 'lg',
75
+ 'range-md': props.md || props.size === 'md',
76
+ 'range-sm': props.sm || props.size === 'sm',
77
+ 'range-xs': props.xs || props.size === 'xs',
78
+ }))
79
+
80
+ // Calculate percentage positions for the filled track
81
+ const lowPercent = computed(() => {
82
+ const range = props.max - props.min
83
+ return ((lowValue.value - props.min) / range) * 100
84
+ })
85
+
86
+ const highPercent = computed(() => {
87
+ const range = props.max - props.min
88
+ return ((highValue.value - props.min) / range) * 100
33
89
  })
34
90
  </script>
35
91
 
36
92
  <template>
93
+ <!-- Single value mode -->
37
94
  <input
38
- v-model="currentValue"
95
+ v-if="!isRange"
96
+ v-model="singleValue"
39
97
  type="range"
40
98
  class="range"
41
- :class="{
42
- 'range-neutral': neutral || color === 'neutral',
43
- 'range-primary': primary || color === 'primary',
44
- 'range-secondary': secondary || color === 'secondary',
45
- 'range-accent': accent || color === 'accent',
46
- 'range-success': success || color === 'success',
47
- 'range-info': info || color === 'info',
48
- 'range-warning': warning || color === 'warning',
49
- 'range-error': error || color === 'error',
50
- 'range-xl': xl || size === 'xl',
51
- 'range-lg': lg || size === 'lg',
52
- 'range-md': md || size === 'md',
53
- 'range-sm': sm || size === 'sm',
54
- 'range-xs': xs || size === 'xs',
55
- }"
99
+ :class="rangeClasses"
56
100
  :min="min"
57
101
  :max="max"
58
102
  :step="step"
59
103
  :disabled="disabled"
60
104
  />
105
+
106
+ <!-- Dual handle range mode -->
107
+ <div v-else class="range range-slider-wrapper" :class="rangeClasses">
108
+ <div class="range-slider-track" />
109
+ <div
110
+ class="range-slider-fill"
111
+ :style="{
112
+ left: `calc(${lowPercent}% + (var(--range-thumb-size, 1.5rem) / 2) - (${lowPercent} * var(--range-thumb-size, 1.5rem) / 100))`,
113
+ width: `calc(${highPercent - lowPercent}% - (${highPercent - lowPercent} * var(--range-thumb-size, 1.5rem) / 100))`,
114
+ }"
115
+ />
116
+ <input
117
+ v-model="lowValue"
118
+ type="range"
119
+ class="range range-slider-input"
120
+ :class="rangeClasses"
121
+ :min="min"
122
+ :max="max"
123
+ :step="step"
124
+ :disabled="disabled"
125
+ />
126
+ <input
127
+ v-model="highValue"
128
+ type="range"
129
+ class="range range-slider-input"
130
+ :class="rangeClasses"
131
+ :min="min"
132
+ :max="max"
133
+ :step="step"
134
+ :disabled="disabled"
135
+ />
136
+ </div>
61
137
  </template>
138
+
139
+ <style>
140
+ .range-slider-wrapper {
141
+ position: relative;
142
+ width: 100%;
143
+ height: var(--range-thumb-size, 1.5rem);
144
+ }
145
+
146
+ .range-slider-track {
147
+ position: absolute;
148
+ top: 50%;
149
+ left: 0;
150
+ right: 0;
151
+ height: calc(var(--range-thumb-size, 1.5rem) / 3);
152
+ transform: translateY(-50%);
153
+ background: color-mix(in oklab, currentColor 10%, transparent);
154
+ border-radius: var(--radius-selector, 1rem);
155
+ }
156
+
157
+ .range-slider-fill {
158
+ position: absolute;
159
+ top: 50%;
160
+ height: var(--range-thumb-size, 1.5rem);
161
+ transform: translateY(-50%);
162
+ background: currentColor;
163
+ border-radius: 0;
164
+ z-index: 1;
165
+ }
166
+
167
+ .range-slider-input {
168
+ position: absolute;
169
+ top: 0;
170
+ left: 0;
171
+ width: 100%;
172
+ height: 100%;
173
+ pointer-events: none;
174
+ background: transparent;
175
+ --range-fill: 0 !important;
176
+ }
177
+
178
+ /* Hide both tracks - we draw our own */
179
+ .range-slider-input::-webkit-slider-runnable-track {
180
+ background: transparent !important;
181
+ box-shadow: none !important;
182
+ }
183
+
184
+ .range-slider-input::-moz-range-track {
185
+ background: transparent !important;
186
+ box-shadow: none !important;
187
+ }
188
+
189
+ /* Enable pointer events only on thumbs */
190
+ .range-slider-input::-webkit-slider-thumb {
191
+ pointer-events: auto;
192
+ position: relative;
193
+ z-index: 3;
194
+ }
195
+
196
+ .range-slider-input::-moz-range-thumb {
197
+ pointer-events: auto;
198
+ position: relative;
199
+ z-index: 3;
200
+ }
201
+ </style>
@@ -1,9 +1,13 @@
1
1
  <script setup lang="ts">
2
+ import { nextTick, onMounted, ref, watch } from 'vue'
3
+
2
4
  const props = withDefaults(
3
5
  defineProps<{
4
6
  modelValue?: string
5
7
  placeholder?: string
6
8
  type?: 'text' | 'phone' | 'email' | 'search'
9
+ rows?: number
10
+ autoExpand?: boolean
7
11
 
8
12
  color?: string
9
13
  neutral?: boolean
@@ -28,15 +32,48 @@ const props = withDefaults(
28
32
  }>(),
29
33
  {
30
34
  type: 'text',
35
+ rows: 2,
31
36
  },
32
37
  )
33
38
  defineEmits(['update:modelValue'])
39
+
40
+ const textareaRef = ref<HTMLTextAreaElement | null>(null)
41
+
42
+ let minHeight = 0
43
+
44
+ function adjustHeight() {
45
+ if (!props.autoExpand || !textareaRef.value) return
46
+ const el = textareaRef.value
47
+
48
+ // Capture the initial rendered height as minimum (respects rows attribute)
49
+ if (minHeight === 0) {
50
+ minHeight = el.offsetHeight
51
+ }
52
+
53
+ el.style.height = 'auto'
54
+ el.style.height = `${Math.max(el.scrollHeight, minHeight)}px`
55
+ }
56
+
57
+ watch(
58
+ () => props.modelValue,
59
+ () => {
60
+ nextTick(adjustHeight)
61
+ },
62
+ )
63
+
64
+ onMounted(() => {
65
+ if (props.autoExpand) {
66
+ nextTick(adjustHeight)
67
+ }
68
+ })
34
69
  </script>
35
70
 
36
71
  <template>
37
72
  <textarea
73
+ ref="textareaRef"
38
74
  :value="modelValue"
39
75
  :type="type"
76
+ :rows="rows"
40
77
  :placeholder="placeholder"
41
78
  :disabled="disabled"
42
79
  class="textarea"
@@ -59,6 +96,11 @@ defineEmits(['update:modelValue'])
59
96
  'textarea-sm': props.sm || props.size === 'sm',
60
97
  'textarea-xs': props.xs || props.size === 'xs',
61
98
  }"
62
- @input="event => $emit('update:modelValue', (event.target as HTMLTextAreaElement).value)"
99
+ @input="
100
+ event => {
101
+ $emit('update:modelValue', (event.target as HTMLTextAreaElement).value)
102
+ adjustHeight()
103
+ }
104
+ "
63
105
  />
64
106
  </template>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "daisy-ui-kit",
3
3
  "type": "module",
4
- "version": "5.0.0-pre.27",
4
+ "version": "5.0.0-pre.29",
5
5
  "packageManager": "pnpm@10.10.0",
6
6
  "author": "feathers.dev",
7
7
  "exports": {