@nomideusz/svelte-calendar 0.3.1 → 0.4.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.
- package/README.md +568 -544
- package/dist/calendar/Calendar.svelte +21 -1
- package/dist/calendar/Calendar.svelte.d.ts +1 -0
- package/dist/calendar/Toolbar.svelte +5 -2
- package/dist/calendar/Toolbar.svelte.d.ts +2 -0
- package/dist/engine/view-state.svelte.d.ts +1 -0
- package/dist/engine/view-state.svelte.js +3 -0
- package/dist/views/agenda/Agenda.svelte +7 -5
- package/dist/views/agenda/Agenda.svelte.d.ts +1 -0
- package/dist/views/day/DayGrid.svelte +6 -3
- package/dist/views/day/DayGrid.svelte.d.ts +2 -0
- package/dist/views/day/DayTimeline.svelte +4 -3
- package/dist/views/day/DayTimeline.svelte.d.ts +5 -1
- package/dist/views/schedule/WeekSchedule.svelte +4 -0
- package/dist/views/schedule/WeekSchedule.svelte.d.ts +2 -0
- package/dist/views/settings/Settings.svelte +451 -173
- package/dist/views/settings/Settings.svelte.d.ts +22 -6
- package/dist/views/week/WeekGrid.svelte +9 -8
- package/dist/views/week/WeekGrid.svelte.d.ts +1 -0
- package/dist/views/week/WeekHeatmap.svelte +8 -7
- package/dist/views/week/WeekHeatmap.svelte.d.ts +1 -0
- package/dist/widget/CalendarWidget.svelte +0 -2
- package/dist/widget/widget.d.ts +1 -7
- package/dist/widget/widget.js +44 -3
- package/package.json +65 -67
- package/widget/widget.js +260 -260
|
@@ -4,27 +4,40 @@
|
|
|
4
4
|
import { presets } from '../../theme/presets.js';
|
|
5
5
|
|
|
6
6
|
/* ─── Field definition types ─────────────────────── */
|
|
7
|
-
type
|
|
7
|
+
type BaseField = {
|
|
8
8
|
key: string;
|
|
9
9
|
label: string;
|
|
10
|
+
group?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type RangeField = BaseField & {
|
|
10
14
|
type: 'range';
|
|
11
15
|
min: number;
|
|
12
16
|
max: number;
|
|
13
17
|
step: number;
|
|
18
|
+
enabledWhen?: string;
|
|
14
19
|
};
|
|
15
20
|
|
|
16
|
-
type ToggleField = {
|
|
17
|
-
key: string;
|
|
18
|
-
label: string;
|
|
21
|
+
type ToggleField = BaseField & {
|
|
19
22
|
type: 'toggle';
|
|
20
23
|
};
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
type SelectField = BaseField & {
|
|
26
|
+
type: 'select';
|
|
27
|
+
options: { value: string; label: string }[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type SegmentField = BaseField & {
|
|
31
|
+
type: 'segment';
|
|
32
|
+
options: { value: string; label: string }[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type SettingsField = RangeField | ToggleField | SelectField | SegmentField;
|
|
23
36
|
|
|
24
37
|
/* ─── Props ───────────────────────────────────────── */
|
|
25
38
|
interface Props {
|
|
26
39
|
fields: SettingsField[];
|
|
27
|
-
values: Record<string, number | boolean>;
|
|
40
|
+
values: Record<string, string | number | boolean>;
|
|
28
41
|
theme: PresetName;
|
|
29
42
|
}
|
|
30
43
|
|
|
@@ -38,270 +51,535 @@
|
|
|
38
51
|
neutral: 'Neutral',
|
|
39
52
|
bare: 'Bare',
|
|
40
53
|
};
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
parchment: '#b85c2f',
|
|
44
|
-
indigo: '#6366f1',
|
|
45
|
-
neutral: '#94a0b4',
|
|
46
|
-
bare: '#94a0b4',
|
|
47
|
-
};
|
|
54
|
+
const darkThemes = new Set<PresetName>(['midnight']);
|
|
55
|
+
const panelId = 'stg-panel';
|
|
48
56
|
|
|
49
57
|
let open = $state(true);
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
const themeStyle = $derived(presets[theme]);
|
|
60
|
+
const colorScheme = $derived(darkThemes.has(theme) ? 'dark' : 'light');
|
|
61
|
+
|
|
62
|
+
function setVal(key: string, v: string | number | boolean) {
|
|
52
63
|
values = { ...values, [key]: v };
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
function fmt(v: number | boolean): string {
|
|
66
|
+
function fmt(v: string | number | boolean): string {
|
|
67
|
+
if (typeof v === 'string') return v;
|
|
56
68
|
if (typeof v === 'boolean') return v ? 'true' : 'false';
|
|
57
69
|
if (Number.isInteger(v)) return String(v);
|
|
58
70
|
return v.toFixed(2);
|
|
59
71
|
}
|
|
72
|
+
|
|
73
|
+
function controlId(key: string): string {
|
|
74
|
+
return `stg-${key.replace(/[^a-zA-Z0-9_-]/g, '-')}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Split fields into top-level (no group name) and grouped */
|
|
78
|
+
const { topFields, groupedFields } = $derived.by(() => {
|
|
79
|
+
const top: SettingsField[] = [];
|
|
80
|
+
const orderedGroups: { name: string; id: string; fields: SettingsField[] }[] = [];
|
|
81
|
+
const indexByName = new Map<string, number>();
|
|
82
|
+
|
|
83
|
+
for (const field of fields) {
|
|
84
|
+
const groupName = field.group ?? '';
|
|
85
|
+
|
|
86
|
+
if (!groupName) {
|
|
87
|
+
top.push(field);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const existingIndex = indexByName.get(groupName);
|
|
92
|
+
if (existingIndex === undefined) {
|
|
93
|
+
const id = groupName
|
|
94
|
+
.toLowerCase()
|
|
95
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
96
|
+
.replace(/(^-|-$)/g, '');
|
|
97
|
+
indexByName.set(groupName, orderedGroups.length);
|
|
98
|
+
orderedGroups.push({ name: groupName, id: id || 'general', fields: [field] });
|
|
99
|
+
} else {
|
|
100
|
+
orderedGroups[existingIndex].fields.push(field);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { topFields: top, groupedFields: orderedGroups };
|
|
105
|
+
});
|
|
60
106
|
</script>
|
|
61
107
|
|
|
62
|
-
<div class="stg">
|
|
63
|
-
<button
|
|
64
|
-
|
|
108
|
+
<div class="stg" style="{themeStyle}; color-scheme: {colorScheme}">
|
|
109
|
+
<button
|
|
110
|
+
class="stg-hd"
|
|
111
|
+
aria-expanded={open}
|
|
112
|
+
aria-controls={panelId}
|
|
113
|
+
onclick={() => (open = !open)}
|
|
114
|
+
>
|
|
115
|
+
<svg
|
|
116
|
+
class="stg-chevron"
|
|
117
|
+
class:stg-chevron--open={open}
|
|
118
|
+
viewBox="0 0 16 16"
|
|
119
|
+
fill="none"
|
|
120
|
+
stroke="currentColor"
|
|
121
|
+
stroke-width="1.5"
|
|
122
|
+
stroke-linecap="round"
|
|
123
|
+
>
|
|
65
124
|
<path d="M6 4l4 4-4 4" />
|
|
66
125
|
</svg>
|
|
67
126
|
<span>Settings</span>
|
|
68
127
|
</button>
|
|
69
128
|
|
|
70
129
|
{#if open}
|
|
71
|
-
<div class="stg-body" transition:slide={{ duration: 180 }}>
|
|
72
|
-
<!--
|
|
73
|
-
<div class="stg-
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
130
|
+
<div class="stg-body" id={panelId} transition:slide={{ duration: 180 }}>
|
|
131
|
+
<!-- ─── Top bar: theme + ungrouped fields ─── -->
|
|
132
|
+
<div class="stg-bar">
|
|
133
|
+
<div class="stg-bar-item">
|
|
134
|
+
<span class="stg-col-title" id="stg-theme-label">Theme</span>
|
|
135
|
+
<div class="stg-pills" role="radiogroup" aria-labelledby="stg-theme-label">
|
|
136
|
+
{#each themeKeys as k (k)}
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
class="stg-pill"
|
|
140
|
+
class:stg-pill--on={theme === k}
|
|
141
|
+
role="radio"
|
|
142
|
+
aria-checked={theme === k}
|
|
143
|
+
onclick={() => (theme = k)}
|
|
144
|
+
>
|
|
145
|
+
{themeLabels[k]}
|
|
146
|
+
</button>
|
|
147
|
+
{/each}
|
|
148
|
+
</div>
|
|
87
149
|
</div>
|
|
88
|
-
</div>
|
|
89
150
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
{#each topFields as f (f.key)}
|
|
152
|
+
{@const fid = controlId(f.key)}
|
|
153
|
+
{#if f.type === 'segment'}
|
|
154
|
+
<div class="stg-bar-item">
|
|
155
|
+
{#if f.label}
|
|
156
|
+
<span class="stg-col-title" id={`${fid}-label`}>{f.label}</span>
|
|
157
|
+
{/if}
|
|
158
|
+
<div class="stg-pills" role="radiogroup" aria-labelledby={f.label ? `${fid}-label` : undefined} aria-label={f.label ? undefined : f.key}>
|
|
159
|
+
{#each f.options as option (option.value)}
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
class="stg-pill"
|
|
163
|
+
class:stg-pill--on={String(values[f.key]) === option.value}
|
|
164
|
+
role="radio"
|
|
165
|
+
aria-checked={String(values[f.key]) === option.value}
|
|
166
|
+
onclick={() => setVal(f.key, option.value)}
|
|
167
|
+
>
|
|
168
|
+
{option.label}
|
|
169
|
+
</button>
|
|
170
|
+
{/each}
|
|
171
|
+
</div>
|
|
106
172
|
</div>
|
|
107
|
-
{:else if f.type === 'toggle'}
|
|
108
|
-
<button
|
|
109
|
-
class="stg-sw"
|
|
110
|
-
class:stg-sw--on={values[f.key] === true}
|
|
111
|
-
onclick={() => setVal(f.key, !values[f.key])}
|
|
112
|
-
aria-pressed={values[f.key] === true}
|
|
113
|
-
aria-label={f.label}
|
|
114
|
-
>
|
|
115
|
-
<span class="stg-sw-knob"></span>
|
|
116
|
-
</button>
|
|
117
173
|
{/if}
|
|
174
|
+
{/each}
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<!-- ─── Grouped fields in columns ─── -->
|
|
178
|
+
{#if groupedFields.length > 0}
|
|
179
|
+
<div class="stg-grid">
|
|
180
|
+
{#each groupedFields as section (section.id)}
|
|
181
|
+
<section class="stg-col" aria-labelledby={`stg-s-${section.id}`}>
|
|
182
|
+
<h3 class="stg-col-title" id={`stg-s-${section.id}`}>{section.name}</h3>
|
|
183
|
+
|
|
184
|
+
{#each section.fields as f (f.key)}
|
|
185
|
+
{@const fid = controlId(f.key)}
|
|
186
|
+
|
|
187
|
+
{#if f.type === 'toggle'}
|
|
188
|
+
<label class="stg-row stg-row--toggle" for={fid}>
|
|
189
|
+
<span class="stg-lbl">{f.label}</span>
|
|
190
|
+
<input
|
|
191
|
+
id={fid}
|
|
192
|
+
class="stg-sr"
|
|
193
|
+
type="checkbox"
|
|
194
|
+
role="switch"
|
|
195
|
+
checked={values[f.key] === true}
|
|
196
|
+
onchange={(e) => setVal(f.key, (e.target as HTMLInputElement).checked)}
|
|
197
|
+
/>
|
|
198
|
+
<span class="stg-sw" aria-hidden="true">
|
|
199
|
+
<span class="stg-sw-knob"></span>
|
|
200
|
+
</span>
|
|
201
|
+
</label>
|
|
202
|
+
|
|
203
|
+
{:else if f.type === 'range'}
|
|
204
|
+
{@const disabled = !!f.enabledWhen && values[f.enabledWhen] !== true}
|
|
205
|
+
<div class="stg-row stg-row--rng" class:stg-row--disabled={disabled}>
|
|
206
|
+
<div class="stg-rng-hd">
|
|
207
|
+
<label class="stg-lbl" for={fid}>{f.label}</label>
|
|
208
|
+
<span class="stg-num">{fmt(values[f.key])}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<input
|
|
211
|
+
id={fid}
|
|
212
|
+
class="stg-rng"
|
|
213
|
+
type="range"
|
|
214
|
+
min={f.min}
|
|
215
|
+
max={f.max}
|
|
216
|
+
step={f.step}
|
|
217
|
+
value={values[f.key]}
|
|
218
|
+
{disabled}
|
|
219
|
+
oninput={(e) => setVal(f.key, Number((e.target as HTMLInputElement).value))}
|
|
220
|
+
/>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
{:else if f.type === 'select'}
|
|
224
|
+
<div class="stg-row">
|
|
225
|
+
<label class="stg-lbl" for={fid}>{f.label}</label>
|
|
226
|
+
<select
|
|
227
|
+
id={fid}
|
|
228
|
+
class="stg-sel"
|
|
229
|
+
value={String(values[f.key] ?? '')}
|
|
230
|
+
onchange={(e) => setVal(f.key, (e.target as HTMLSelectElement).value)}
|
|
231
|
+
>
|
|
232
|
+
{#each f.options as option (option.value)}
|
|
233
|
+
<option value={option.value}>{option.label}</option>
|
|
234
|
+
{/each}
|
|
235
|
+
</select>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
{:else if f.type === 'segment'}
|
|
239
|
+
<div class="stg-row stg-row--seg">
|
|
240
|
+
{#if f.label}
|
|
241
|
+
<span class="stg-lbl" id={`${fid}-label`}>{f.label}</span>
|
|
242
|
+
{/if}
|
|
243
|
+
<div class="stg-pills" role="radiogroup" aria-labelledby={f.label ? `${fid}-label` : undefined} aria-label={f.label ? undefined : f.key}>
|
|
244
|
+
{#each f.options as option (option.value)}
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
class="stg-pill"
|
|
248
|
+
class:stg-pill--on={String(values[f.key]) === option.value}
|
|
249
|
+
role="radio"
|
|
250
|
+
aria-checked={String(values[f.key]) === option.value}
|
|
251
|
+
onclick={() => setVal(f.key, option.value)}
|
|
252
|
+
>
|
|
253
|
+
{option.label}
|
|
254
|
+
</button>
|
|
255
|
+
{/each}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
{/if}
|
|
259
|
+
{/each}
|
|
260
|
+
</section>
|
|
261
|
+
{/each}
|
|
118
262
|
</div>
|
|
119
|
-
{/
|
|
263
|
+
{/if}
|
|
120
264
|
</div>
|
|
121
265
|
{/if}
|
|
122
266
|
</div>
|
|
123
267
|
|
|
124
268
|
<style>
|
|
269
|
+
/* ─── Root: themed via --dt-* tokens from inline style ─── */
|
|
125
270
|
.stg {
|
|
126
|
-
border: 1px solid rgba(148, 160, 180, 0.
|
|
271
|
+
border: 1px solid var(--dt-border, rgba(148, 160, 180, 0.10));
|
|
127
272
|
border-radius: 10px;
|
|
128
|
-
background:
|
|
273
|
+
background: var(--dt-surface, #10141c);
|
|
129
274
|
overflow: hidden;
|
|
275
|
+
font-family: var(--dt-sans, 'Outfit', system-ui, sans-serif);
|
|
130
276
|
margin-bottom: 20px;
|
|
131
277
|
}
|
|
132
278
|
|
|
133
|
-
/* ───
|
|
134
|
-
.stg-
|
|
279
|
+
/* ─── Collapsible header ─────────────────────────── */
|
|
280
|
+
.stg-hd {
|
|
135
281
|
display: flex;
|
|
136
282
|
align-items: center;
|
|
137
|
-
gap:
|
|
283
|
+
gap: 7px;
|
|
138
284
|
width: 100%;
|
|
139
285
|
padding: 10px 14px;
|
|
140
286
|
background: none;
|
|
141
287
|
border: none;
|
|
142
288
|
cursor: pointer;
|
|
143
|
-
font:
|
|
144
|
-
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
font-size: 12px;
|
|
291
|
+
line-height: 1;
|
|
292
|
+
letter-spacing: 0.06em;
|
|
145
293
|
text-transform: uppercase;
|
|
146
|
-
color: rgba(148, 160, 180, 0.
|
|
294
|
+
color: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
147
295
|
transition: color 120ms;
|
|
148
296
|
}
|
|
149
|
-
.stg-
|
|
150
|
-
color: rgba(
|
|
297
|
+
.stg-hd:hover {
|
|
298
|
+
color: var(--dt-text, rgba(228, 234, 245, 0.88));
|
|
151
299
|
}
|
|
152
300
|
|
|
153
|
-
.stg-
|
|
154
|
-
width:
|
|
155
|
-
height:
|
|
156
|
-
transition: transform 160ms ease;
|
|
301
|
+
.stg-chevron {
|
|
302
|
+
width: 11px;
|
|
303
|
+
height: 11px;
|
|
157
304
|
flex-shrink: 0;
|
|
305
|
+
transition: transform 150ms ease;
|
|
158
306
|
}
|
|
159
|
-
.stg-
|
|
307
|
+
.stg-chevron--open {
|
|
160
308
|
transform: rotate(90deg);
|
|
161
309
|
}
|
|
162
310
|
|
|
163
|
-
/* ─── Body
|
|
311
|
+
/* ─── Body ───────────────────────────────────────── */
|
|
164
312
|
.stg-body {
|
|
165
|
-
padding:
|
|
313
|
+
padding: 0 14px 12px;
|
|
166
314
|
display: flex;
|
|
167
315
|
flex-direction: column;
|
|
168
|
-
gap:
|
|
316
|
+
gap: 6px;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* ─── Top bar: theme + mode pills ────────────────── */
|
|
320
|
+
.stg-bar {
|
|
321
|
+
display: flex;
|
|
322
|
+
flex-wrap: wrap;
|
|
323
|
+
align-items: center;
|
|
324
|
+
gap: 6px 20px;
|
|
325
|
+
padding-bottom: 4px;
|
|
169
326
|
}
|
|
170
327
|
|
|
171
|
-
|
|
328
|
+
.stg-bar-item {
|
|
329
|
+
display: flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
gap: 8px;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ─── Column grid for grouped fields ─────────────── */
|
|
335
|
+
.stg-grid {
|
|
336
|
+
display: grid;
|
|
337
|
+
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
|
338
|
+
gap: 2px 20px;
|
|
339
|
+
border-top: 1px solid color-mix(in srgb, var(--dt-border, rgba(148, 160, 180, 0.10)) 50%, transparent);
|
|
340
|
+
padding-top: 6px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.stg-col {
|
|
344
|
+
padding: 4px 0 2px;
|
|
345
|
+
min-width: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.stg-col-title {
|
|
349
|
+
margin: 0 0 2px;
|
|
350
|
+
font-weight: 600;
|
|
351
|
+
font-size: 11px;
|
|
352
|
+
line-height: 1;
|
|
353
|
+
letter-spacing: 0.08em;
|
|
354
|
+
text-transform: uppercase;
|
|
355
|
+
color: var(--dt-text-3, rgba(148, 160, 180, 0.40));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/* ─── Row (inline: label left, control right) ──── */
|
|
172
359
|
.stg-row {
|
|
173
360
|
display: flex;
|
|
174
361
|
align-items: center;
|
|
175
|
-
|
|
176
|
-
|
|
362
|
+
justify-content: space-between;
|
|
363
|
+
gap: 10px;
|
|
364
|
+
min-height: 26px;
|
|
365
|
+
padding: 2px 0;
|
|
177
366
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
367
|
+
|
|
368
|
+
.stg-row--rng {
|
|
369
|
+
flex-direction: column;
|
|
370
|
+
align-items: stretch;
|
|
371
|
+
justify-content: flex-start;
|
|
372
|
+
gap: 2px;
|
|
373
|
+
min-height: auto;
|
|
374
|
+
padding: 4px 0;
|
|
184
375
|
}
|
|
185
376
|
|
|
186
|
-
|
|
187
|
-
|
|
377
|
+
.stg-row--disabled {
|
|
378
|
+
opacity: 0.35;
|
|
379
|
+
pointer-events: none;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.stg-rng-hd {
|
|
188
383
|
display: flex;
|
|
189
|
-
|
|
190
|
-
|
|
384
|
+
align-items: baseline;
|
|
385
|
+
justify-content: space-between;
|
|
386
|
+
gap: 8px;
|
|
191
387
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
388
|
+
|
|
389
|
+
.stg-row--seg {
|
|
390
|
+
flex-direction: column;
|
|
391
|
+
align-items: flex-start;
|
|
392
|
+
justify-content: flex-start;
|
|
393
|
+
gap: 3px;
|
|
394
|
+
padding: 3px 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.stg-row--toggle {
|
|
200
398
|
cursor: pointer;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
.stg-
|
|
215
|
-
|
|
216
|
-
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/* ─── Label ──────────────────────────────────────── */
|
|
402
|
+
.stg-lbl {
|
|
403
|
+
font-weight: 400;
|
|
404
|
+
font-size: 13px;
|
|
405
|
+
line-height: 1.2;
|
|
406
|
+
color: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
407
|
+
white-space: nowrap;
|
|
408
|
+
flex-shrink: 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* ─── Toggle switch ──────────────────────────────── */
|
|
412
|
+
.stg-sr {
|
|
413
|
+
position: absolute;
|
|
414
|
+
width: 1px;
|
|
415
|
+
height: 1px;
|
|
416
|
+
margin: -1px;
|
|
417
|
+
padding: 0;
|
|
418
|
+
overflow: hidden;
|
|
419
|
+
clip: rect(0, 0, 0, 0);
|
|
420
|
+
border: 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.stg-sw {
|
|
424
|
+
position: relative;
|
|
425
|
+
display: block;
|
|
426
|
+
width: 32px;
|
|
427
|
+
height: 18px;
|
|
428
|
+
border-radius: 9px;
|
|
429
|
+
border: 1px solid var(--dt-border, rgba(148, 160, 180, 0.10));
|
|
430
|
+
background: color-mix(in srgb, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 10%, transparent);
|
|
431
|
+
cursor: pointer;
|
|
432
|
+
flex-shrink: 0;
|
|
433
|
+
transition: background 150ms, border-color 150ms;
|
|
434
|
+
}
|
|
435
|
+
.stg-sr:checked + .stg-sw {
|
|
436
|
+
background: var(--dt-accent-dim, rgba(99, 102, 241, 0.12));
|
|
437
|
+
border-color: color-mix(in srgb, var(--dt-accent, #6366f1) 35%, transparent);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.stg-sw-knob {
|
|
441
|
+
position: absolute;
|
|
442
|
+
top: 2px;
|
|
443
|
+
left: 2px;
|
|
444
|
+
width: 12px;
|
|
445
|
+
height: 12px;
|
|
217
446
|
border-radius: 50%;
|
|
218
|
-
background: var(--
|
|
219
|
-
|
|
447
|
+
background: var(--dt-text-3, rgba(148, 160, 180, 0.40));
|
|
448
|
+
transition: transform 150ms ease, background 150ms;
|
|
220
449
|
}
|
|
221
|
-
.stg-
|
|
222
|
-
|
|
223
|
-
|
|
450
|
+
.stg-sr:checked + .stg-sw .stg-sw-knob {
|
|
451
|
+
transform: translateX(14px);
|
|
452
|
+
background: var(--dt-accent, #6366f1);
|
|
224
453
|
}
|
|
225
454
|
|
|
226
455
|
/* ─── Range slider ───────────────────────────────── */
|
|
227
|
-
.stg-
|
|
228
|
-
|
|
229
|
-
display: flex;
|
|
230
|
-
align-items: center;
|
|
231
|
-
gap: 10px;
|
|
232
|
-
}
|
|
233
|
-
.stg-slider {
|
|
234
|
-
flex: 1;
|
|
456
|
+
.stg-rng {
|
|
457
|
+
width: 100%;
|
|
235
458
|
-webkit-appearance: none;
|
|
236
459
|
appearance: none;
|
|
237
460
|
height: 3px;
|
|
238
461
|
border-radius: 2px;
|
|
239
|
-
background: rgba(148, 160, 180, 0.
|
|
462
|
+
background: color-mix(in srgb, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 30%, transparent);
|
|
240
463
|
outline: none;
|
|
241
464
|
cursor: pointer;
|
|
242
465
|
}
|
|
243
|
-
.stg-
|
|
466
|
+
.stg-rng::-webkit-slider-thumb {
|
|
244
467
|
-webkit-appearance: none;
|
|
245
|
-
width:
|
|
246
|
-
height:
|
|
468
|
+
width: 12px;
|
|
469
|
+
height: 12px;
|
|
247
470
|
border-radius: 50%;
|
|
248
|
-
background: rgba(
|
|
249
|
-
border:
|
|
471
|
+
background: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
472
|
+
border: none;
|
|
250
473
|
cursor: grab;
|
|
251
474
|
transition: background 100ms, transform 100ms;
|
|
252
475
|
}
|
|
253
|
-
.stg-
|
|
254
|
-
background: rgba(
|
|
476
|
+
.stg-rng::-webkit-slider-thumb:hover {
|
|
477
|
+
background: var(--dt-text, rgba(228, 234, 245, 0.88));
|
|
255
478
|
transform: scale(1.15);
|
|
256
479
|
}
|
|
257
|
-
.stg-
|
|
480
|
+
.stg-rng::-webkit-slider-thumb:active {
|
|
258
481
|
cursor: grabbing;
|
|
259
482
|
}
|
|
260
|
-
.stg-
|
|
261
|
-
width:
|
|
262
|
-
height:
|
|
483
|
+
.stg-rng::-moz-range-thumb {
|
|
484
|
+
width: 12px;
|
|
485
|
+
height: 12px;
|
|
263
486
|
border-radius: 50%;
|
|
264
|
-
background: rgba(
|
|
265
|
-
border:
|
|
487
|
+
background: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
488
|
+
border: none;
|
|
266
489
|
cursor: grab;
|
|
267
490
|
}
|
|
268
|
-
|
|
491
|
+
|
|
492
|
+
.stg-rng:disabled {
|
|
493
|
+
cursor: default;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.stg-num {
|
|
269
497
|
flex-shrink: 0;
|
|
270
|
-
min-width:
|
|
498
|
+
min-width: 22px;
|
|
271
499
|
text-align: right;
|
|
272
|
-
font: 500
|
|
273
|
-
color: rgba(220, 225, 235, 0.45);
|
|
500
|
+
font: 500 11px / 1 var(--dt-mono, 'SF Mono', 'Cascadia Code', ui-monospace, monospace);
|
|
274
501
|
font-variant-numeric: tabular-nums;
|
|
502
|
+
color: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
275
503
|
}
|
|
276
504
|
|
|
277
|
-
/* ───
|
|
278
|
-
.stg-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
505
|
+
/* ─── Select dropdown ────────────────────────────── */
|
|
506
|
+
.stg-sel {
|
|
507
|
+
appearance: none;
|
|
508
|
+
font-weight: 400;
|
|
509
|
+
font-size: 13px;
|
|
510
|
+
line-height: 1;
|
|
511
|
+
padding: 5px 24px 5px 8px;
|
|
512
|
+
border-radius: 6px;
|
|
513
|
+
border: 1px solid var(--dt-border, rgba(148, 160, 180, 0.10));
|
|
514
|
+
background: color-mix(in srgb, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 8%, var(--dt-surface, #10141c));
|
|
515
|
+
color: var(--dt-text, rgba(228, 234, 245, 0.88));
|
|
516
|
+
outline: none;
|
|
285
517
|
cursor: pointer;
|
|
286
|
-
|
|
287
|
-
|
|
518
|
+
background-image: linear-gradient(45deg, transparent 50%, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 50%),
|
|
519
|
+
linear-gradient(135deg, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 50%, transparent 50%);
|
|
520
|
+
background-position: calc(100% - 12px) center, calc(100% - 8px) center;
|
|
521
|
+
background-size: 4px 4px, 4px 4px;
|
|
522
|
+
background-repeat: no-repeat;
|
|
523
|
+
transition: border-color 120ms;
|
|
288
524
|
}
|
|
289
|
-
.stg-
|
|
290
|
-
|
|
291
|
-
border-color: rgba(99, 102, 241, 0.35);
|
|
525
|
+
.stg-sel:hover {
|
|
526
|
+
border-color: var(--dt-border-day, rgba(148, 160, 180, 0.14));
|
|
292
527
|
}
|
|
293
|
-
.stg-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
528
|
+
.stg-sel:focus {
|
|
529
|
+
border-color: color-mix(in srgb, var(--dt-accent, #6366f1) 45%, transparent);
|
|
530
|
+
}
|
|
531
|
+
.stg-sel option {
|
|
532
|
+
background: var(--dt-bg, #0b0e14);
|
|
533
|
+
color: var(--dt-text, rgba(228, 234, 245, 0.88));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/* ─── Segment pills ──────────────────────────────── */
|
|
537
|
+
.stg-pills {
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-wrap: wrap;
|
|
540
|
+
gap: 4px;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.stg-pill {
|
|
544
|
+
padding: 4px 9px;
|
|
545
|
+
border-radius: 100px;
|
|
546
|
+
border: 1px solid color-mix(in srgb, var(--dt-border, rgba(148, 160, 180, 0.10)) 60%, transparent);
|
|
547
|
+
background: color-mix(in srgb, var(--dt-text-3, rgba(148, 160, 180, 0.40)) 8%, transparent);
|
|
548
|
+
color: var(--dt-text-2, rgba(148, 160, 180, 0.62));
|
|
549
|
+
font-weight: 400;
|
|
550
|
+
font-size: 12px;
|
|
551
|
+
line-height: 1;
|
|
552
|
+
cursor: pointer;
|
|
553
|
+
transition: border-color 120ms, color 120ms, background 120ms;
|
|
554
|
+
}
|
|
555
|
+
.stg-pill:hover {
|
|
556
|
+
color: var(--dt-text, rgba(228, 234, 245, 0.88));
|
|
557
|
+
border-color: var(--dt-border, rgba(148, 160, 180, 0.10));
|
|
302
558
|
}
|
|
303
|
-
.stg-
|
|
304
|
-
|
|
305
|
-
|
|
559
|
+
.stg-pill--on {
|
|
560
|
+
color: var(--dt-accent, #6366f1);
|
|
561
|
+
border-color: color-mix(in srgb, var(--dt-accent, #6366f1) 35%, transparent);
|
|
562
|
+
background: var(--dt-accent-dim, rgba(99, 102, 241, 0.12));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* ─── Focus rings ────────────────────────────────── */
|
|
566
|
+
.stg-sr:focus-visible + .stg-sw,
|
|
567
|
+
.stg-sel:focus-visible,
|
|
568
|
+
.stg-rng:focus-visible,
|
|
569
|
+
.stg-pill:focus-visible,
|
|
570
|
+
.stg-hd:focus-visible {
|
|
571
|
+
outline: 2px solid color-mix(in srgb, var(--dt-accent, #6366f1) 55%, transparent);
|
|
572
|
+
outline-offset: 2px;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* ─── Responsive: stack columns on small screens ── */
|
|
576
|
+
@media (max-width: 600px) {
|
|
577
|
+
.stg-grid {
|
|
578
|
+
grid-template-columns: 1fr;
|
|
579
|
+
}
|
|
580
|
+
.stg-bar {
|
|
581
|
+
flex-direction: column;
|
|
582
|
+
gap: 6px;
|
|
583
|
+
}
|
|
306
584
|
}
|
|
307
585
|
</style>
|