@makolabs/ripple 3.0.1 → 3.0.3
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/charts/Chart.svelte +8 -36
- package/dist/drawer/Drawer.svelte +29 -36
- package/dist/drawer/drawer.js +2 -2
- package/dist/elements/accordion/Accordion.svelte +1 -1
- package/dist/elements/combobox/ComboBox.svelte +49 -16
- package/dist/elements/dropdown/Select.svelte +104 -70
- package/dist/elements/popover/Popover.svelte +48 -45
- package/dist/filters/CompactFilters.svelte +1 -1
- package/dist/forms/DateRange.svelte +218 -199
- package/dist/forms/NumberInput.svelte +148 -39
- package/dist/forms/SegmentedControl.svelte +1 -1
- package/dist/forms/Toggle.svelte +18 -18
- package/dist/forms/calendar/Calendar.svelte +2 -1
- package/dist/forms/date-picker/DatePicker.svelte +8 -1
- package/dist/forms/form-types.d.ts +28 -0
- package/dist/forms/month-picker/MonthPicker.svelte +306 -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 +7 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/layout/activity-list/ActivityList.svelte +1 -1
- package/dist/modal/modal.js +4 -4
- package/package.json +1 -1
package/dist/forms/Toggle.svelte
CHANGED
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
const toggleSize = $derived(
|
|
46
46
|
(
|
|
47
47
|
{
|
|
48
|
-
[Size.XS]: 'w-
|
|
49
|
-
[Size.SM]: 'w-
|
|
50
|
-
[Size.MD]: 'w-
|
|
51
|
-
[Size.LG]: 'w-
|
|
52
|
-
[Size.XL]: 'w-
|
|
53
|
-
[Size.XXL]: 'w-
|
|
48
|
+
[Size.XS]: 'w-7 h-3.5',
|
|
49
|
+
[Size.SM]: 'w-8 h-4',
|
|
50
|
+
[Size.MD]: 'w-10 h-5',
|
|
51
|
+
[Size.LG]: 'w-12 h-6',
|
|
52
|
+
[Size.XL]: 'w-14 h-7',
|
|
53
|
+
[Size.XXL]: 'w-14 h-7'
|
|
54
54
|
} satisfies Record<VariantSizes, string>
|
|
55
55
|
)[size]
|
|
56
56
|
);
|
|
@@ -58,12 +58,12 @@
|
|
|
58
58
|
const thumbSize = $derived(
|
|
59
59
|
(
|
|
60
60
|
{
|
|
61
|
-
[Size.XS]: 'h-2 w-2',
|
|
62
|
-
[Size.SM]: 'h-
|
|
63
|
-
[Size.MD]: 'h-
|
|
64
|
-
[Size.LG]: 'h-
|
|
65
|
-
[Size.XL]: 'h-
|
|
66
|
-
[Size.XXL]: 'h-
|
|
61
|
+
[Size.XS]: 'h-2.5 w-2.5',
|
|
62
|
+
[Size.SM]: 'h-3 w-3',
|
|
63
|
+
[Size.MD]: 'h-4 w-4',
|
|
64
|
+
[Size.LG]: 'h-5 w-5',
|
|
65
|
+
[Size.XL]: 'h-6 w-6',
|
|
66
|
+
[Size.XXL]: 'h-6 w-6'
|
|
67
67
|
} satisfies Record<VariantSizes, string>
|
|
68
68
|
)[size]
|
|
69
69
|
);
|
|
@@ -71,12 +71,12 @@
|
|
|
71
71
|
const thumbPosition = $derived(
|
|
72
72
|
(
|
|
73
73
|
{
|
|
74
|
-
[Size.XS]: value ? 'translate-x-3' : 'translate-x-0.5',
|
|
75
|
-
[Size.SM]: value ? 'translate-x-
|
|
76
|
-
[Size.MD]: value ? 'translate-x-
|
|
77
|
-
[Size.LG]: value ? 'translate-x-
|
|
78
|
-
[Size.XL]: value ? 'translate-x-
|
|
79
|
-
[Size.XXL]: value ? 'translate-x-
|
|
74
|
+
[Size.XS]: value ? 'translate-x-3.5' : 'translate-x-0.5',
|
|
75
|
+
[Size.SM]: value ? 'translate-x-4' : 'translate-x-0.5',
|
|
76
|
+
[Size.MD]: value ? 'translate-x-5' : 'translate-x-0.5',
|
|
77
|
+
[Size.LG]: value ? 'translate-x-6' : 'translate-x-0.5',
|
|
78
|
+
[Size.XL]: value ? 'translate-x-7' : 'translate-x-0.5',
|
|
79
|
+
[Size.XXL]: value ? 'translate-x-7' : 'translate-x-0.5'
|
|
80
80
|
} satisfies Record<VariantSizes, string>
|
|
81
81
|
)[size]
|
|
82
82
|
);
|
|
@@ -228,7 +228,8 @@
|
|
|
228
228
|
|
|
229
229
|
<div
|
|
230
230
|
class={cn(
|
|
231
|
-
'
|
|
231
|
+
'inline-block bg-white select-none',
|
|
232
|
+
'border-default-200 rounded-lg border shadow-xs',
|
|
232
233
|
density.panel,
|
|
233
234
|
density.padding,
|
|
234
235
|
className
|
|
@@ -127,7 +127,14 @@
|
|
|
127
127
|
</button>
|
|
128
128
|
|
|
129
129
|
{#snippet content()}
|
|
130
|
-
<Calendar
|
|
130
|
+
<Calendar
|
|
131
|
+
{value}
|
|
132
|
+
{minDate}
|
|
133
|
+
{maxDate}
|
|
134
|
+
{size}
|
|
135
|
+
class="max-sm:w-full max-sm:rounded-none max-sm:border-0 max-sm:shadow-none"
|
|
136
|
+
onselect={(d) => handleSelect(d as Date)}
|
|
137
|
+
/>
|
|
131
138
|
{/snippet}
|
|
132
139
|
</Popover>
|
|
133
140
|
|
|
@@ -236,6 +236,24 @@ export type NumberInputProps = {
|
|
|
236
236
|
placeholder?: string;
|
|
237
237
|
size?: VariantSizes;
|
|
238
238
|
class?: ClassValue;
|
|
239
|
+
/**
|
|
240
|
+
* Leading icon rendered inside the field. Pass a Svelte component
|
|
241
|
+
* (e.g. an icon from your icon library) to fully customise, or use
|
|
242
|
+
* `iconPreset` for built-in options. When both are set, `icon` wins.
|
|
243
|
+
*/
|
|
244
|
+
icon?: Component;
|
|
245
|
+
/**
|
|
246
|
+
* Built-in leading icon preset. Saves importing a separate icon
|
|
247
|
+
* component for common use cases.
|
|
248
|
+
* - `'currency'` — banknotes
|
|
249
|
+
* - `'quantity'` — hash / #
|
|
250
|
+
* - `'percentage'` — %
|
|
251
|
+
* - `'weight'` — scale
|
|
252
|
+
* - `'temperature'` — thermometer
|
|
253
|
+
*
|
|
254
|
+
* Omit (or pass `undefined`) for no icon. Overridden by `icon` prop.
|
|
255
|
+
*/
|
|
256
|
+
iconPreset?: 'currency' | 'quantity' | 'percentage' | 'weight' | 'temperature';
|
|
239
257
|
/**
|
|
240
258
|
* Currently selected unit. When `units` is provided, this doubles as
|
|
241
259
|
* the controlled value of the unit dropdown.
|
|
@@ -252,6 +270,16 @@ export type NumberInputProps = {
|
|
|
252
270
|
dropdownIcon?: Component;
|
|
253
271
|
/** Fires when the user picks a different unit. */
|
|
254
272
|
onunitchange?: (prevUnit: string, newUnit: string) => void;
|
|
273
|
+
/**
|
|
274
|
+
* Format large numbers with thousands separators for readability
|
|
275
|
+
* (e.g. `1,232,312`). Formatting is applied on blur; while focused,
|
|
276
|
+
* the raw number is shown so typing stays predictable. @default true
|
|
277
|
+
*/
|
|
278
|
+
formatThousands?: boolean;
|
|
279
|
+
/**
|
|
280
|
+
* BCP 47 locale used for thousands-separator formatting. @default 'en-US'
|
|
281
|
+
*/
|
|
282
|
+
locale?: string;
|
|
255
283
|
testId?: string;
|
|
256
284
|
};
|
|
257
285
|
/**
|
|
@@ -0,0 +1,306 @@
|
|
|
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
|
+
const d = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
122
|
+
if (minDate && d < new Date(minDate.getFullYear(), minDate.getMonth(), 1)) return;
|
|
123
|
+
if (maxDate && d > new Date(maxDate.getFullYear(), maxDate.getMonth(), 1)) return;
|
|
124
|
+
value = d;
|
|
125
|
+
onselect?.(value);
|
|
126
|
+
open = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Auto-scroll to the selected (or current) month when the panel opens.
|
|
130
|
+
$effect(() => {
|
|
131
|
+
if (!open || !listEl) return;
|
|
132
|
+
requestAnimationFrame(() => {
|
|
133
|
+
const target =
|
|
134
|
+
listEl?.querySelector('[data-selected="true"]') ??
|
|
135
|
+
listEl?.querySelector('[data-current="true"]');
|
|
136
|
+
target?.scrollIntoView({ block: 'center' });
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<div class={cn('w-full', className)}>
|
|
142
|
+
{#if label}
|
|
143
|
+
<label for={id} class="text-default-700 mb-1 block text-sm font-medium">
|
|
144
|
+
{label}
|
|
145
|
+
</label>
|
|
146
|
+
{/if}
|
|
147
|
+
|
|
148
|
+
<input
|
|
149
|
+
type="hidden"
|
|
150
|
+
{name}
|
|
151
|
+
value={value ? `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}` : ''}
|
|
152
|
+
/>
|
|
153
|
+
|
|
154
|
+
<Popover trigger="manual" bind:open placement="bottom" {disabled}>
|
|
155
|
+
<button
|
|
156
|
+
{id}
|
|
157
|
+
type="button"
|
|
158
|
+
{disabled}
|
|
159
|
+
onclick={() => {
|
|
160
|
+
if (!disabled) open = !open;
|
|
161
|
+
}}
|
|
162
|
+
aria-haspopup="dialog"
|
|
163
|
+
aria-expanded={open}
|
|
164
|
+
class={cn(
|
|
165
|
+
'flex w-full items-center border bg-white transition-colors',
|
|
166
|
+
'focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',
|
|
167
|
+
tokens.height,
|
|
168
|
+
tokens.padX,
|
|
169
|
+
tokens.text,
|
|
170
|
+
tokens.gap,
|
|
171
|
+
tokens.radius,
|
|
172
|
+
tokens.shadow,
|
|
173
|
+
hasErrors
|
|
174
|
+
? 'border-danger-300 focus-visible:border-danger-500 focus-visible:ring-danger-500'
|
|
175
|
+
: 'border-default-300 focus-visible:border-primary-500 focus-visible:ring-primary-500',
|
|
176
|
+
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
|
|
177
|
+
)}
|
|
178
|
+
data-testid={buildTestId('month-picker', undefined, testId)}
|
|
179
|
+
>
|
|
180
|
+
<span class={cn('flex-1 text-left', !display && 'text-default-400')}>
|
|
181
|
+
{display || placeholder}
|
|
182
|
+
</span>
|
|
183
|
+
{#if clearable && value && !disabled}
|
|
184
|
+
<span
|
|
185
|
+
role="button"
|
|
186
|
+
tabindex="-1"
|
|
187
|
+
onclick={clear}
|
|
188
|
+
onkeydown={(e) => {
|
|
189
|
+
if (e.key === 'Enter') clear(e as unknown as MouseEvent);
|
|
190
|
+
}}
|
|
191
|
+
aria-label="Clear month"
|
|
192
|
+
class={cn(
|
|
193
|
+
'text-default-400 hover:text-default-700 flex cursor-pointer items-center justify-center rounded',
|
|
194
|
+
tokens.iconSize
|
|
195
|
+
)}
|
|
196
|
+
>
|
|
197
|
+
<svg class="size-3" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
198
|
+
<path
|
|
199
|
+
d="M3 3l6 6M9 3l-6 6"
|
|
200
|
+
stroke="currentColor"
|
|
201
|
+
stroke-width="1.5"
|
|
202
|
+
stroke-linecap="round"
|
|
203
|
+
/>
|
|
204
|
+
</svg>
|
|
205
|
+
</span>
|
|
206
|
+
{/if}
|
|
207
|
+
<svg
|
|
208
|
+
class={cn('text-default-400', tokens.iconSize)}
|
|
209
|
+
viewBox="0 0 20 20"
|
|
210
|
+
fill="currentColor"
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
>
|
|
213
|
+
<path
|
|
214
|
+
fill-rule="evenodd"
|
|
215
|
+
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"
|
|
216
|
+
clip-rule="evenodd"
|
|
217
|
+
/>
|
|
218
|
+
</svg>
|
|
219
|
+
</button>
|
|
220
|
+
|
|
221
|
+
{#snippet content({ close })}
|
|
222
|
+
{@render monthGrid(close)}
|
|
223
|
+
{/snippet}
|
|
224
|
+
</Popover>
|
|
225
|
+
|
|
226
|
+
{#if hasErrors}
|
|
227
|
+
<ul class="mt-1 space-y-0.5" role="alert">
|
|
228
|
+
{#each errors as error (error)}
|
|
229
|
+
<li class="text-danger-600 text-xs">{error}</li>
|
|
230
|
+
{/each}
|
|
231
|
+
</ul>
|
|
232
|
+
{/if}
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
{#snippet monthGrid(close: () => void)}
|
|
236
|
+
<div class="flex w-full flex-col sm:w-56">
|
|
237
|
+
<div
|
|
238
|
+
bind:this={listEl}
|
|
239
|
+
class="max-h-72 overflow-y-auto [scrollbar-width:none] sm:max-h-72 [&::-webkit-scrollbar]:hidden"
|
|
240
|
+
>
|
|
241
|
+
{#each years as year (year)}
|
|
242
|
+
{@const yearSelected = value?.getFullYear() === year}
|
|
243
|
+
<div
|
|
244
|
+
class={cn(
|
|
245
|
+
'border-default-200 sticky top-0 z-10 border-b px-3 py-1.5 text-xs font-bold',
|
|
246
|
+
yearSelected ? 'bg-primary-50 text-primary-700' : 'bg-default-50 text-default-800'
|
|
247
|
+
)}
|
|
248
|
+
data-year={year}
|
|
249
|
+
>
|
|
250
|
+
{year}
|
|
251
|
+
</div>
|
|
252
|
+
<div class="grid grid-cols-4 gap-0.5 p-1.5">
|
|
253
|
+
{#each MONTH_SHORT as monthLabel, m (m)}
|
|
254
|
+
{@const entry = monthMap.get(year * 12 + m)}
|
|
255
|
+
{@const sel = entry ? isSelected(entry) : false}
|
|
256
|
+
{@const cur = entry ? isCurrent(entry) : false}
|
|
257
|
+
{@const dis = entry?.disabled ?? true}
|
|
258
|
+
<button
|
|
259
|
+
type="button"
|
|
260
|
+
data-month={m}
|
|
261
|
+
data-selected={sel || undefined}
|
|
262
|
+
data-current={cur || undefined}
|
|
263
|
+
onclick={() => entry && pick(entry)}
|
|
264
|
+
disabled={dis}
|
|
265
|
+
class={cn(
|
|
266
|
+
'cursor-pointer rounded px-1 py-2 text-xs font-medium transition-colors sm:py-1',
|
|
267
|
+
sel
|
|
268
|
+
? 'bg-primary-500 text-white'
|
|
269
|
+
: cur
|
|
270
|
+
? 'text-primary-600 bg-primary-50 ring-primary-300 ring-1'
|
|
271
|
+
: 'text-default-600 hover:bg-default-100',
|
|
272
|
+
dis && 'text-default-300 cursor-not-allowed'
|
|
273
|
+
)}
|
|
274
|
+
>
|
|
275
|
+
{monthLabel}
|
|
276
|
+
</button>
|
|
277
|
+
{/each}
|
|
278
|
+
</div>
|
|
279
|
+
{/each}
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div class="border-default-200 flex items-center justify-between border-t px-3 py-2">
|
|
283
|
+
{#if clearable}
|
|
284
|
+
<button
|
|
285
|
+
type="button"
|
|
286
|
+
onclick={(e) => {
|
|
287
|
+
clear(e);
|
|
288
|
+
close();
|
|
289
|
+
}}
|
|
290
|
+
class="text-primary-600 cursor-pointer text-xs font-medium hover:underline"
|
|
291
|
+
>
|
|
292
|
+
Clear
|
|
293
|
+
</button>
|
|
294
|
+
{:else}
|
|
295
|
+
<span></span>
|
|
296
|
+
{/if}
|
|
297
|
+
<button
|
|
298
|
+
type="button"
|
|
299
|
+
onclick={setThisMonth}
|
|
300
|
+
class="text-primary-600 cursor-pointer text-xs font-medium hover:underline"
|
|
301
|
+
>
|
|
302
|
+
This month
|
|
303
|
+
</button>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
{/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;
|
|
@@ -3,9 +3,11 @@ import { cn } from '../helper/cls.js';
|
|
|
3
3
|
import { Color } from '../variants.js';
|
|
4
4
|
import { formSizeTokens } from './form-size.js';
|
|
5
5
|
export const segmentedTrack = tv({
|
|
6
|
-
// `
|
|
7
|
-
//
|
|
8
|
-
|
|
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',
|
|
9
11
|
variants: {
|
|
10
12
|
appearance: {
|
|
11
13
|
surface: 'border-default-200 bg-white p-0',
|
|
@@ -48,14 +50,14 @@ function segmentSize(size) {
|
|
|
48
50
|
export function segmentClasses(args) {
|
|
49
51
|
const { selected, disabled, appearance, color, size } = args;
|
|
50
52
|
if (appearance === 'pills') {
|
|
51
|
-
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');
|
|
52
54
|
if (disabled)
|
|
53
55
|
return cn(pillBase, 'text-default-400');
|
|
54
56
|
if (selected)
|
|
55
57
|
return cn(pillBase, selectedByColor[color]);
|
|
56
58
|
return cn(pillBase, 'bg-default-100 text-default-700 hover:bg-default-200');
|
|
57
59
|
}
|
|
58
|
-
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',
|
|
59
61
|
// Inverted track has p-0.5 padding around segments — give them inner radius
|
|
60
62
|
// so selected segments don't render as square boxes (matches surface look).
|
|
61
63
|
appearance === 'inverted' && 'rounded-md', segmentSize(size), disabled && 'cursor-not-allowed opacity-50');
|
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}
|
package/dist/modal/modal.js
CHANGED
|
@@ -2,11 +2,11 @@ import { tv } from 'tailwind-variants';
|
|
|
2
2
|
import { Size } from '../index.js';
|
|
3
3
|
export const modal = tv({
|
|
4
4
|
slots: {
|
|
5
|
-
base: 'fixed inset-0 z-50 flex items-center justify-center p-4',
|
|
5
|
+
base: 'fixed inset-0 z-50 flex items-center justify-center p-2 sm:p-4',
|
|
6
6
|
backdrop: 'fixed inset-0 bg-black/50 backdrop-blur-sm',
|
|
7
|
-
container: 'relative w-full flex flex-col bg-white rounded-xl shadow-2xl',
|
|
8
|
-
header: 'px-6 py-4 border-b border-default-200 flex items-center justify-between shrink-0',
|
|
9
|
-
body: 'flex-1 px-6 py-4 overflow-y-auto',
|
|
7
|
+
container: 'relative w-full flex flex-col bg-white rounded-xl shadow-2xl max-sm:max-w-none max-sm:max-h-[95vh]',
|
|
8
|
+
header: 'px-4 py-3 sm:px-6 sm:py-4 border-b border-default-200 flex items-center justify-between shrink-0',
|
|
9
|
+
body: 'flex-1 px-4 py-3 sm:px-6 sm:py-4 overflow-y-auto',
|
|
10
10
|
footer: 'bg-default-50/80 rounded-b-xl shrink-0',
|
|
11
11
|
title: 'text-lg font-semibold text-default-900',
|
|
12
12
|
description: 'text-sm text-default-600 mt-1',
|