@picked/svelte 0.0.45-next.1 → 0.0.45-next.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/plugins/devtools/DevToolsExperience.svelte +61 -44
- package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +2 -1
- package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +22 -20
- package/dist/plugins/devtools/components/Tabs.svelte +20 -7
- package/dist/plugins/devtools/experience.js +1 -1
- package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +18 -18
- package/dist/plugins/devtools/screens/events/EventListItem.svelte +28 -42
- package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +10 -11
- package/dist/plugins/devtools/screens/events/EventsTable.svelte +31 -33
- package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +13 -16
- package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +20 -71
- package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +96 -232
- package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +14 -16
- package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +18 -16
- package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +14 -16
- package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +25 -63
- package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +43 -64
- package/dist/plugins/devtools/ui/actions/Button.svelte +171 -0
- package/dist/plugins/devtools/ui/actions/CopyButton.svelte +88 -0
- package/dist/plugins/devtools/ui/data/Badge.svelte +62 -0
- package/dist/plugins/devtools/ui/data/CodeBlock.svelte +38 -0
- package/dist/plugins/devtools/ui/data/DataTable.svelte +134 -0
- package/dist/plugins/devtools/ui/data/EmptyState.svelte +26 -0
- package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +66 -0
- package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +55 -0
- package/dist/plugins/devtools/ui/feedback/Banner.svelte +116 -0
- package/dist/plugins/devtools/ui/feedback/Spinner.svelte +79 -0
- package/dist/plugins/devtools/ui/forms/Checkbox.svelte +139 -0
- package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +218 -0
- package/dist/plugins/devtools/ui/forms/Select.svelte +137 -0
- package/dist/plugins/devtools/ui/forms/Switch.svelte +158 -0
- package/dist/plugins/devtools/ui/forms/TextField.svelte +125 -0
- package/dist/plugins/devtools/ui/index.d.ts +22 -0
- package/dist/plugins/devtools/ui/index.js +1 -0
- package/dist/plugins/devtools/ui/layout/Card.svelte +55 -0
- package/dist/plugins/devtools/ui/layout/Divider.svelte +34 -0
- package/dist/plugins/devtools/ui/layout/Spacer.svelte +11 -0
- package/dist/plugins/devtools/ui/layout/Stack.svelte +129 -0
- package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +90 -0
- package/dist/plugins/devtools/ui/tokens.css +144 -0
- package/dist/plugins/devtools/ui/tokens.css.js +1 -0
- package/dist/plugins/devtools/ui/typography/Heading.svelte +55 -0
- package/dist/plugins/devtools/ui/typography/Text.svelte +93 -0
- package/package.json +5 -2
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@paired-component apps/dashboard/src/shared/ui/forms/radio-group.tsx
|
|
3
|
+
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
+
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
+
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
interface RadioOption {
|
|
9
|
+
value: string
|
|
10
|
+
label: string
|
|
11
|
+
helpText?: string
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
label: string
|
|
17
|
+
name: string
|
|
18
|
+
options: RadioOption[]
|
|
19
|
+
value?: string
|
|
20
|
+
onchange?: (value: string) => void
|
|
21
|
+
error?: string
|
|
22
|
+
helpText?: string
|
|
23
|
+
disabled?: boolean
|
|
24
|
+
required?: boolean
|
|
25
|
+
orientation?: 'horizontal' | 'vertical'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
label,
|
|
30
|
+
name,
|
|
31
|
+
options,
|
|
32
|
+
value = '',
|
|
33
|
+
onchange,
|
|
34
|
+
error,
|
|
35
|
+
helpText,
|
|
36
|
+
disabled = false,
|
|
37
|
+
required = false,
|
|
38
|
+
orientation = 'vertical',
|
|
39
|
+
}: Props = $props()
|
|
40
|
+
|
|
41
|
+
let groupId = $derived(`radio-${name}`)
|
|
42
|
+
let errorId = $derived(`${groupId}-error`)
|
|
43
|
+
let helpId = $derived(`${groupId}-help`)
|
|
44
|
+
|
|
45
|
+
function handleChange(event: Event): void {
|
|
46
|
+
if (event.target instanceof HTMLInputElement) {
|
|
47
|
+
onchange?.(event.target.value)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<div
|
|
53
|
+
class="radio-group"
|
|
54
|
+
role="radiogroup"
|
|
55
|
+
aria-labelledby="{groupId}-label"
|
|
56
|
+
aria-describedby={error ? errorId : helpText ? helpId : undefined}
|
|
57
|
+
data-name={name}
|
|
58
|
+
>
|
|
59
|
+
<span id="{groupId}-label" class="group-label">
|
|
60
|
+
{label}
|
|
61
|
+
{#if required}<span class="required"> *</span>{/if}
|
|
62
|
+
</span>
|
|
63
|
+
|
|
64
|
+
<div class="options orientation-{orientation}">
|
|
65
|
+
{#each options as option}
|
|
66
|
+
<label class="radio-label" class:is-disabled={disabled || option.disabled}>
|
|
67
|
+
<input
|
|
68
|
+
type="radio"
|
|
69
|
+
class="radio-input"
|
|
70
|
+
{name}
|
|
71
|
+
value={option.value}
|
|
72
|
+
checked={value === option.value}
|
|
73
|
+
disabled={disabled || option.disabled}
|
|
74
|
+
onchange={handleChange}
|
|
75
|
+
/>
|
|
76
|
+
<span
|
|
77
|
+
class="radio-circle"
|
|
78
|
+
class:selected={value === option.value}
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
>
|
|
81
|
+
{#if value === option.value}
|
|
82
|
+
<span class="radio-dot"></span>
|
|
83
|
+
{/if}
|
|
84
|
+
</span>
|
|
85
|
+
<span class="option-content">
|
|
86
|
+
<span class="option-label">{option.label}</span>
|
|
87
|
+
{#if option.helpText}
|
|
88
|
+
<span class="option-help">{option.helpText}</span>
|
|
89
|
+
{/if}
|
|
90
|
+
</span>
|
|
91
|
+
</label>
|
|
92
|
+
{/each}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{#if error}
|
|
96
|
+
<p id={errorId} class="error-text">{error}</p>
|
|
97
|
+
{:else if helpText}
|
|
98
|
+
<p id={helpId} class="help-text">{helpText}</p>
|
|
99
|
+
{/if}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<style>
|
|
103
|
+
.radio-group {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
gap: var(--space-1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.group-label {
|
|
110
|
+
font-size: var(--text-sm);
|
|
111
|
+
font-weight: var(--font-medium);
|
|
112
|
+
color: var(--color-gray-900);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.required {
|
|
116
|
+
color: var(--color-red-500);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.options {
|
|
120
|
+
display: flex;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.orientation-horizontal {
|
|
124
|
+
flex-direction: row;
|
|
125
|
+
gap: var(--space-4);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.orientation-vertical {
|
|
129
|
+
flex-direction: column;
|
|
130
|
+
gap: var(--space-2);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.radio-label {
|
|
134
|
+
display: flex;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
align-items: flex-start;
|
|
137
|
+
gap: var(--space-2);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.radio-label.is-disabled {
|
|
141
|
+
cursor: not-allowed;
|
|
142
|
+
opacity: 0.5;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.radio-input {
|
|
146
|
+
position: absolute;
|
|
147
|
+
width: 1px;
|
|
148
|
+
height: 1px;
|
|
149
|
+
padding: 0;
|
|
150
|
+
margin: -1px;
|
|
151
|
+
overflow: hidden;
|
|
152
|
+
clip: rect(0, 0, 0, 0);
|
|
153
|
+
white-space: nowrap;
|
|
154
|
+
border-width: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.radio-circle {
|
|
158
|
+
margin-top: 0.125rem;
|
|
159
|
+
display: flex;
|
|
160
|
+
width: 1rem;
|
|
161
|
+
height: 1rem;
|
|
162
|
+
flex-shrink: 0;
|
|
163
|
+
align-items: center;
|
|
164
|
+
justify-content: center;
|
|
165
|
+
border-radius: var(--radius-full);
|
|
166
|
+
border: 1px solid var(--color-gray-300);
|
|
167
|
+
background-color: var(--color-white);
|
|
168
|
+
transition: border-color 0.15s ease, background-color 0.15s ease;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.radio-circle.selected {
|
|
172
|
+
border-color: var(--color-blue-600);
|
|
173
|
+
background-color: var(--color-blue-600);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.radio-circle:not(.selected):hover {
|
|
177
|
+
border-color: var(--color-gray-500);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.radio-input:focus-visible + .radio-circle {
|
|
181
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
182
|
+
outline-offset: var(--ring-offset);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.radio-dot {
|
|
186
|
+
width: 0.375rem;
|
|
187
|
+
height: 0.375rem;
|
|
188
|
+
border-radius: var(--radius-full);
|
|
189
|
+
background-color: var(--color-white);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.option-content {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.option-label {
|
|
198
|
+
font-size: var(--text-sm);
|
|
199
|
+
color: var(--color-gray-900);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.option-help {
|
|
203
|
+
font-size: var(--text-xs);
|
|
204
|
+
color: var(--color-gray-500);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.error-text {
|
|
208
|
+
font-size: var(--text-sm);
|
|
209
|
+
color: var(--color-red-600);
|
|
210
|
+
margin: 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.help-text {
|
|
214
|
+
font-size: var(--text-sm);
|
|
215
|
+
color: var(--color-gray-500);
|
|
216
|
+
margin: 0;
|
|
217
|
+
}
|
|
218
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@paired-component apps/dashboard/src/shared/ui/forms/select.tsx
|
|
3
|
+
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
+
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
+
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
interface SelectOption {
|
|
9
|
+
value: string
|
|
10
|
+
label: string
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
label: string
|
|
16
|
+
name: string
|
|
17
|
+
options: SelectOption[]
|
|
18
|
+
value?: string
|
|
19
|
+
onchange?: (value: string) => void
|
|
20
|
+
placeholder?: string
|
|
21
|
+
error?: string
|
|
22
|
+
helpText?: string
|
|
23
|
+
disabled?: boolean
|
|
24
|
+
required?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
label,
|
|
29
|
+
name,
|
|
30
|
+
options,
|
|
31
|
+
value = '',
|
|
32
|
+
onchange,
|
|
33
|
+
placeholder,
|
|
34
|
+
error,
|
|
35
|
+
helpText,
|
|
36
|
+
disabled = false,
|
|
37
|
+
required = false,
|
|
38
|
+
}: Props = $props()
|
|
39
|
+
|
|
40
|
+
let selectId = $derived(`select-${name}`)
|
|
41
|
+
let errorId = $derived(`${selectId}-error`)
|
|
42
|
+
let helpId = $derived(`${selectId}-help`)
|
|
43
|
+
|
|
44
|
+
function handleChange(event: Event): void {
|
|
45
|
+
if (event.target instanceof HTMLSelectElement) {
|
|
46
|
+
onchange?.(event.target.value)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<div class="select-field">
|
|
52
|
+
<label class="label" for={selectId}>
|
|
53
|
+
{label}
|
|
54
|
+
{#if required}<span class="required"> *</span>{/if}
|
|
55
|
+
</label>
|
|
56
|
+
<select
|
|
57
|
+
id={selectId}
|
|
58
|
+
class="select"
|
|
59
|
+
class:has-error={!!error}
|
|
60
|
+
{name}
|
|
61
|
+
{disabled}
|
|
62
|
+
{required}
|
|
63
|
+
{value}
|
|
64
|
+
aria-invalid={error ? true : undefined}
|
|
65
|
+
aria-describedby={error ? errorId : helpText ? helpId : undefined}
|
|
66
|
+
onchange={handleChange}
|
|
67
|
+
>
|
|
68
|
+
{#if placeholder}
|
|
69
|
+
<option value="" disabled>{placeholder}</option>
|
|
70
|
+
{/if}
|
|
71
|
+
{#each options as option}
|
|
72
|
+
<option value={option.value} disabled={option.disabled}>{option.label}</option>
|
|
73
|
+
{/each}
|
|
74
|
+
</select>
|
|
75
|
+
{#if error}
|
|
76
|
+
<p id={errorId} class="error-text">{error}</p>
|
|
77
|
+
{:else if helpText}
|
|
78
|
+
<p id={helpId} class="help-text">{helpText}</p>
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<style>
|
|
83
|
+
.select-field {
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
gap: var(--space-1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.label {
|
|
90
|
+
font-size: var(--text-sm);
|
|
91
|
+
font-weight: var(--font-medium);
|
|
92
|
+
color: var(--color-gray-900);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.required {
|
|
96
|
+
color: var(--color-red-500);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.select {
|
|
100
|
+
border-radius: var(--radius-md);
|
|
101
|
+
border: 1px solid var(--color-gray-300);
|
|
102
|
+
padding: var(--space-2) var(--space-3);
|
|
103
|
+
font-size: var(--text-sm);
|
|
104
|
+
font-family: inherit;
|
|
105
|
+
color: var(--color-gray-900);
|
|
106
|
+
background-color: var(--color-white);
|
|
107
|
+
appearance: auto;
|
|
108
|
+
transition: border-color 0.15s ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.select:focus {
|
|
112
|
+
outline: none;
|
|
113
|
+
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.select.has-error {
|
|
117
|
+
border-color: var(--color-red-500);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.select:disabled {
|
|
121
|
+
cursor: not-allowed;
|
|
122
|
+
background-color: var(--color-gray-100);
|
|
123
|
+
opacity: 0.5;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.error-text {
|
|
127
|
+
font-size: var(--text-sm);
|
|
128
|
+
color: var(--color-red-600);
|
|
129
|
+
margin: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.help-text {
|
|
133
|
+
font-size: var(--text-sm);
|
|
134
|
+
color: var(--color-gray-500);
|
|
135
|
+
margin: 0;
|
|
136
|
+
}
|
|
137
|
+
</style>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@paired-component apps/dashboard/src/shared/ui/forms/switch.tsx
|
|
3
|
+
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
+
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
+
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
interface Props {
|
|
9
|
+
label: string
|
|
10
|
+
name: string
|
|
11
|
+
checked?: boolean
|
|
12
|
+
onchange?: (checked: boolean) => void
|
|
13
|
+
helpText?: string
|
|
14
|
+
disabled?: boolean
|
|
15
|
+
size?: 'small' | 'medium'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
label,
|
|
20
|
+
name,
|
|
21
|
+
checked = false,
|
|
22
|
+
onchange,
|
|
23
|
+
helpText,
|
|
24
|
+
disabled = false,
|
|
25
|
+
size = 'medium',
|
|
26
|
+
}: Props = $props()
|
|
27
|
+
|
|
28
|
+
function handleClick(): void {
|
|
29
|
+
if (!disabled) {
|
|
30
|
+
onchange?.(!checked)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
35
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
36
|
+
event.preventDefault()
|
|
37
|
+
handleClick()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<div class="switch-field">
|
|
43
|
+
<label class="switch-label" class:is-disabled={disabled}>
|
|
44
|
+
<button
|
|
45
|
+
type="button"
|
|
46
|
+
class="switch-track size-{size}"
|
|
47
|
+
class:checked
|
|
48
|
+
role="switch"
|
|
49
|
+
aria-checked={checked}
|
|
50
|
+
aria-label={label}
|
|
51
|
+
{disabled}
|
|
52
|
+
{name}
|
|
53
|
+
onclick={handleClick}
|
|
54
|
+
onkeydown={handleKeydown}
|
|
55
|
+
>
|
|
56
|
+
<span class="switch-thumb size-{size}" class:checked></span>
|
|
57
|
+
</button>
|
|
58
|
+
<span class="label-text">{label}</span>
|
|
59
|
+
</label>
|
|
60
|
+
{#if helpText}
|
|
61
|
+
<p class="help-text">{helpText}</p>
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<style>
|
|
66
|
+
.switch-field {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: var(--space-1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.switch-label {
|
|
73
|
+
display: flex;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: var(--space-3);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.switch-label.is-disabled {
|
|
80
|
+
cursor: not-allowed;
|
|
81
|
+
opacity: 0.5;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.switch-track {
|
|
85
|
+
position: relative;
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
border-radius: var(--radius-full);
|
|
89
|
+
background-color: var(--color-gray-200);
|
|
90
|
+
border: none;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
padding: 0;
|
|
93
|
+
transition: background-color 0.2s ease;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.switch-track.size-small {
|
|
97
|
+
width: 2.25rem;
|
|
98
|
+
height: 1.25rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.switch-track.size-medium {
|
|
102
|
+
width: 2.75rem;
|
|
103
|
+
height: 1.5rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.switch-track.checked {
|
|
107
|
+
background-color: var(--color-blue-600);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.switch-track:focus-visible {
|
|
111
|
+
outline: var(--ring-width) solid var(--ring-color);
|
|
112
|
+
outline-offset: var(--ring-offset);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.switch-track:disabled {
|
|
116
|
+
cursor: not-allowed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.switch-thumb {
|
|
120
|
+
display: inline-block;
|
|
121
|
+
transform: translateX(0.125rem);
|
|
122
|
+
border-radius: var(--radius-full);
|
|
123
|
+
background-color: var(--color-white);
|
|
124
|
+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
125
|
+
transition: transform 0.2s ease;
|
|
126
|
+
margin-top: 1px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.switch-thumb.size-small {
|
|
130
|
+
width: 1rem;
|
|
131
|
+
height: 1rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.switch-thumb.size-medium {
|
|
135
|
+
width: 1.25rem;
|
|
136
|
+
height: 1.25rem;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.switch-thumb.size-small.checked {
|
|
140
|
+
transform: translateX(1rem);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.switch-thumb.size-medium.checked {
|
|
144
|
+
transform: translateX(1.25rem);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.label-text {
|
|
148
|
+
font-size: var(--text-sm);
|
|
149
|
+
font-weight: var(--font-medium);
|
|
150
|
+
color: var(--color-gray-900);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.help-text {
|
|
154
|
+
margin: 0;
|
|
155
|
+
font-size: var(--text-sm);
|
|
156
|
+
color: var(--color-gray-500);
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@paired-component apps/dashboard/src/shared/ui/forms/text-field.tsx
|
|
3
|
+
@design-parity Props, variants, sizes, and visual output must stay in sync
|
|
4
|
+
with the Dashboard React counterpart. When modifying this component, replicate
|
|
5
|
+
the change in the paired React component to maintain visual and behavioral parity.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
interface Props {
|
|
9
|
+
label: string
|
|
10
|
+
name: string
|
|
11
|
+
type?: 'text' | 'email' | 'password' | 'number'
|
|
12
|
+
placeholder?: string
|
|
13
|
+
value?: string
|
|
14
|
+
oninput?: (value: string) => void
|
|
15
|
+
error?: string
|
|
16
|
+
helpText?: string
|
|
17
|
+
disabled?: boolean
|
|
18
|
+
required?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
label,
|
|
23
|
+
name,
|
|
24
|
+
type = 'text',
|
|
25
|
+
placeholder,
|
|
26
|
+
value = '',
|
|
27
|
+
oninput,
|
|
28
|
+
error,
|
|
29
|
+
helpText,
|
|
30
|
+
disabled = false,
|
|
31
|
+
required = false,
|
|
32
|
+
}: Props = $props()
|
|
33
|
+
|
|
34
|
+
let inputId = $derived(`field-${name}`)
|
|
35
|
+
let errorId = $derived(`${inputId}-error`)
|
|
36
|
+
let helpId = $derived(`${inputId}-help`)
|
|
37
|
+
|
|
38
|
+
function handleInput(event: Event): void {
|
|
39
|
+
if (event.target instanceof HTMLInputElement) {
|
|
40
|
+
oninput?.(event.target.value)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="text-field">
|
|
46
|
+
<label class="label" for={inputId}>
|
|
47
|
+
{label}
|
|
48
|
+
{#if required}<span class="required"> *</span>{/if}
|
|
49
|
+
</label>
|
|
50
|
+
<input
|
|
51
|
+
id={inputId}
|
|
52
|
+
class="input"
|
|
53
|
+
class:has-error={!!error}
|
|
54
|
+
{type}
|
|
55
|
+
{name}
|
|
56
|
+
{placeholder}
|
|
57
|
+
{value}
|
|
58
|
+
{disabled}
|
|
59
|
+
{required}
|
|
60
|
+
aria-invalid={error ? true : undefined}
|
|
61
|
+
aria-describedby={error ? errorId : helpText ? helpId : undefined}
|
|
62
|
+
oninput={handleInput}
|
|
63
|
+
/>
|
|
64
|
+
{#if error}
|
|
65
|
+
<p id={errorId} class="error-text">{error}</p>
|
|
66
|
+
{:else if helpText}
|
|
67
|
+
<p id={helpId} class="help-text">{helpText}</p>
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<style>
|
|
72
|
+
.text-field {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
gap: var(--space-1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.label {
|
|
79
|
+
font-size: var(--text-sm);
|
|
80
|
+
font-weight: var(--font-medium);
|
|
81
|
+
color: var(--color-gray-900);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.required {
|
|
85
|
+
color: var(--color-red-500);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.input {
|
|
89
|
+
border-radius: var(--radius-md);
|
|
90
|
+
border: 1px solid var(--color-gray-300);
|
|
91
|
+
padding: var(--space-2) var(--space-3);
|
|
92
|
+
font-size: var(--text-sm);
|
|
93
|
+
font-family: inherit;
|
|
94
|
+
color: var(--color-gray-900);
|
|
95
|
+
background-color: var(--color-white);
|
|
96
|
+
transition: border-color 0.15s ease;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.input:focus {
|
|
100
|
+
outline: none;
|
|
101
|
+
box-shadow: 0 0 0 var(--ring-width) var(--ring-color);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.input.has-error {
|
|
105
|
+
border-color: var(--color-red-500);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.input:disabled {
|
|
109
|
+
cursor: not-allowed;
|
|
110
|
+
background-color: var(--color-gray-100);
|
|
111
|
+
opacity: 0.5;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.error-text {
|
|
115
|
+
font-size: var(--text-sm);
|
|
116
|
+
color: var(--color-red-600);
|
|
117
|
+
margin: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.help-text {
|
|
121
|
+
font-size: var(--text-sm);
|
|
122
|
+
color: var(--color-gray-500);
|
|
123
|
+
margin: 0;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { default as Button } from './actions/Button.svelte';
|
|
2
|
+
export { default as CopyButton } from './actions/CopyButton.svelte';
|
|
3
|
+
export { default as Heading } from './typography/Heading.svelte';
|
|
4
|
+
export { default as Text } from './typography/Text.svelte';
|
|
5
|
+
export { default as Card } from './layout/Card.svelte';
|
|
6
|
+
export { default as Divider } from './layout/Divider.svelte';
|
|
7
|
+
export { default as Spacer } from './layout/Spacer.svelte';
|
|
8
|
+
export { default as Stack } from './layout/Stack.svelte';
|
|
9
|
+
export { default as Checkbox } from './forms/Checkbox.svelte';
|
|
10
|
+
export { default as RadioGroup } from './forms/RadioGroup.svelte';
|
|
11
|
+
export { default as Select } from './forms/Select.svelte';
|
|
12
|
+
export { default as Switch } from './forms/Switch.svelte';
|
|
13
|
+
export { default as TextField } from './forms/TextField.svelte';
|
|
14
|
+
export { default as Banner } from './feedback/Banner.svelte';
|
|
15
|
+
export { default as Spinner } from './feedback/Spinner.svelte';
|
|
16
|
+
export { default as Tooltip } from './overlays/Tooltip.svelte';
|
|
17
|
+
export { default as Badge } from './data/Badge.svelte';
|
|
18
|
+
export { default as CodeBlock } from './data/CodeBlock.svelte';
|
|
19
|
+
export { default as DataTable } from './data/DataTable.svelte';
|
|
20
|
+
export { default as EmptyState } from './data/EmptyState.svelte';
|
|
21
|
+
export { default as ExpandableRow } from './data/ExpandableRow.svelte';
|
|
22
|
+
export { default as VisualizationContainer } from './data/VisualizationContainer.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{default as t}from"./actions/Button.svelte";import{default as r}from"./actions/CopyButton.svelte";import{default as d}from"./typography/Heading.svelte";import{default as p}from"./typography/Text.svelte";import{default as x}from"./layout/Card.svelte";import{default as s}from"./layout/Divider.svelte";import{default as n}from"./layout/Spacer.svelte";import{default as S}from"./layout/Stack.svelte";import{default as C}from"./forms/Checkbox.svelte";import{default as b}from"./forms/RadioGroup.svelte";import{default as g}from"./forms/Select.svelte";import{default as w}from"./forms/Switch.svelte";import{default as D}from"./forms/TextField.svelte";import{default as R}from"./feedback/Banner.svelte";import{default as z}from"./feedback/Spinner.svelte";import{default as G}from"./overlays/Tooltip.svelte";import{default as V}from"./data/Badge.svelte";import{default as q}from"./data/CodeBlock.svelte";import{default as I}from"./data/DataTable.svelte";import{default as K}from"./data/EmptyState.svelte";import{default as M}from"./data/ExpandableRow.svelte";import{default as O}from"./data/VisualizationContainer.svelte";export{V as Badge,R as Banner,t as Button,x as Card,C as Checkbox,q as CodeBlock,r as CopyButton,I as DataTable,s as Divider,K as EmptyState,M as ExpandableRow,d as Heading,b as RadioGroup,g as Select,n as Spacer,z as Spinner,S as Stack,w as Switch,p as Text,D as TextField,G as Tooltip,O as VisualizationContainer};
|