@makolabs/ripple 3.0.0 → 3.0.2
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.
- package/dist/elements/accordion/Accordion.svelte +1 -1
- package/dist/elements/combobox/ComboBox.svelte +59 -29
- package/dist/elements/dropdown/Select.svelte +98 -62
- package/dist/elements/dropdown/select.d.ts +3 -108
- package/dist/elements/dropdown/select.js +37 -46
- package/dist/elements/popover/Popover.svelte +59 -36
- package/dist/filters/CompactFilters.svelte +1 -1
- package/dist/forms/Checkbox.svelte +24 -9
- package/dist/forms/DateRange.svelte +236 -204
- package/dist/forms/Input.svelte +18 -18
- package/dist/forms/MarketSelector.svelte +1 -1
- package/dist/forms/NumberInput.svelte +160 -55
- package/dist/forms/RadioGroup.svelte +6 -2
- package/dist/forms/SegmentedControl.svelte +1 -1
- package/dist/forms/Tags.svelte +32 -11
- package/dist/forms/Textarea.svelte +8 -13
- package/dist/forms/Toggle.svelte +22 -14
- package/dist/forms/calendar/Calendar.svelte +107 -8
- package/dist/forms/calendar/calendar-types.d.ts +8 -0
- package/dist/forms/date-picker/DatePicker.svelte +11 -14
- package/dist/forms/form-size.d.ts +37 -0
- package/dist/forms/form-size.js +67 -0
- package/dist/forms/form-types.d.ts +33 -0
- package/dist/forms/month-picker/MonthPicker.svelte +299 -0
- package/dist/forms/month-picker/MonthPicker.svelte.d.ts +4 -0
- package/dist/forms/month-picker/month-picker-types.d.ts +22 -0
- package/dist/forms/month-picker/month-picker-types.js +1 -0
- package/dist/forms/segmented-control.d.ts +2 -2
- package/dist/forms/segmented-control.js +18 -15
- package/dist/forms/slider.js +35 -28
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/layout/activity-list/ActivityList.svelte +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../helper/cls.js';
|
|
3
|
+
import { buildTestId } from '../../helper/testid.js';
|
|
4
|
+
import { Size } from '../../variants.js';
|
|
5
|
+
import { formSizeTokens } from '../form-size.js';
|
|
6
|
+
import Popover from '../../elements/popover/Popover.svelte';
|
|
7
|
+
import type { MonthPickerProps } from './month-picker-types.js';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
name,
|
|
11
|
+
id = name,
|
|
12
|
+
label,
|
|
13
|
+
value = $bindable<Date | null>(null),
|
|
14
|
+
placeholder = 'Select month',
|
|
15
|
+
minDate,
|
|
16
|
+
maxDate,
|
|
17
|
+
clearable = true,
|
|
18
|
+
disabled = false,
|
|
19
|
+
size = Size.MD,
|
|
20
|
+
errors = [],
|
|
21
|
+
class: className = '',
|
|
22
|
+
onselect,
|
|
23
|
+
testId
|
|
24
|
+
}: MonthPickerProps = $props();
|
|
25
|
+
|
|
26
|
+
let open = $state(false);
|
|
27
|
+
let listEl = $state<HTMLDivElement | undefined>();
|
|
28
|
+
const tokens = $derived(formSizeTokens[size]);
|
|
29
|
+
const hasErrors = $derived(errors.length > 0);
|
|
30
|
+
|
|
31
|
+
const MONTH_NAMES = [
|
|
32
|
+
'January',
|
|
33
|
+
'February',
|
|
34
|
+
'March',
|
|
35
|
+
'April',
|
|
36
|
+
'May',
|
|
37
|
+
'June',
|
|
38
|
+
'July',
|
|
39
|
+
'August',
|
|
40
|
+
'September',
|
|
41
|
+
'October',
|
|
42
|
+
'November',
|
|
43
|
+
'December'
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
const MONTH_SHORT = [
|
|
47
|
+
'Jan',
|
|
48
|
+
'Feb',
|
|
49
|
+
'Mar',
|
|
50
|
+
'Apr',
|
|
51
|
+
'May',
|
|
52
|
+
'Jun',
|
|
53
|
+
'Jul',
|
|
54
|
+
'Aug',
|
|
55
|
+
'Sep',
|
|
56
|
+
'Oct',
|
|
57
|
+
'Nov',
|
|
58
|
+
'Dec'
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
const display = $derived(value ? `${MONTH_NAMES[value.getMonth()]} ${value.getFullYear()}` : '');
|
|
62
|
+
|
|
63
|
+
// Build a flat list of months spanning a range of years. Default:
|
|
64
|
+
// 5 years before → 5 years after the anchor (selected or today).
|
|
65
|
+
const RANGE_YEARS = 5;
|
|
66
|
+
type MonthEntry = { year: number; month: number; date: Date; label: string; disabled: boolean };
|
|
67
|
+
|
|
68
|
+
const anchor = $derived(value ?? new Date());
|
|
69
|
+
const startYear = $derived(minDate ? minDate.getFullYear() : anchor.getFullYear() - RANGE_YEARS);
|
|
70
|
+
const endYear = $derived(maxDate ? maxDate.getFullYear() : anchor.getFullYear() + RANGE_YEARS);
|
|
71
|
+
|
|
72
|
+
const months = $derived.by<MonthEntry[]>(() => {
|
|
73
|
+
const out: MonthEntry[] = [];
|
|
74
|
+
for (let y = startYear; y <= endYear; y++) {
|
|
75
|
+
for (let m = 0; m < 12; m++) {
|
|
76
|
+
const d = new Date(y, m, 1);
|
|
77
|
+
let disabled = false;
|
|
78
|
+
if (minDate && d < new Date(minDate.getFullYear(), minDate.getMonth(), 1)) disabled = true;
|
|
79
|
+
if (maxDate && d > new Date(maxDate.getFullYear(), maxDate.getMonth(), 1)) disabled = true;
|
|
80
|
+
out.push({
|
|
81
|
+
year: y,
|
|
82
|
+
month: m,
|
|
83
|
+
date: d,
|
|
84
|
+
label: MONTH_NAMES[m],
|
|
85
|
+
disabled
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Unique sorted years and a fast lookup by year*12+month key.
|
|
93
|
+
const years = $derived([...new Set(months.map((e) => e.year))]);
|
|
94
|
+
const monthMap = $derived(new Map(months.map((e) => [e.year * 12 + e.month, e])));
|
|
95
|
+
|
|
96
|
+
function isSelected(entry: MonthEntry): boolean {
|
|
97
|
+
return !!value && value.getFullYear() === entry.year && value.getMonth() === entry.month;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isCurrent(entry: MonthEntry): boolean {
|
|
101
|
+
const now = new Date();
|
|
102
|
+
return entry.year === now.getFullYear() && entry.month === now.getMonth();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function pick(entry: MonthEntry) {
|
|
106
|
+
if (entry.disabled) return;
|
|
107
|
+
value = entry.date;
|
|
108
|
+
onselect?.(entry.date);
|
|
109
|
+
open = false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function clear(e: MouseEvent) {
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
value = null;
|
|
115
|
+
open = false;
|
|
116
|
+
onselect?.(null);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function setThisMonth() {
|
|
120
|
+
const now = new Date();
|
|
121
|
+
value = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
122
|
+
onselect?.(value);
|
|
123
|
+
open = false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Auto-scroll to the selected (or current) month when the panel opens.
|
|
127
|
+
$effect(() => {
|
|
128
|
+
if (!open || !listEl) return;
|
|
129
|
+
requestAnimationFrame(() => {
|
|
130
|
+
const target =
|
|
131
|
+
listEl?.querySelector('[data-selected="true"]') ??
|
|
132
|
+
listEl?.querySelector('[data-current="true"]');
|
|
133
|
+
target?.scrollIntoView({ block: 'center' });
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<div class={cn('w-full', className)}>
|
|
139
|
+
{#if label}
|
|
140
|
+
<label for={id} class="text-default-700 mb-1 block text-sm font-medium">
|
|
141
|
+
{label}
|
|
142
|
+
</label>
|
|
143
|
+
{/if}
|
|
144
|
+
|
|
145
|
+
<input type="hidden" {name} value={value?.toISOString() ?? ''} />
|
|
146
|
+
|
|
147
|
+
<Popover trigger="manual" bind:open placement="bottom" {disabled}>
|
|
148
|
+
<button
|
|
149
|
+
{id}
|
|
150
|
+
type="button"
|
|
151
|
+
{disabled}
|
|
152
|
+
onclick={() => {
|
|
153
|
+
if (!disabled) open = !open;
|
|
154
|
+
}}
|
|
155
|
+
aria-haspopup="dialog"
|
|
156
|
+
aria-expanded={open}
|
|
157
|
+
class={cn(
|
|
158
|
+
'flex w-full items-center border bg-white transition-colors',
|
|
159
|
+
'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
|
160
|
+
tokens.height,
|
|
161
|
+
tokens.padX,
|
|
162
|
+
tokens.text,
|
|
163
|
+
tokens.gap,
|
|
164
|
+
tokens.radius,
|
|
165
|
+
tokens.shadow,
|
|
166
|
+
hasErrors
|
|
167
|
+
? 'border-danger-300 focus-visible:border-danger-500 focus-visible:ring-danger-500'
|
|
168
|
+
: 'border-default-300 focus-visible:border-primary-500 focus-visible:ring-primary-500',
|
|
169
|
+
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
170
|
+
)}
|
|
171
|
+
data-testid={buildTestId('month-picker', undefined, testId)}
|
|
172
|
+
>
|
|
173
|
+
<span class={cn('flex-1 text-left', !display && 'text-default-400')}>
|
|
174
|
+
{display || placeholder}
|
|
175
|
+
</span>
|
|
176
|
+
{#if clearable && value && !disabled}
|
|
177
|
+
<span
|
|
178
|
+
role="button"
|
|
179
|
+
tabindex="-1"
|
|
180
|
+
onclick={clear}
|
|
181
|
+
onkeydown={(e) => {
|
|
182
|
+
if (e.key === 'Enter') clear(e as unknown as MouseEvent);
|
|
183
|
+
}}
|
|
184
|
+
aria-label="Clear month"
|
|
185
|
+
class={cn(
|
|
186
|
+
'text-default-400 hover:text-default-700 flex cursor-pointer items-center justify-center rounded',
|
|
187
|
+
tokens.iconSize
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
<svg class="size-3" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
191
|
+
<path
|
|
192
|
+
d="M3 3l6 6M9 3l-6 6"
|
|
193
|
+
stroke="currentColor"
|
|
194
|
+
stroke-width="1.5"
|
|
195
|
+
stroke-linecap="round"
|
|
196
|
+
/>
|
|
197
|
+
</svg>
|
|
198
|
+
</span>
|
|
199
|
+
{/if}
|
|
200
|
+
<svg
|
|
201
|
+
class={cn('text-default-400', tokens.iconSize)}
|
|
202
|
+
viewBox="0 0 20 20"
|
|
203
|
+
fill="currentColor"
|
|
204
|
+
aria-hidden="true"
|
|
205
|
+
>
|
|
206
|
+
<path
|
|
207
|
+
fill-rule="evenodd"
|
|
208
|
+
d="M5.75 2a.75.75 0 0 1 .75.75V4h7V2.75a.75.75 0 0 1 1.5 0V4h.25A2.75 2.75 0 0 1 18 6.75v8.5A2.75 2.75 0 0 1 15.25 18H4.75A2.75 2.75 0 0 1 2 15.25v-8.5A2.75 2.75 0 0 1 4.75 4H5V2.75A.75.75 0 0 1 5.75 2zM4.75 5.5c-.69 0-1.25.56-1.25 1.25V8h13V6.75c0-.69-.56-1.25-1.25-1.25H4.75zM16.5 9.5h-13v5.75c0 .69.56 1.25 1.25 1.25h10.5c.69 0 1.25-.56 1.25-1.25V9.5z"
|
|
209
|
+
clip-rule="evenodd"
|
|
210
|
+
/>
|
|
211
|
+
</svg>
|
|
212
|
+
</button>
|
|
213
|
+
|
|
214
|
+
{#snippet content({ close })}
|
|
215
|
+
{@render monthGrid(close)}
|
|
216
|
+
{/snippet}
|
|
217
|
+
</Popover>
|
|
218
|
+
|
|
219
|
+
{#if hasErrors}
|
|
220
|
+
<ul class="mt-1 space-y-0.5" role="alert">
|
|
221
|
+
{#each errors as error (error)}
|
|
222
|
+
<li class="text-danger-600 text-xs">{error}</li>
|
|
223
|
+
{/each}
|
|
224
|
+
</ul>
|
|
225
|
+
{/if}
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
{#snippet monthGrid(close: () => void)}
|
|
229
|
+
<div class="flex w-full flex-col sm:w-56">
|
|
230
|
+
<div
|
|
231
|
+
bind:this={listEl}
|
|
232
|
+
class="max-h-72 overflow-y-auto [scrollbar-width:none] sm:max-h-72 [&::-webkit-scrollbar]:hidden"
|
|
233
|
+
>
|
|
234
|
+
{#each years as year (year)}
|
|
235
|
+
{@const yearSelected = value?.getFullYear() === year}
|
|
236
|
+
<div
|
|
237
|
+
class={cn(
|
|
238
|
+
'border-default-200 sticky top-0 z-10 border-b px-3 py-1.5 text-xs font-bold',
|
|
239
|
+
yearSelected ? 'bg-primary-50 text-primary-700' : 'bg-default-50 text-default-800'
|
|
240
|
+
)}
|
|
241
|
+
data-year={year}
|
|
242
|
+
>
|
|
243
|
+
{year}
|
|
244
|
+
</div>
|
|
245
|
+
<div class="grid grid-cols-4 gap-0.5 p-1.5">
|
|
246
|
+
{#each MONTH_SHORT as monthLabel, m (m)}
|
|
247
|
+
{@const entry = monthMap.get(year * 12 + m)}
|
|
248
|
+
{@const sel = entry ? isSelected(entry) : false}
|
|
249
|
+
{@const cur = entry ? isCurrent(entry) : false}
|
|
250
|
+
{@const dis = entry?.disabled ?? true}
|
|
251
|
+
<button
|
|
252
|
+
type="button"
|
|
253
|
+
data-month={m}
|
|
254
|
+
data-selected={sel || undefined}
|
|
255
|
+
data-current={cur || undefined}
|
|
256
|
+
onclick={() => entry && pick(entry)}
|
|
257
|
+
disabled={dis}
|
|
258
|
+
class={cn(
|
|
259
|
+
'cursor-pointer rounded px-1 py-2 text-xs font-medium transition-colors sm:py-1',
|
|
260
|
+
sel
|
|
261
|
+
? 'bg-primary-500 text-white'
|
|
262
|
+
: cur
|
|
263
|
+
? 'text-primary-600 bg-primary-50 ring-primary-300 ring-1'
|
|
264
|
+
: 'text-default-600 hover:bg-default-100',
|
|
265
|
+
dis && 'text-default-300 cursor-not-allowed'
|
|
266
|
+
)}
|
|
267
|
+
>
|
|
268
|
+
{monthLabel}
|
|
269
|
+
</button>
|
|
270
|
+
{/each}
|
|
271
|
+
</div>
|
|
272
|
+
{/each}
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div class="border-default-200 flex items-center justify-between border-t px-3 py-2">
|
|
276
|
+
{#if clearable}
|
|
277
|
+
<button
|
|
278
|
+
type="button"
|
|
279
|
+
onclick={(e) => {
|
|
280
|
+
clear(e);
|
|
281
|
+
close();
|
|
282
|
+
}}
|
|
283
|
+
class="text-primary-600 cursor-pointer text-xs font-medium hover:underline"
|
|
284
|
+
>
|
|
285
|
+
Clear
|
|
286
|
+
</button>
|
|
287
|
+
{:else}
|
|
288
|
+
<span></span>
|
|
289
|
+
{/if}
|
|
290
|
+
<button
|
|
291
|
+
type="button"
|
|
292
|
+
onclick={setThisMonth}
|
|
293
|
+
class="text-primary-600 cursor-pointer text-xs font-medium hover:underline"
|
|
294
|
+
>
|
|
295
|
+
This month
|
|
296
|
+
</button>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
{/snippet}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ClassValue } from 'tailwind-variants';
|
|
2
|
+
import type { VariantSizes } from '../../index.js';
|
|
3
|
+
export type MonthPickerProps = {
|
|
4
|
+
name: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Bindable selected month. Always the 1st of the month, or null. */
|
|
8
|
+
value?: Date | null;
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
/** Earliest selectable month (inclusive, day is ignored). */
|
|
11
|
+
minDate?: Date;
|
|
12
|
+
/** Latest selectable month (inclusive, day is ignored). */
|
|
13
|
+
maxDate?: Date;
|
|
14
|
+
/** Show a × to clear the selection. @default true */
|
|
15
|
+
clearable?: boolean;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
size?: VariantSizes;
|
|
18
|
+
errors?: string[];
|
|
19
|
+
class?: ClassValue;
|
|
20
|
+
onselect?: (value: Date | null) => void;
|
|
21
|
+
testId?: string;
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,7 +12,7 @@ export declare const segmentedTrack: import("tailwind-variants").TVReturnType<{
|
|
|
12
12
|
vertical: string;
|
|
13
13
|
auto: string;
|
|
14
14
|
};
|
|
15
|
-
}, undefined, "
|
|
15
|
+
}, undefined, "flex max-w-full overflow-x-auto rounded-lg border sm:inline-flex [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", {
|
|
16
16
|
appearance: {
|
|
17
17
|
surface: string;
|
|
18
18
|
inverted: string;
|
|
@@ -34,7 +34,7 @@ export declare const segmentedTrack: import("tailwind-variants").TVReturnType<{
|
|
|
34
34
|
vertical: string;
|
|
35
35
|
auto: string;
|
|
36
36
|
};
|
|
37
|
-
}, undefined, "
|
|
37
|
+
}, undefined, "flex max-w-full overflow-x-auto rounded-lg border sm:inline-flex [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", unknown, unknown, undefined>>;
|
|
38
38
|
export declare function segmentClasses(args: {
|
|
39
39
|
selected: boolean;
|
|
40
40
|
disabled: boolean;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { tv } from 'tailwind-variants';
|
|
2
2
|
import { cn } from '../helper/cls.js';
|
|
3
|
-
import { Color
|
|
3
|
+
import { Color } from '../variants.js';
|
|
4
|
+
import { formSizeTokens } from './form-size.js';
|
|
4
5
|
export const segmentedTrack = tv({
|
|
5
|
-
// `
|
|
6
|
-
//
|
|
7
|
-
|
|
6
|
+
// Below `sm`, take full width so the control fills its container on
|
|
7
|
+
// mobile. Above `sm`, shrink to content via `inline-flex`. Either
|
|
8
|
+
// way, `overflow-x-auto` + hidden scrollbar lets a long segment row
|
|
9
|
+
// swipe horizontally without a visible bar.
|
|
10
|
+
base: 'flex max-w-full overflow-x-auto rounded-lg border sm:inline-flex [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
|
|
8
11
|
variants: {
|
|
9
12
|
appearance: {
|
|
10
13
|
surface: 'border-default-200 bg-white p-0',
|
|
@@ -36,28 +39,28 @@ const selectedByColor = {
|
|
|
36
39
|
[Color.WARNING]: 'bg-warning-600 text-white shadow-sm',
|
|
37
40
|
[Color.DANGER]: 'bg-danger-600 text-white shadow-sm'
|
|
38
41
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
42
|
+
// Pull padding/text/gap from the shared form-size tokens so a
|
|
43
|
+
// SegmentedControl at `size="md"` coordinates with Input / Select /
|
|
44
|
+
// DateRange / etc. at the same tier. Segments don't use `height` —
|
|
45
|
+
// their height falls out of text + padding.
|
|
46
|
+
function segmentSize(size) {
|
|
47
|
+
const t = formSizeTokens[size];
|
|
48
|
+
return `${t.gap} ${t.padX} ${t.padY} ${t.text}`;
|
|
49
|
+
}
|
|
47
50
|
export function segmentClasses(args) {
|
|
48
51
|
const { selected, disabled, appearance, color, size } = args;
|
|
49
52
|
if (appearance === 'pills') {
|
|
50
|
-
const pillBase = cn('flex cursor-pointer items-center justify-center rounded-full font-medium transition-colors duration-150
|
|
53
|
+
const pillBase = cn('flex cursor-pointer items-center justify-center rounded-full font-medium transition-colors duration-150 flex-1 whitespace-nowrap sm:flex-initial sm:shrink-0', 'focus-visible:ring-primary-500 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-offset-2', segmentSize(size), disabled && 'cursor-not-allowed opacity-50');
|
|
51
54
|
if (disabled)
|
|
52
55
|
return cn(pillBase, 'text-default-400');
|
|
53
56
|
if (selected)
|
|
54
57
|
return cn(pillBase, selectedByColor[color]);
|
|
55
58
|
return cn(pillBase, 'bg-default-100 text-default-700 hover:bg-default-200');
|
|
56
59
|
}
|
|
57
|
-
const base = cn('flex cursor-pointer items-center justify-center font-medium transition-colors duration-150
|
|
60
|
+
const base = cn('flex cursor-pointer items-center justify-center font-medium transition-colors duration-150 flex-1 whitespace-nowrap sm:flex-initial sm:shrink-0', 'focus-visible:ring-primary-500 focus-visible:ring-2 focus-visible:outline-none', appearance === 'inverted' ? 'focus-visible:ring-offset-0' : 'focus-visible:ring-offset-2',
|
|
58
61
|
// Inverted track has p-0.5 padding around segments — give them inner radius
|
|
59
62
|
// so selected segments don't render as square boxes (matches surface look).
|
|
60
|
-
appearance === 'inverted' && 'rounded-md', segmentSize
|
|
63
|
+
appearance === 'inverted' && 'rounded-md', segmentSize(size), disabled && 'cursor-not-allowed opacity-50');
|
|
61
64
|
if (disabled) {
|
|
62
65
|
return cn(base, 'text-default-400');
|
|
63
66
|
}
|
package/dist/forms/slider.js
CHANGED
|
@@ -1,62 +1,69 @@
|
|
|
1
1
|
import { tv } from 'tailwind-variants';
|
|
2
2
|
import { Size } from '../variants.js';
|
|
3
|
+
import { formSizeTokens } from './form-size.js';
|
|
4
|
+
// Track/thumb dimensions are Slider-specific — a "size" for a slider
|
|
5
|
+
// means the physical rail, not an input height. We keep these tuned per
|
|
6
|
+
// tier but pull `text` classes from the shared form-size tokens so a
|
|
7
|
+
// Slider's label reads the same size as an adjacent Input's text.
|
|
8
|
+
const textFor = (size) => formSizeTokens[size].text;
|
|
3
9
|
export const slider = tv({
|
|
4
10
|
slots: {
|
|
5
11
|
base: 'relative w-full',
|
|
6
|
-
track: 'absolute
|
|
12
|
+
track: 'absolute w-full rounded-full bg-default-200 cursor-pointer',
|
|
7
13
|
range: 'absolute h-full rounded-full bg-primary-500',
|
|
8
14
|
thumb: [
|
|
9
15
|
'absolute top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white border-2 border-primary-500',
|
|
10
16
|
'focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
|
|
11
17
|
'hover:scale-110 transition-transform cursor-pointer'
|
|
12
18
|
],
|
|
13
|
-
mark: 'absolute text-
|
|
14
|
-
label: 'mb-2 block
|
|
15
|
-
value: 'mt-1 text-
|
|
19
|
+
mark: 'absolute text-default-500 -translate-x-1/2',
|
|
20
|
+
label: 'mb-2 block font-medium text-default-700',
|
|
21
|
+
value: 'mt-1 text-default-500'
|
|
16
22
|
},
|
|
17
23
|
variants: {
|
|
18
24
|
size: {
|
|
19
25
|
[Size.XS]: {
|
|
20
|
-
track: 'h-
|
|
21
|
-
thumb: 'w-
|
|
22
|
-
mark:
|
|
23
|
-
label:
|
|
24
|
-
value:
|
|
26
|
+
track: 'h-0.5',
|
|
27
|
+
thumb: 'w-2.5 h-2.5',
|
|
28
|
+
mark: `${textFor(Size.XS)} top-3`,
|
|
29
|
+
label: textFor(Size.XS),
|
|
30
|
+
value: textFor(Size.XS)
|
|
25
31
|
},
|
|
26
32
|
[Size.SM]: {
|
|
27
33
|
track: 'h-1',
|
|
28
34
|
thumb: 'w-3 h-3',
|
|
29
|
-
mark:
|
|
30
|
-
label:
|
|
31
|
-
value:
|
|
35
|
+
mark: `${textFor(Size.SM)} top-4`,
|
|
36
|
+
label: textFor(Size.SM),
|
|
37
|
+
value: textFor(Size.SM)
|
|
32
38
|
},
|
|
33
39
|
[Size.MD]: {
|
|
34
|
-
track: 'h-
|
|
35
|
-
thumb: 'w-
|
|
36
|
-
mark:
|
|
37
|
-
label:
|
|
38
|
-
value:
|
|
40
|
+
track: 'h-1.5',
|
|
41
|
+
thumb: 'w-3.5 h-3.5',
|
|
42
|
+
mark: `${textFor(Size.MD)} top-5`,
|
|
43
|
+
label: textFor(Size.MD),
|
|
44
|
+
value: textFor(Size.MD)
|
|
39
45
|
},
|
|
40
46
|
[Size.LG]: {
|
|
41
|
-
track: 'h-
|
|
42
|
-
thumb: 'w-
|
|
43
|
-
mark:
|
|
44
|
-
label:
|
|
45
|
-
value:
|
|
47
|
+
track: 'h-2',
|
|
48
|
+
thumb: 'w-4 h-4',
|
|
49
|
+
mark: `${textFor(Size.LG)} top-6`,
|
|
50
|
+
label: textFor(Size.LG),
|
|
51
|
+
value: textFor(Size.LG)
|
|
46
52
|
},
|
|
47
53
|
[Size.XL]: {
|
|
48
54
|
track: 'h-3',
|
|
49
55
|
thumb: 'w-5 h-5',
|
|
50
|
-
mark:
|
|
51
|
-
label:
|
|
52
|
-
value:
|
|
56
|
+
mark: `${textFor(Size.XL)} top-7`,
|
|
57
|
+
label: textFor(Size.XL),
|
|
58
|
+
value: textFor(Size.XL)
|
|
53
59
|
},
|
|
60
|
+
// Form controls cap at xl — see `form-size.ts`.
|
|
54
61
|
[Size.XXL]: {
|
|
55
62
|
track: 'h-3',
|
|
56
63
|
thumb: 'w-5 h-5',
|
|
57
|
-
mark:
|
|
58
|
-
label:
|
|
59
|
-
value:
|
|
64
|
+
mark: `${textFor(Size.XXL)} top-7`,
|
|
65
|
+
label: textFor(Size.XXL),
|
|
66
|
+
value: textFor(Size.XXL)
|
|
60
67
|
}
|
|
61
68
|
},
|
|
62
69
|
disabled: {
|
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export type { TooltipProps, TooltipPlacement, TooltipSize, TooltipVariant } from
|
|
|
51
51
|
export type { PopoverProps, PopoverPlacement, PopoverTrigger } from './elements/popover/popover-types.js';
|
|
52
52
|
export type { CalendarProps, CalendarMode } from './forms/calendar/calendar-types.js';
|
|
53
53
|
export type { DatePickerProps } from './forms/date-picker/date-picker-types.js';
|
|
54
|
+
export type { MonthPickerProps } from './forms/month-picker/month-picker-types.js';
|
|
54
55
|
export type { ComboBoxProps, ComboBoxItem } from './elements/combobox/combobox-types.js';
|
|
55
56
|
export type { ContextMenuProps, ContextMenuItem } from './elements/context-menu/context-menu-types.js';
|
|
56
57
|
export type { AccordionProps } from './elements/accordion/accordion-types.js';
|
|
@@ -124,6 +125,7 @@ export { default as NumberInput } from './forms/NumberInput.svelte';
|
|
|
124
125
|
export { default as DateRange } from './forms/DateRange.svelte';
|
|
125
126
|
export { default as Calendar } from './forms/calendar/Calendar.svelte';
|
|
126
127
|
export { default as DatePicker } from './forms/date-picker/DatePicker.svelte';
|
|
128
|
+
export { default as MonthPicker } from './forms/month-picker/MonthPicker.svelte';
|
|
127
129
|
export { default as Tags } from './forms/Tags.svelte';
|
|
128
130
|
export { default as SegmentedControl } from './forms/SegmentedControl.svelte';
|
|
129
131
|
export { default as MarketSelector } from './forms/MarketSelector.svelte';
|
package/dist/index.js
CHANGED
|
@@ -112,6 +112,7 @@ export { default as NumberInput } from './forms/NumberInput.svelte';
|
|
|
112
112
|
export { default as DateRange } from './forms/DateRange.svelte';
|
|
113
113
|
export { default as Calendar } from './forms/calendar/Calendar.svelte';
|
|
114
114
|
export { default as DatePicker } from './forms/date-picker/DatePicker.svelte';
|
|
115
|
+
export { default as MonthPicker } from './forms/month-picker/MonthPicker.svelte';
|
|
115
116
|
export { default as Tags } from './forms/Tags.svelte';
|
|
116
117
|
export { default as SegmentedControl } from './forms/SegmentedControl.svelte';
|
|
117
118
|
export { default as MarketSelector } from './forms/MarketSelector.svelte';
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
<div class="mb-1 flex justify-between gap-x-4">
|
|
116
116
|
<div class="flex flex-wrap items-center gap-2">
|
|
117
117
|
<button
|
|
118
|
-
class="text-default-900 text-sm font-medium"
|
|
118
|
+
class="text-default-900 cursor-pointer text-sm font-medium"
|
|
119
119
|
onclick={() => onitemclick?.(activityItem, index)}
|
|
120
120
|
>
|
|
121
121
|
{activityItem.title}
|