@mrintel/villain-ui 0.8.1 → 0.9.1
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 +52 -3
- package/dist/components/buttons/Button.svelte +1 -1
- package/dist/components/buttons/FloatingActionButton.svelte +1 -1
- package/dist/components/buttons/IconButton.svelte +2 -2
- package/dist/components/buttons/LinkButton.svelte +1 -1
- package/dist/components/buttons/buttonClasses.js +7 -7
- package/dist/components/cards/Card.svelte +43 -11
- package/dist/components/cards/Card.svelte.d.ts +11 -0
- package/dist/components/cards/Panel.svelte +44 -5
- package/dist/components/cards/Panel.svelte.d.ts +7 -0
- package/dist/components/cards/SectionHeader.svelte +2 -2
- package/dist/components/data/Avatar.svelte +1 -1
- package/dist/components/data/Badge.svelte +3 -3
- package/dist/components/data/CalendarGrid.svelte +6 -4
- package/dist/components/data/CalendarGrid.svelte.d.ts +3 -0
- package/dist/components/data/CodeBlock.svelte +1 -1
- package/dist/components/data/List.svelte +1 -1
- package/dist/components/data/Pagination.svelte +3 -3
- package/dist/components/data/Stat.svelte +3 -5
- package/dist/components/data/Table.svelte +12 -14
- package/dist/components/data/Tag.svelte +1 -1
- package/dist/components/data/WeekHeatmap.svelte +56 -28
- package/dist/components/forms/Checkbox.svelte +1 -1
- package/dist/components/forms/DatePicker.svelte +3 -1
- package/dist/components/forms/DateTimePicker.svelte +3 -1
- package/dist/components/forms/FileUpload.svelte +1 -1
- package/dist/components/forms/FormSection.svelte +21 -0
- package/dist/components/forms/FormSection.svelte.d.ts +14 -0
- package/dist/components/forms/InputGroup.svelte +1 -1
- package/dist/components/forms/RadioGroup.svelte +0 -1
- package/dist/components/forms/RangeSlider.svelte +2 -6
- package/dist/components/forms/SelectMenu.svelte +4 -3
- package/dist/components/forms/Switch.svelte +1 -1
- package/dist/components/forms/TimePicker.svelte +1 -1
- package/dist/components/forms/TimePickerGrid.svelte +0 -3
- package/dist/components/forms/formClasses.d.ts +2 -2
- package/dist/components/forms/formClasses.js +2 -2
- package/dist/components/forms/index.d.ts +1 -0
- package/dist/components/forms/index.js +1 -0
- package/dist/components/navigation/Breadcrumbs.svelte +3 -3
- package/dist/components/navigation/ContextMenu.svelte +2 -2
- package/dist/components/navigation/DropdownMenu.svelte +2 -2
- package/dist/components/navigation/Menu.svelte +2 -2
- package/dist/components/navigation/Navbar.svelte +10 -2
- package/dist/components/navigation/Sidebar.svelte +6 -9
- package/dist/components/navigation/Tabs.svelte +2 -2
- package/dist/components/overlays/Alert.svelte +1 -1
- package/dist/components/overlays/CommandPalette.svelte +2 -2
- package/dist/components/overlays/Drawer.svelte +5 -5
- package/dist/components/overlays/Dropdown.svelte +1 -1
- package/dist/components/overlays/Modal.svelte +2 -2
- package/dist/components/overlays/Popover.svelte +1 -1
- package/dist/components/overlays/ProgressBar.svelte +3 -3
- package/dist/components/overlays/SkeletonLoader.svelte +2 -2
- package/dist/components/overlays/Spinner.svelte +1 -1
- package/dist/components/overlays/Toast.svelte +2 -2
- package/dist/components/overlays/Tooltip.svelte +1 -1
- package/dist/components/typography/Code.svelte +1 -1
- package/dist/components/utilities/Carousel.svelte +4 -4
- package/dist/components/utilities/Collapse.svelte +1 -1
- package/dist/components/utilities/Hero.svelte +3 -2
- package/dist/components/utilities/SystemConsole.svelte +1 -1
- package/dist/components/utilities/SystemInterface.svelte +1 -2
- package/dist/components/wizard/StepForm.svelte +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/theme.css +207 -190
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">import
|
|
1
|
+
<script lang="ts">import Portal from '../utilities/Portal.svelte';
|
|
2
2
|
let { matrix, rowOrder, dayLabels, hourLabel = (h) => String(h), hourLabelEvery = 1, normalize = true, highlight, markNow = false, cellLabel, onCellSelect, selected = null, ariaLabel = 'Weekly traffic heatmap', class: className = '' } = $props();
|
|
3
3
|
const BASE_DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
4
4
|
const hours = Array.from({ length: 24 }, (_, h) => h);
|
|
@@ -20,6 +20,14 @@ function labelFor(cell, dayLabel, isNow) {
|
|
|
20
20
|
return isNow ? `${text} (now)` : text;
|
|
21
21
|
}
|
|
22
22
|
const fill = (intensity) => `color-mix(in srgb, var(--color-accent) ${intensity}%, var(--color-base-2))`;
|
|
23
|
+
// Single branded tooltip, portaled to <body> so it escapes the overflow-x
|
|
24
|
+
// scroll wrapper (a per-cell absolute tooltip would expand/scroll the grid).
|
|
25
|
+
let tip = $state(null);
|
|
26
|
+
function showTip(el, label) {
|
|
27
|
+
const r = el.getBoundingClientRect();
|
|
28
|
+
tip = { label, x: r.left + r.width / 2, y: r.top };
|
|
29
|
+
}
|
|
30
|
+
const hideTip = () => (tip = null);
|
|
23
31
|
</script>
|
|
24
32
|
|
|
25
33
|
{#if matrix.length > 0}
|
|
@@ -46,34 +54,44 @@ const fill = (intensity) => `color-mix(in srgb, var(--color-accent) ${intensity}
|
|
|
46
54
|
{@const isNow = markNow && row.day === nowDay && hour === nowHour}
|
|
47
55
|
{@const isSel = !!selected && selected.day === row.day && selected.hour === hour}
|
|
48
56
|
{@const label = labelFor(cell, row.label, isNow)}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
{#if onCellSelect}
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
class="heatmap-cell"
|
|
61
|
+
class:is-highlight={hl}
|
|
62
|
+
class:is-ring={isNow || isSel}
|
|
63
|
+
style="background-color: {fill(cell.intensity)}"
|
|
64
|
+
aria-label={label}
|
|
65
|
+
aria-pressed={isSel}
|
|
66
|
+
onclick={() => onCellSelect(cell)}
|
|
67
|
+
onmouseenter={(e) => showTip(e.currentTarget, label)}
|
|
68
|
+
onmouseleave={hideTip}
|
|
69
|
+
onfocus={(e) => showTip(e.currentTarget, label)}
|
|
70
|
+
onblur={hideTip}
|
|
71
|
+
></button>
|
|
72
|
+
{:else}
|
|
73
|
+
<div
|
|
74
|
+
class="heatmap-cell"
|
|
75
|
+
class:is-highlight={hl}
|
|
76
|
+
class:is-ring={isNow || isSel}
|
|
77
|
+
style="background-color: {fill(cell.intensity)}"
|
|
78
|
+
aria-hidden="true"
|
|
79
|
+
onmouseenter={(e) => showTip(e.currentTarget, label)}
|
|
80
|
+
onmouseleave={hideTip}
|
|
81
|
+
></div>
|
|
82
|
+
{/if}
|
|
73
83
|
{/each}
|
|
74
84
|
{/each}
|
|
75
85
|
</div>
|
|
76
86
|
</div>
|
|
87
|
+
|
|
88
|
+
{#if tip}
|
|
89
|
+
<Portal>
|
|
90
|
+
<div class="heatmap-tip panel-floating" style="left: {tip.x}px; top: {tip.y}px;">
|
|
91
|
+
{tip.label}
|
|
92
|
+
</div>
|
|
93
|
+
</Portal>
|
|
94
|
+
{/if}
|
|
77
95
|
{/if}
|
|
78
96
|
|
|
79
97
|
<style>
|
|
@@ -102,15 +120,25 @@ const fill = (intensity) => `color-mix(in srgb, var(--color-accent) ${intensity}
|
|
|
102
120
|
}
|
|
103
121
|
|
|
104
122
|
.heatmap-cell {
|
|
105
|
-
display: block;
|
|
106
|
-
width: 100%;
|
|
107
123
|
height: var(--heatmap-cell-size, 1.5rem);
|
|
108
124
|
min-width: var(--heatmap-cell-size, 1.5rem);
|
|
109
|
-
border-radius:
|
|
125
|
+
border-radius: 0;
|
|
110
126
|
border: 0;
|
|
111
127
|
padding: 0;
|
|
112
128
|
}
|
|
113
129
|
|
|
130
|
+
.heatmap-tip {
|
|
131
|
+
position: fixed;
|
|
132
|
+
transform: translate(-50%, calc(-100% - 8px));
|
|
133
|
+
z-index: var(--z-50, 50);
|
|
134
|
+
pointer-events: none;
|
|
135
|
+
white-space: nowrap;
|
|
136
|
+
padding: 0.4rem 0.75rem;
|
|
137
|
+
border-radius: var(--radius-sm);
|
|
138
|
+
font-size: 0.8rem;
|
|
139
|
+
color: var(--color-text);
|
|
140
|
+
}
|
|
141
|
+
|
|
114
142
|
button.heatmap-cell {
|
|
115
143
|
cursor: pointer;
|
|
116
144
|
}
|
|
@@ -12,7 +12,7 @@ const hasError = $derived(error || !!validationMessage);
|
|
|
12
12
|
{disabled}
|
|
13
13
|
bind:checked
|
|
14
14
|
onchange={onchange}
|
|
15
|
-
class="w-6 h-6 rounded-sm border-2 bg-transparent appearance-none transition-all duration-200 ease-luxe cursor-pointer checked:bg-accent checked:border-accent
|
|
15
|
+
class="w-6 h-6 rounded-sm border-2 bg-transparent appearance-none transition-all duration-200 ease-luxe cursor-pointer checked:bg-accent checked:border-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {hasError ? 'border-error' : 'border-[var(--color-border-bright)]'} {disabled ? 'cursor-not-allowed' : ''}"
|
|
16
16
|
/>
|
|
17
17
|
{#if iconBefore}
|
|
18
18
|
<span class="inline-flex items-center justify-center text-text-soft">
|
|
@@ -246,7 +246,7 @@ const errorClasses = $derived(hasError ? 'error-state' : '');
|
|
|
246
246
|
role="dialog"
|
|
247
247
|
aria-modal="true"
|
|
248
248
|
aria-label="Choose date"
|
|
249
|
-
class="calendar-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-
|
|
249
|
+
class="calendar-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-[var(--radius-sm)] overflow-hidden"
|
|
250
250
|
onclick={(e) => e.stopPropagation()}
|
|
251
251
|
>
|
|
252
252
|
<CalendarGrid
|
|
@@ -254,6 +254,8 @@ const errorClasses = $derived(hasError ? 'error-state' : '');
|
|
|
254
254
|
selectedDate={selectedDate}
|
|
255
255
|
onDateSelect={handleDateSelect}
|
|
256
256
|
highlightToday={true}
|
|
257
|
+
minDate={minDate}
|
|
258
|
+
maxDate={maxDate}
|
|
257
259
|
class="calendar-picker"
|
|
258
260
|
/>
|
|
259
261
|
</div>
|
|
@@ -256,7 +256,7 @@ const errorClasses = $derived(hasError ? 'error-state' : '');
|
|
|
256
256
|
role="dialog"
|
|
257
257
|
aria-modal="true"
|
|
258
258
|
aria-label="Choose date and time"
|
|
259
|
-
class="datetime-picker-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-
|
|
259
|
+
class="datetime-picker-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-[var(--radius-sm)] overflow-hidden"
|
|
260
260
|
onclick={(e) => e.stopPropagation()}
|
|
261
261
|
>
|
|
262
262
|
<!-- Tab navigation -->
|
|
@@ -308,6 +308,8 @@ const errorClasses = $derived(hasError ? 'error-state' : '');
|
|
|
308
308
|
selectedDate={selectedDate}
|
|
309
309
|
onDateSelect={handleDateSelect}
|
|
310
310
|
highlightToday={true}
|
|
311
|
+
minDate={minDateStr ? new Date(minDateStr + 'T00:00:00') : undefined}
|
|
312
|
+
maxDate={maxDateStr ? new Date(maxDateStr + 'T00:00:00') : undefined}
|
|
311
313
|
class="calendar-picker"
|
|
312
314
|
/>
|
|
313
315
|
{:else}
|
|
@@ -67,7 +67,7 @@ const fileList = $derived(files ? Array.from(files) : []);
|
|
|
67
67
|
ondrop={handleDrop}
|
|
68
68
|
onclick={handleClick}
|
|
69
69
|
onkeydown={(e) => e.key === 'Enter' && handleClick()}
|
|
70
|
-
class="panel-raised rounded-[var(--radius-
|
|
70
|
+
class="panel-raised rounded-[var(--radius-sm)] p-8 text-center cursor-pointer transition-all duration-300 ease-luxe {isDragging ? 'border-2 border-accent accent-glow bg-base-2' : ''} {hasError && !isDragging ? 'border-2 border-error' : ''} {disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : ''}"
|
|
71
71
|
>
|
|
72
72
|
<div class="flex flex-col items-center gap-2">
|
|
73
73
|
{#if icon}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">let { legend, description, columns = 1, class: className = '', children, ...restProps } = $props();
|
|
2
|
+
export {};
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<fieldset class="border-0 p-0 m-0 min-w-0 {className}" {...restProps}>
|
|
6
|
+
{#if legend}
|
|
7
|
+
<legend
|
|
8
|
+
class="font-[var(--font-mono)] text-xs uppercase tracking-[0.08em] text-[var(--color-text-soft)] p-0 mb-2 w-full border-b border-[var(--color-border-subtle)] pb-3"
|
|
9
|
+
>
|
|
10
|
+
{legend}
|
|
11
|
+
</legend>
|
|
12
|
+
{/if}
|
|
13
|
+
{#if description}
|
|
14
|
+
<p class="text-sm text-[var(--color-text-soft)] mt-3 mb-6">{description}</p>
|
|
15
|
+
{:else if legend}
|
|
16
|
+
<div class="mt-6"></div>
|
|
17
|
+
{/if}
|
|
18
|
+
<div class="grid gap-5 {columns === 2 ? 'md:grid-cols-2' : ''}">
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
21
|
+
</fieldset>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Mono uppercase section label (e.g. "01 / IDENTITY"). */
|
|
4
|
+
legend?: string;
|
|
5
|
+
/** Supporting copy under the legend. */
|
|
6
|
+
description?: string;
|
|
7
|
+
/** Field layout: single column (default) or two columns on md+. */
|
|
8
|
+
columns?: 1 | 2;
|
|
9
|
+
class?: string;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const FormSection: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type FormSection = ReturnType<typeof FormSection>;
|
|
14
|
+
export default FormSection;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
// No props needed for standard slot usage
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
|
-
<div class="flex items-stretch rounded-[var(--radius-
|
|
5
|
+
<div class="flex items-stretch rounded-[var(--radius-sm)] overflow-hidden panel-raised [&>*:not(:first-child)]:ml-[-1px] [&>*:not(:first-child):not(:last-child)]:rounded-none [&>*:first-child]:rounded-r-none [&>*:last-child]:rounded-l-none">
|
|
6
6
|
<slot />
|
|
7
7
|
</div>
|
|
@@ -47,7 +47,6 @@ const percentage = $derived(max === min ? 0 : ((value - min) / (max - min)) * 10
|
|
|
47
47
|
height: 1.25rem;
|
|
48
48
|
border-radius: var(--radius-pill);
|
|
49
49
|
background: var(--color-accent);
|
|
50
|
-
box-shadow: var(--shadow-accent-glow);
|
|
51
50
|
cursor: pointer;
|
|
52
51
|
transition: transform 0.2s var(--ease-luxe);
|
|
53
52
|
}
|
|
@@ -62,7 +61,6 @@ const percentage = $derived(max === min ? 0 : ((value - min) / (max - min)) * 10
|
|
|
62
61
|
height: 1.25rem;
|
|
63
62
|
border-radius: var(--radius-pill);
|
|
64
63
|
background: var(--color-accent);
|
|
65
|
-
box-shadow: var(--shadow-accent-glow);
|
|
66
64
|
cursor: pointer;
|
|
67
65
|
transition: transform 0.2s var(--ease-luxe);
|
|
68
66
|
border: none;
|
|
@@ -73,15 +71,13 @@ const percentage = $derived(max === min ? 0 : ((value - min) / (max - min)) * 10
|
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
input[type="range"]:focus::-webkit-slider-thumb {
|
|
76
|
-
box-shadow:
|
|
77
|
-
var(--shadow-accent-glow),
|
|
74
|
+
box-shadow:
|
|
78
75
|
0 0 0 3px var(--color-base-1),
|
|
79
76
|
0 0 0 5px var(--color-accent);
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
input[type="range"]:focus::-moz-range-thumb {
|
|
83
|
-
box-shadow:
|
|
84
|
-
var(--shadow-accent-glow),
|
|
80
|
+
box-shadow:
|
|
85
81
|
0 0 0 3px var(--color-base-1),
|
|
86
82
|
0 0 0 5px var(--color-accent);
|
|
87
83
|
}
|
|
@@ -86,16 +86,17 @@ function handleListboxKeyDown(event) {
|
|
|
86
86
|
break;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
function scrollOptionIntoView(index) {
|
|
89
|
+
function scrollOptionIntoView(index, block = 'nearest') {
|
|
90
90
|
requestAnimationFrame(() => {
|
|
91
91
|
const optionEl = listboxElement?.querySelector(`[data-index="${index}"]`);
|
|
92
|
-
optionEl?.scrollIntoView({ block
|
|
92
|
+
optionEl?.scrollIntoView({ block });
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
|
-
// Set highlighted index and focus listbox when dropdown opens
|
|
95
|
+
// Set highlighted index, scroll it into view, and focus listbox when dropdown opens
|
|
96
96
|
$effect(() => {
|
|
97
97
|
if (open) {
|
|
98
98
|
highlightedIndex = selectedIndex >= 0 ? selectedIndex : 0;
|
|
99
|
+
scrollOptionIntoView(highlightedIndex, 'center');
|
|
99
100
|
if (listboxElement) {
|
|
100
101
|
requestAnimationFrame(() => listboxElement?.focus());
|
|
101
102
|
}
|
|
@@ -14,7 +14,7 @@ const hasError = $derived(error || !!validationMessage);
|
|
|
14
14
|
{disabled}
|
|
15
15
|
bind:checked
|
|
16
16
|
onchange={onchange}
|
|
17
|
-
class="w-14 h-7 rounded-pill bg-base-3 border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent
|
|
17
|
+
class="w-14 h-7 rounded-pill bg-base-3 border appearance-none transition-all duration-300 ease-luxe cursor-pointer checked:bg-accent focus:outline-none focus:ring-2 focus:ring-accent focus:ring-offset-2 focus:ring-offset-base-1 relative {hasError ? 'border-error' : 'border-border'} {disabled ? 'cursor-not-allowed' : ''}"
|
|
18
18
|
/>
|
|
19
19
|
{#if iconBefore}
|
|
20
20
|
<span class="inline-flex items-center justify-center text-text-soft">
|
|
@@ -169,7 +169,7 @@ const errorClasses = $derived(hasError ? 'error-state' : '');
|
|
|
169
169
|
role="dialog"
|
|
170
170
|
aria-modal="true"
|
|
171
171
|
aria-label="Choose time"
|
|
172
|
-
class="time-picker-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-
|
|
172
|
+
class="time-picker-dropdown {variant === 'glass' ? 'glass-panel' : 'panel-floating'} rounded-[var(--radius-sm)] overflow-hidden"
|
|
173
173
|
>
|
|
174
174
|
<TimePickerGrid
|
|
175
175
|
bind:hours={selectedHours}
|
|
@@ -445,7 +445,6 @@ $effect(() => {
|
|
|
445
445
|
border-color: var(--color-accent);
|
|
446
446
|
color: var(--color-base-0);
|
|
447
447
|
font-weight: 700;
|
|
448
|
-
box-shadow: 0 0 12px var(--color-accent-glow);
|
|
449
448
|
}
|
|
450
449
|
|
|
451
450
|
.time-option.disabled,
|
|
@@ -482,8 +481,6 @@ $effect(() => {
|
|
|
482
481
|
|
|
483
482
|
.confirm-button:hover:not(:disabled) {
|
|
484
483
|
background: var(--color-accent-hover);
|
|
485
|
-
box-shadow: 0 0 20px var(--color-accent-glow);
|
|
486
|
-
transform: translateY(-1px);
|
|
487
484
|
}
|
|
488
485
|
|
|
489
486
|
.confirm-button:active:not(:disabled) {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const baseInputClasses = "
|
|
2
|
-
export declare const focusClasses = "focus:outline-none focus:border-accent
|
|
1
|
+
export declare const baseInputClasses = "input-well rounded-[var(--radius-sm)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-150 ease-luxe w-full";
|
|
2
|
+
export declare const focusClasses = "focus:outline-none focus:border-[var(--color-accent)]";
|
|
3
3
|
export declare const disabledClasses = "opacity-50 cursor-not-allowed";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const baseInputClasses = '
|
|
2
|
-
export const focusClasses = 'focus:outline-none focus:border-accent
|
|
1
|
+
export const baseInputClasses = 'input-well rounded-[var(--radius-sm)] px-5 py-3.5 font-body text-text placeholder:text-text-muted transition-all duration-150 ease-luxe w-full';
|
|
2
|
+
export const focusClasses = 'focus:outline-none focus:border-[var(--color-accent)]';
|
|
3
3
|
export const disabledClasses = 'opacity-50 cursor-not-allowed';
|
|
@@ -11,3 +11,4 @@ export { default as DatePicker } from './DatePicker.svelte';
|
|
|
11
11
|
export { default as TimePicker } from './TimePicker.svelte';
|
|
12
12
|
export { default as DateTimePicker } from './DateTimePicker.svelte';
|
|
13
13
|
export { default as SelectMenu } from './SelectMenu.svelte';
|
|
14
|
+
export { default as FormSection } from './FormSection.svelte';
|
|
@@ -11,3 +11,4 @@ export { default as DatePicker } from './DatePicker.svelte';
|
|
|
11
11
|
export { default as TimePicker } from './TimePicker.svelte';
|
|
12
12
|
export { default as DateTimePicker } from './DateTimePicker.svelte';
|
|
13
13
|
export { default as SelectMenu } from './SelectMenu.svelte';
|
|
14
|
+
export { default as FormSection } from './FormSection.svelte';
|
|
@@ -9,7 +9,7 @@ export {};
|
|
|
9
9
|
{#if item.href}
|
|
10
10
|
<a
|
|
11
11
|
href={item.href}
|
|
12
|
-
class="flex items-center gap-2 text-
|
|
12
|
+
class="flex items-center gap-2 text-xs font-[var(--font-mono)] uppercase tracking-[0.08em] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)]"
|
|
13
13
|
>
|
|
14
14
|
{#if item.icon}
|
|
15
15
|
<span class="inline-flex items-center justify-center">
|
|
@@ -21,7 +21,7 @@ export {};
|
|
|
21
21
|
{:else if item.onclick}
|
|
22
22
|
<button
|
|
23
23
|
onclick={item.onclick}
|
|
24
|
-
class="flex items-center gap-2 text-
|
|
24
|
+
class="flex items-center gap-2 text-xs font-[var(--font-mono)] uppercase tracking-[0.08em] text-[var(--color-text-soft)] hover:text-[var(--color-accent)] hover:underline transition-colors duration-200 ease-[var(--ease-luxe)] cursor-pointer"
|
|
25
25
|
>
|
|
26
26
|
{#if item.icon}
|
|
27
27
|
<span class="inline-flex items-center justify-center">
|
|
@@ -31,7 +31,7 @@ export {};
|
|
|
31
31
|
{item.label}
|
|
32
32
|
</button>
|
|
33
33
|
{:else}
|
|
34
|
-
<span class="flex items-center gap-2 text-
|
|
34
|
+
<span class="flex items-center gap-2 text-xs font-[var(--font-mono)] uppercase tracking-[0.08em] {index === items.length - 1 ? 'text-[var(--color-text)]' : 'text-[var(--color-text-soft)]'}">
|
|
35
35
|
{#if item.icon}
|
|
36
36
|
<span class="inline-flex items-center justify-center">
|
|
37
37
|
{@render item.icon()}
|
|
@@ -111,7 +111,7 @@ $effect(() => {
|
|
|
111
111
|
role="menu"
|
|
112
112
|
tabindex="-1"
|
|
113
113
|
onkeydown={handleMenuKeyDown}
|
|
114
|
-
class="fixed z-[var(--z-50)] glass-panel rounded-[var(--radius-
|
|
114
|
+
class="fixed z-[var(--z-50)] glass-panel rounded-[var(--radius-sm)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[fade-in_0.15s_var(--ease-luxe)]"
|
|
115
115
|
style="left: {x}px; top: {y}px;"
|
|
116
116
|
>
|
|
117
117
|
{#each items as item, index}
|
|
@@ -121,7 +121,7 @@ $effect(() => {
|
|
|
121
121
|
tabindex={index === selectedIndex ? 0 : -1}
|
|
122
122
|
onclick={() => handleItemClick(item)}
|
|
123
123
|
disabled={item.disabled}
|
|
124
|
-
class="w-full text-left px-4 py-2 text-
|
|
124
|
+
class="w-full text-left px-4 py-2 font-[var(--font-mono)] text-xs uppercase tracking-[0.08em] text-text hover:bg-base-3 transition-colors duration-200 flex items-center gap-2 {item.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}"
|
|
125
125
|
>
|
|
126
126
|
{#if item.icon}
|
|
127
127
|
{@render item.icon()}
|
|
@@ -117,7 +117,7 @@ $effect(() => {
|
|
|
117
117
|
role="menu"
|
|
118
118
|
tabindex="-1"
|
|
119
119
|
onkeydown={handleMenuKeyDown}
|
|
120
|
-
class="absolute {placementClasses[placement]} z-[var(--z-50)] glass-panel rounded-[var(--radius-
|
|
120
|
+
class="absolute {placementClasses[placement]} z-[var(--z-50)] glass-panel rounded-[var(--radius-sm)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[scale-in_0.15s_var(--ease-sharp)]"
|
|
121
121
|
>
|
|
122
122
|
{#each items as item, index}
|
|
123
123
|
<button
|
|
@@ -126,7 +126,7 @@ $effect(() => {
|
|
|
126
126
|
tabindex={index === selectedIndex ? 0 : -1}
|
|
127
127
|
onclick={() => handleItemClick(item)}
|
|
128
128
|
disabled={item.disabled}
|
|
129
|
-
class="w-full text-left px-4 py-2 text-
|
|
129
|
+
class="w-full text-left px-4 py-2 font-[var(--font-mono)] text-xs uppercase tracking-[0.08em] text-text hover:bg-base-3 transition-colors duration-200 flex items-center gap-2 {item.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}"
|
|
130
130
|
>
|
|
131
131
|
{#if item.icon}
|
|
132
132
|
{@render item.icon()}
|
|
@@ -49,7 +49,7 @@ $effect(() => {
|
|
|
49
49
|
export {};
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
|
-
<div bind:this={menuContainer} role="menu" tabindex="-1" onkeydown={handleMenuKeyDown} class="glass-panel rounded-[var(--radius-
|
|
52
|
+
<div bind:this={menuContainer} role="menu" tabindex="-1" onkeydown={handleMenuKeyDown} class="glass-panel rounded-[var(--radius-sm)] p-3 shadow-[var(--shadow-deep)]">
|
|
53
53
|
{#if children}
|
|
54
54
|
{@render children()}
|
|
55
55
|
{:else if items}
|
|
@@ -60,7 +60,7 @@ export {};
|
|
|
60
60
|
tabindex={index === selectedIndex ? 0 : -1}
|
|
61
61
|
onclick={() => handleItemClick(item)}
|
|
62
62
|
disabled={item.disabled}
|
|
63
|
-
class="flex items-center gap-2 w-full px-4 py-3 rounded-[var(--radius-
|
|
63
|
+
class="flex items-center gap-2 w-full px-4 py-3 rounded-[var(--radius-sm)] text-[var(--color-text)] text-xs font-[var(--font-mono)] uppercase tracking-[0.08em] hover:bg-[var(--color-base-3)] hover-lift transition-all duration-200 ease-[var(--ease-luxe)] {item.disabled ? 'opacity-50 pointer-events-none' : 'cursor-pointer'}"
|
|
64
64
|
>
|
|
65
65
|
{#if item.icon}
|
|
66
66
|
{@render item.icon()}
|
|
@@ -80,9 +80,18 @@ export {};
|
|
|
80
80
|
</nav>
|
|
81
81
|
|
|
82
82
|
<style>
|
|
83
|
+
/* System text: navigation is mono, uppercase, tracked */
|
|
84
|
+
nav :global(a),
|
|
85
|
+
nav :global(button) {
|
|
86
|
+
font-family: var(--font-mono);
|
|
87
|
+
font-size: var(--text-xs);
|
|
88
|
+
text-transform: uppercase;
|
|
89
|
+
letter-spacing: 0.08em;
|
|
90
|
+
}
|
|
91
|
+
|
|
83
92
|
nav :global(.navbar-logo) {
|
|
84
93
|
color: var(--color-accent);
|
|
85
|
-
font-weight:
|
|
94
|
+
font-weight: 700;
|
|
86
95
|
font-size: var(--text-lg);
|
|
87
96
|
}
|
|
88
97
|
|
|
@@ -102,7 +111,6 @@ export {};
|
|
|
102
111
|
right: 0;
|
|
103
112
|
height: 2px;
|
|
104
113
|
background: var(--color-accent);
|
|
105
|
-
box-shadow: var(--shadow-accent-glow);
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
nav :global(a:not(.active):hover),
|
|
@@ -171,7 +171,7 @@ export {};
|
|
|
171
171
|
justify-content: center;
|
|
172
172
|
width: 2.5rem;
|
|
173
173
|
height: 2.5rem;
|
|
174
|
-
border-radius: var(--radius-
|
|
174
|
+
border-radius: var(--radius-sm);
|
|
175
175
|
background: var(--color-accent-overlay-20);
|
|
176
176
|
color: var(--color-accent);
|
|
177
177
|
font-weight: 600;
|
|
@@ -204,11 +204,6 @@ export {};
|
|
|
204
204
|
font-weight: 600;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
aside :global(a.active),
|
|
208
|
-
aside :global(button.active) {
|
|
209
|
-
box-shadow: var(--shadow-accent-glow);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
207
|
aside :global(a:not(.active):hover),
|
|
213
208
|
aside :global(button:not(.active):hover) {
|
|
214
209
|
background: var(--color-base-3);
|
|
@@ -222,7 +217,7 @@ export {};
|
|
|
222
217
|
gap: 0.75rem;
|
|
223
218
|
padding: 0.75rem 1rem;
|
|
224
219
|
margin: 0.25rem 0.5rem;
|
|
225
|
-
border-radius:
|
|
220
|
+
border-radius: 0;
|
|
226
221
|
color: var(--color-text-soft);
|
|
227
222
|
text-decoration: none;
|
|
228
223
|
transition: all 0.2s var(--ease-luxe);
|
|
@@ -231,6 +226,8 @@ export {};
|
|
|
231
226
|
background: transparent;
|
|
232
227
|
width: calc(100% - 1rem);
|
|
233
228
|
text-align: left;
|
|
234
|
-
font-family: var(--font-
|
|
235
|
-
font-size:
|
|
229
|
+
font-family: var(--font-mono);
|
|
230
|
+
font-size: var(--text-xs);
|
|
231
|
+
text-transform: uppercase;
|
|
232
|
+
letter-spacing: 0.08em;
|
|
236
233
|
}</style>
|
|
@@ -59,7 +59,7 @@ const orientationClasses = {
|
|
|
59
59
|
horizontal: 'flex flex-row',
|
|
60
60
|
vertical: 'flex flex-col'
|
|
61
61
|
};
|
|
62
|
-
const radiusClass = variant === 'pills' ? 'rounded-pill' : 'rounded-
|
|
62
|
+
const radiusClass = variant === 'pills' ? 'rounded-pill' : 'rounded-none';
|
|
63
63
|
export {};
|
|
64
64
|
</script>
|
|
65
65
|
|
|
@@ -73,7 +73,7 @@ export {};
|
|
|
73
73
|
onclick={() => handleTabClick(tab.id, tab.disabled)}
|
|
74
74
|
onkeydown={(e) => handleKeyDown(e, tab.id)}
|
|
75
75
|
disabled={tab.disabled}
|
|
76
|
-
class="flex items-center gap-2 px-6 py-3 {radiusClass} font-
|
|
76
|
+
class="flex items-center gap-2 px-6 py-3 {radiusClass} font-mono text-xs uppercase tracking-[0.08em] transition-all duration-150 ease-luxe {activeTab === tab.id ? 'bg-accent text-text' : 'text-text-soft hover:bg-base-3'} {tab.disabled ? 'opacity-50 pointer-events-none' : 'cursor-pointer'}"
|
|
77
77
|
>
|
|
78
78
|
{#if tab.iconBefore}
|
|
79
79
|
<span class="inline-flex items-center justify-center">
|
|
@@ -40,7 +40,7 @@ export {};
|
|
|
40
40
|
|
|
41
41
|
{#if visible}
|
|
42
42
|
<div
|
|
43
|
-
class="glass-panel rounded-[var(--radius-
|
|
43
|
+
class="glass-panel rounded-[var(--radius-sm)] p-4 flex gap-3 {variantClasses[variant]} {className} animate-[fade-in_0.2s_var(--ease-sharp)]"
|
|
44
44
|
role={roleMap[variant]}
|
|
45
45
|
aria-live={ariaLiveMap[variant]}
|
|
46
46
|
>
|
|
@@ -103,12 +103,12 @@ $effect(() => {
|
|
|
103
103
|
|
|
104
104
|
{#if open}
|
|
105
105
|
<div
|
|
106
|
-
class="fixed inset-0 z-[var(--z-50)] flex items-start justify-center pt-[20vh] p-4 bg-overlay
|
|
106
|
+
class="fixed inset-0 z-[var(--z-50)] flex items-start justify-center pt-[20vh] p-4 bg-overlay animate-[fade-in_0.2s_var(--ease-luxe)]"
|
|
107
107
|
onclick={handleBackdropClick}
|
|
108
108
|
role="presentation"
|
|
109
109
|
>
|
|
110
110
|
<div
|
|
111
|
-
class="panel-floating rounded-
|
|
111
|
+
class="panel-floating rounded-[var(--radius-md)] shadow-deep w-full max-w-[36rem] animate-[scale-in_0.2s_var(--ease-sharp)]"
|
|
112
112
|
role="combobox"
|
|
113
113
|
aria-expanded="true"
|
|
114
114
|
aria-haspopup="listbox"
|
|
@@ -34,10 +34,10 @@ const positionClasses = {
|
|
|
34
34
|
bottom: 'bottom-0 left-0 w-full'
|
|
35
35
|
};
|
|
36
36
|
const animationClasses = {
|
|
37
|
-
left: 'animate-[slide-in-left_0.
|
|
38
|
-
right: 'animate-[slide-in-right_0.
|
|
39
|
-
top: 'animate-[slide-in-top_0.
|
|
40
|
-
bottom: 'animate-[slide-in-bottom_0.
|
|
37
|
+
left: 'animate-[slide-in-left_0.2s_var(--ease-sharp)]',
|
|
38
|
+
right: 'animate-[slide-in-right_0.2s_var(--ease-sharp)]',
|
|
39
|
+
top: 'animate-[slide-in-top_0.2s_var(--ease-sharp)]',
|
|
40
|
+
bottom: 'animate-[slide-in-bottom_0.2s_var(--ease-sharp)]'
|
|
41
41
|
};
|
|
42
42
|
function handleClose() {
|
|
43
43
|
open = false;
|
|
@@ -106,7 +106,7 @@ $effect(() => {
|
|
|
106
106
|
|
|
107
107
|
{#if open}
|
|
108
108
|
<div
|
|
109
|
-
class="fixed inset-0 z-[var(--z-50)] bg-overlay
|
|
109
|
+
class="fixed inset-0 z-[var(--z-50)] bg-overlay animate-[fade-in_0.2s_var(--ease-luxe)]"
|
|
110
110
|
onclick={handleBackdropClick}
|
|
111
111
|
role="presentation"
|
|
112
112
|
>
|
|
@@ -76,7 +76,7 @@ $effect(() => {
|
|
|
76
76
|
<div
|
|
77
77
|
bind:this={dropdownElement}
|
|
78
78
|
id={dropdownId}
|
|
79
|
-
class="absolute {placementClasses[placement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-
|
|
79
|
+
class="absolute {placementClasses[placement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-sm)] shadow-[var(--shadow-deep)] min-w-[12rem] animate-[scale-in_0.15s_var(--ease-sharp)]"
|
|
80
80
|
style={fullWidth && triggerWidth > 0 ? `width: ${triggerWidth}px` : ''}
|
|
81
81
|
role="menu"
|
|
82
82
|
>
|
|
@@ -77,13 +77,13 @@ $effect(() => {
|
|
|
77
77
|
|
|
78
78
|
{#if open}
|
|
79
79
|
<div
|
|
80
|
-
class="fixed inset-0 z-[var(--z-50)] flex items-center justify-center p-4 bg-overlay
|
|
80
|
+
class="fixed inset-0 z-[var(--z-50)] flex items-center justify-center p-4 bg-overlay animate-[fade-in_0.2s_var(--ease-luxe)]"
|
|
81
81
|
onclick={handleBackdropClick}
|
|
82
82
|
role="presentation"
|
|
83
83
|
>
|
|
84
84
|
<div
|
|
85
85
|
bind:this={modalElement}
|
|
86
|
-
class="panel-floating rounded-[var(--radius-
|
|
86
|
+
class="panel-floating rounded-[var(--radius-md)] shadow-deep w-full {sizeClasses[size]} {className} animate-[scale-in_0.2s_var(--ease-sharp)] flex flex-col max-h-[90vh]"
|
|
87
87
|
role="dialog"
|
|
88
88
|
aria-modal="true"
|
|
89
89
|
aria-labelledby={title ? titleId : undefined}
|
|
@@ -97,7 +97,7 @@ $effect(() => {
|
|
|
97
97
|
<div
|
|
98
98
|
bind:this={popoverElement}
|
|
99
99
|
id={popoverId}
|
|
100
|
-
class="absolute {placementClasses[actualPlacement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-
|
|
100
|
+
class="absolute {placementClasses[actualPlacement]} z-[var(--z-50)] panel-floating rounded-[var(--radius-sm)] shadow-deep p-4 animate-[scale-in_0.15s_var(--ease-sharp)]"
|
|
101
101
|
role="dialog"
|
|
102
102
|
>
|
|
103
103
|
{@render children?.()}
|