@kahitsan/ksui 0.33.0 → 0.34.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/package.json +4 -1
- package/src/components/base/AccountAvatar.tsx +1 -1
- package/src/components/base/AddAttachmentTile.tsx +4 -4
- package/src/components/base/BadgeSelect.tsx +10 -10
- package/src/components/base/Button.tsx +70 -38
- package/src/components/base/CameraCapture.tsx +8 -8
- package/src/components/base/ChartLegend.tsx +3 -3
- package/src/components/base/CopyButton.tsx +1 -1
- package/src/components/base/DataTable.tsx +68 -72
- package/src/components/base/DatePicker.tsx +77 -82
- package/src/components/base/DateTile.tsx +7 -7
- package/src/components/base/DetailRow.tsx +2 -2
- package/src/components/base/Dropdown.tsx +15 -15
- package/src/components/base/ExistingAttachmentTile.tsx +6 -6
- package/src/components/base/EyebrowBadge.tsx +15 -15
- package/src/components/base/FormErrorBanner.tsx +1 -1
- package/src/components/base/FormField.tsx +2 -2
- package/src/components/base/ImageCropper.tsx +19 -19
- package/src/components/base/ImageViewer.tsx +7 -11
- package/src/components/base/KpiCard.tsx +7 -7
- package/src/components/base/Modal.tsx +10 -14
- package/src/components/base/ProgressBar.tsx +63 -59
- package/src/components/base/RadioCardGroup.tsx +13 -6
- package/src/components/base/SectionHeading.tsx +4 -4
- package/src/components/base/SegmentedFilter.tsx +60 -4
- package/src/components/base/SocialLinksBar.tsx +3 -15
- package/src/components/base/StatusIndicator.tsx +11 -11
- package/src/components/base/StatusPill.tsx +15 -15
- package/src/components/base/TagPill.tsx +1 -1
- package/src/components/base/ThemeToggle.tsx +12 -20
- package/src/components/base/Tooltip.tsx +2 -2
- package/src/components/composite/AccountRadioPicker.tsx +8 -4
- package/src/components/composite/ComboBox.tsx +51 -44
- package/src/components/composite/CustomRenderer.tsx +6 -13
- package/src/components/composite/FileField.tsx +10 -17
- package/src/components/composite/FlowGraph.tsx +33 -37
- package/src/components/composite/FlowRunner.tsx +11 -18
- package/src/components/composite/FormAdvancedSection.tsx +31 -31
- package/src/components/composite/LiveTimer.tsx +3 -3
- package/src/components/composite/MarkdownNotes.tsx +11 -11
- package/src/components/composite/MentionTextarea.tsx +10 -10
- package/src/components/composite/NotFound.tsx +7 -7
- package/src/components/composite/PaymentAccountPicker.tsx +19 -19
- package/src/components/composite/SalesBodyEditor.tsx +26 -26
- package/src/components/composite/SearchableSelect.tsx +14 -14
- package/src/components/composite/SecretReveal.tsx +5 -5
- package/src/components/composite/TransactionAccountFields.tsx +78 -0
- package/src/components/composite/TransactionForm.tsx +83 -169
- package/src/components/composite/TransactionPayableFields.tsx +97 -0
- package/src/components/composite/TransferAccountsPicker.tsx +20 -20
- package/src/components/composite/TransferFeeChip.tsx +2 -2
- package/src/components/composite/VoucherPicker.tsx +25 -25
- package/src/components/composite/resource/ResourceForm.tsx +1 -1
- package/src/components/composite/resource/ResourcePage.tsx +8 -8
- package/src/components/composite/resource/cells.tsx +6 -3
- package/src/utils/INPUT_CLASS.ts +6 -3
- package/src/utils/account-icons.ts +29 -7
- package/src/utils/highlight.tsx +5 -6
- package/src/utils/inject-css.ts +13 -0
- package/tailwind.js +11 -7
|
@@ -40,11 +40,11 @@ function AccountTile(p: AccountTileProps): JSX.Element {
|
|
|
40
40
|
onClick={p.onEditRequest}
|
|
41
41
|
class={`group flex flex-1 min-w-0 items-center gap-3 border px-4 py-3 text-left transition-colors cursor-pointer ${p.clipClass}`}
|
|
42
42
|
classList={{
|
|
43
|
-
"border-
|
|
43
|
+
"border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_40%,transparent)] bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_5%,transparent)] hover:bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_10%,transparent)]":
|
|
44
44
|
isFilled() && isFrom(),
|
|
45
|
-
"border-
|
|
45
|
+
"border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_40%,transparent)] bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_5%,transparent)] hover:bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_10%,transparent)]":
|
|
46
46
|
isFilled() && !isFrom(),
|
|
47
|
-
"border-dashed border-
|
|
47
|
+
"border-dashed border-[var(--ks-input-border,#3f3f46)] bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_40%,transparent)] hover:border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_50%,transparent)] hover:bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_60%,transparent)]":
|
|
48
48
|
!isFilled(),
|
|
49
49
|
}}
|
|
50
50
|
aria-label={
|
|
@@ -57,7 +57,7 @@ function AccountTile(p: AccountTileProps): JSX.Element {
|
|
|
57
57
|
when={p.account}
|
|
58
58
|
keyed
|
|
59
59
|
fallback={
|
|
60
|
-
<span class="flex h-8 w-8 items-center justify-center text-
|
|
60
|
+
<span class="flex h-8 w-8 items-center justify-center text-[var(--ks-fg-subtle,#71717a)]">
|
|
61
61
|
<Plus size={18} />
|
|
62
62
|
</span>
|
|
63
63
|
}
|
|
@@ -66,7 +66,7 @@ function AccountTile(p: AccountTileProps): JSX.Element {
|
|
|
66
66
|
<AccountAvatar
|
|
67
67
|
account={a}
|
|
68
68
|
size={28}
|
|
69
|
-
iconClass={isFrom() ? "text-
|
|
69
|
+
iconClass={isFrom() ? "text-[var(--ks-info,#38bdf8)]" : "text-[var(--ks-accent-hover,#fcd34d)]"}
|
|
70
70
|
/>
|
|
71
71
|
)}
|
|
72
72
|
</Show>
|
|
@@ -74,9 +74,9 @@ function AccountTile(p: AccountTileProps): JSX.Element {
|
|
|
74
74
|
<span
|
|
75
75
|
class="text-[10px] font-semibold uppercase tracking-widest"
|
|
76
76
|
classList={{
|
|
77
|
-
"text-
|
|
78
|
-
"text-
|
|
79
|
-
"text-
|
|
77
|
+
"text-[var(--ks-info,#38bdf8)]": isFilled() && isFrom(),
|
|
78
|
+
"text-[var(--ks-accent-hover,#fcd34d)]": isFilled() && !isFrom(),
|
|
79
|
+
"text-[var(--ks-fg-subtle,#71717a)]": !isFilled(),
|
|
80
80
|
}}
|
|
81
81
|
>
|
|
82
82
|
{isFrom() ? "From" : "To"}
|
|
@@ -85,23 +85,23 @@ function AccountTile(p: AccountTileProps): JSX.Element {
|
|
|
85
85
|
when={p.account}
|
|
86
86
|
keyed
|
|
87
87
|
fallback={
|
|
88
|
-
<span class="text-sm text-
|
|
88
|
+
<span class="text-sm text-[var(--ks-fg-subtle,#71717a)]">{p.emptyHint}</span>
|
|
89
89
|
}
|
|
90
90
|
>
|
|
91
91
|
{(a) => (
|
|
92
92
|
<>
|
|
93
|
-
<span class="truncate text-sm font-semibold text-
|
|
93
|
+
<span class="truncate text-sm font-semibold text-[var(--ks-fg,#ffffff)]">
|
|
94
94
|
{a.name}
|
|
95
95
|
</span>
|
|
96
96
|
<Show when={a.balance != null}>
|
|
97
|
-
<span class="flex items-baseline gap-1 text-[11px] tabular-nums text-
|
|
97
|
+
<span class="flex items-baseline gap-1 text-[11px] tabular-nums text-[var(--ks-fg-muted,#a1a1aa)]">
|
|
98
98
|
<span class="truncate">{formatPHP(a.balance!)}</span>
|
|
99
99
|
<Show when={p.delta !== 0}>
|
|
100
100
|
<span
|
|
101
101
|
class="whitespace-nowrap font-semibold"
|
|
102
102
|
classList={{
|
|
103
|
-
"text-
|
|
104
|
-
"text-
|
|
103
|
+
"text-[var(--ks-danger-fg,#f87171)]": p.delta < 0,
|
|
104
|
+
"text-[var(--ks-success-fg,#34d399)]": p.delta > 0,
|
|
105
105
|
}}
|
|
106
106
|
data-testid={`transactions-form-transfer-delta-${p.role}`}
|
|
107
107
|
>
|
|
@@ -168,7 +168,7 @@ export default function TransferAccountsPicker(
|
|
|
168
168
|
type="button"
|
|
169
169
|
data-testid="transactions-form-transfer-swap"
|
|
170
170
|
onClick={swap}
|
|
171
|
-
class="flex h-9 w-9 shrink-0 items-center justify-center self-center border border-
|
|
171
|
+
class="flex h-9 w-9 shrink-0 items-center justify-center self-center border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] text-[var(--ks-fg,#ffffff)] transition-colors hover:border-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_60%,transparent)] hover:text-[var(--ks-accent-hover,#fcd34d)] cursor-pointer ks-hud-clip-button max-sm:rotate-90"
|
|
172
172
|
aria-label="Swap source and destination"
|
|
173
173
|
title="Swap source and destination"
|
|
174
174
|
>
|
|
@@ -193,8 +193,8 @@ export default function TransferAccountsPicker(
|
|
|
193
193
|
x2="56"
|
|
194
194
|
y2="7"
|
|
195
195
|
>
|
|
196
|
-
<stop offset="0" stop-color
|
|
197
|
-
<stop offset="1" stop-color
|
|
196
|
+
<stop offset="0" style={{ "stop-color": "var(--ks-info, #38bdf8)" }} />
|
|
197
|
+
<stop offset="1" style={{ "stop-color": "var(--ks-warning, #f59e0b)" }} />
|
|
198
198
|
</linearGradient>
|
|
199
199
|
</defs>
|
|
200
200
|
<line
|
|
@@ -206,7 +206,7 @@ export default function TransferAccountsPicker(
|
|
|
206
206
|
stroke-width="2"
|
|
207
207
|
class="fin-flow-dash"
|
|
208
208
|
/>
|
|
209
|
-
<path d="M44 1 L56 7 L44 13 Z" fill
|
|
209
|
+
<path d="M44 1 L56 7 L44 13 Z" style={{ fill: "var(--ks-warning, #f59e0b)" }} />
|
|
210
210
|
</svg>
|
|
211
211
|
<svg
|
|
212
212
|
viewBox="0 0 14 40"
|
|
@@ -221,8 +221,8 @@ export default function TransferAccountsPicker(
|
|
|
221
221
|
x2="7"
|
|
222
222
|
y2="40"
|
|
223
223
|
>
|
|
224
|
-
<stop offset="0" stop-color
|
|
225
|
-
<stop offset="1" stop-color
|
|
224
|
+
<stop offset="0" style={{ "stop-color": "var(--ks-info, #38bdf8)" }} />
|
|
225
|
+
<stop offset="1" style={{ "stop-color": "var(--ks-warning, #f59e0b)" }} />
|
|
226
226
|
</linearGradient>
|
|
227
227
|
</defs>
|
|
228
228
|
<line
|
|
@@ -234,7 +234,7 @@ export default function TransferAccountsPicker(
|
|
|
234
234
|
stroke-width="2"
|
|
235
235
|
class="fin-flow-dash"
|
|
236
236
|
/>
|
|
237
|
-
<path d="M1 30 L13 30 L7 40 Z" fill
|
|
237
|
+
<path d="M1 30 L13 30 L7 40 Z" style={{ fill: "var(--ks-warning, #f59e0b)" }} />
|
|
238
238
|
</svg>
|
|
239
239
|
</div>
|
|
240
240
|
</Show>
|
|
@@ -13,9 +13,9 @@ export default function TransferFeeChip(props: TransferFeeChipProps) {
|
|
|
13
13
|
onClick={props.onToggle}
|
|
14
14
|
class="flex items-center gap-1.5 self-center rounded-md border px-2.5 py-1 text-[11px] font-semibold uppercase tracking-wider transition-colors cursor-pointer"
|
|
15
15
|
classList={{
|
|
16
|
-
"border-
|
|
16
|
+
"border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_50%,transparent)] bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_15%,transparent)] text-[var(--ks-info-fg,#7dd3fc)] hover:bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_25%,transparent)]":
|
|
17
17
|
props.enabled,
|
|
18
|
-
"border-
|
|
18
|
+
"border-[var(--ks-input-border,#3f3f46)] bg-[color-mix(in_srgb,var(--ks-surface,#0f0f0f)_60%,transparent)] text-[var(--ks-fg-muted,#a1a1aa)] hover:border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_40%,transparent)] hover:text-[var(--ks-info-fg,#7dd3fc)]":
|
|
19
19
|
!props.enabled,
|
|
20
20
|
}}
|
|
21
21
|
aria-pressed={props.enabled}
|
|
@@ -229,15 +229,15 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
229
229
|
onClick={() => !props.disabled && setOpen((o) => !o)}
|
|
230
230
|
class={`${props.compact ? "inline-flex" : "w-full flex"} items-center gap-2 ${
|
|
231
231
|
props.compact ? "px-2.5 py-2" : "px-3 py-2.5"
|
|
232
|
-
} rounded-lg bg-
|
|
232
|
+
} rounded-lg bg-[color-mix(in_srgb,var(--ks-border,rgba(39,39,42,0.5))_30%,transparent)] border border-[color-mix(in_srgb,var(--ks-border-strong,#3f3f46)_50%,transparent)] hover:border-[color-mix(in_srgb,var(--ks-primary,#c9a961)_40%,transparent)] hover:bg-[color-mix(in_srgb,var(--ks-primary,#c9a961)_5%,transparent)] transition-colors text-sm text-left cursor-pointer disabled:cursor-not-allowed disabled:opacity-60`}
|
|
233
233
|
aria-haspopup="listbox"
|
|
234
234
|
aria-expanded={open()}
|
|
235
235
|
>
|
|
236
|
-
<Ticket size={16} class="shrink-0 text-
|
|
237
|
-
<Show when={props.selected} fallback={<span class="text-
|
|
236
|
+
<Ticket size={16} class="shrink-0 text-[var(--ks-fg-muted,#a1a1aa)]" />
|
|
237
|
+
<Show when={props.selected} fallback={<span class="text-[var(--ks-fg-subtle,#71717a)] italic">No voucher</span>}>
|
|
238
238
|
<span class="flex-1 min-w-0">
|
|
239
|
-
<span class="block truncate text-
|
|
240
|
-
<span class="block truncate text-[11px] text-
|
|
239
|
+
<span class="block truncate text-[var(--ks-fg,#ffffff)] font-medium">{props.selected!.code}</span>
|
|
240
|
+
<span class="block truncate text-[11px] text-[var(--ks-success-fg,#34d399)]">
|
|
241
241
|
{formatVoucherDescription(props.selected!)}
|
|
242
242
|
<Show when={previewDiscount() > 0}> · {formatCurrency(previewDiscount())} off</Show>
|
|
243
243
|
</span>
|
|
@@ -246,7 +246,7 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
246
246
|
type="button"
|
|
247
247
|
data-testid="voucher-picker-clear"
|
|
248
248
|
onClick={clear}
|
|
249
|
-
class="shrink-0 p-1 rounded text-
|
|
249
|
+
class="shrink-0 p-1 rounded text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-danger-fg,#f87171)] hover:bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)] transition-colors cursor-pointer"
|
|
250
250
|
title="Remove voucher"
|
|
251
251
|
aria-label="Remove voucher"
|
|
252
252
|
>
|
|
@@ -260,14 +260,14 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
260
260
|
<div
|
|
261
261
|
ref={popupRef}
|
|
262
262
|
data-testid="voucher-picker-popup"
|
|
263
|
-
class="z-[100] rounded-md border border-
|
|
263
|
+
class="z-[100] rounded-md border border-[var(--ks-input-border,#3f3f46)] bg-[color-mix(in_srgb,var(--ks-overlay-surface,#18181b)_95%,transparent)] backdrop-blur shadow-xl overflow-hidden flex flex-col"
|
|
264
264
|
style={popupStyle()}
|
|
265
265
|
>
|
|
266
|
-
<div class="px-3 py-2 border-b border-
|
|
267
|
-
<Ticket size={14} class="text-
|
|
268
|
-
<span class="text-xs uppercase tracking-widest text-
|
|
266
|
+
<div class="px-3 py-2 border-b border-[var(--ks-border-strong,#3f3f46)] flex items-center gap-2">
|
|
267
|
+
<Ticket size={14} class="text-[var(--ks-fg-subtle,#71717a)] shrink-0" />
|
|
268
|
+
<span class="text-xs uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-bold">Vouchers</span>
|
|
269
269
|
<Show when={loading()}>
|
|
270
|
-
<Loader2 size={14} class="animate-spin text-
|
|
270
|
+
<Loader2 size={14} class="animate-spin text-[var(--ks-fg-subtle,#71717a)] ml-auto shrink-0" />
|
|
271
271
|
</Show>
|
|
272
272
|
</div>
|
|
273
273
|
<ul
|
|
@@ -277,7 +277,7 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
277
277
|
>
|
|
278
278
|
<Show when={error()}>
|
|
279
279
|
<li>
|
|
280
|
-
<div role="status" class="px-3 py-2 text-xs text-
|
|
280
|
+
<div role="status" class="px-3 py-2 text-xs text-[var(--ks-danger-fg,#f87171)]">
|
|
281
281
|
{error()}
|
|
282
282
|
</div>
|
|
283
283
|
</li>
|
|
@@ -286,7 +286,7 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
286
286
|
when={!loading() && !error() && applicable().length === 0 && inapplicable().length === 0}
|
|
287
287
|
>
|
|
288
288
|
<li>
|
|
289
|
-
<div role="status" class="px-3 py-4 text-xs text-
|
|
289
|
+
<div role="status" class="px-3 py-4 text-xs text-[var(--ks-fg-subtle,#71717a)] text-center">
|
|
290
290
|
No vouchers available.
|
|
291
291
|
</div>
|
|
292
292
|
</li>
|
|
@@ -301,20 +301,20 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
301
301
|
type="button"
|
|
302
302
|
data-testid={`voucher-picker-result-${v.id}`}
|
|
303
303
|
onClick={() => select(v)}
|
|
304
|
-
class="w-full text-left px-3 py-2 hover:bg-
|
|
304
|
+
class="w-full text-left px-3 py-2 hover:bg-[color-mix(in_srgb,var(--ks-primary,#c9a961)_10%,transparent)] transition-colors flex items-start gap-2 cursor-pointer"
|
|
305
305
|
>
|
|
306
|
-
<Ticket size={14} class="shrink-0 mt-0.5 text-
|
|
306
|
+
<Ticket size={14} class="shrink-0 mt-0.5 text-[var(--ks-success-fg,#34d399)]" aria-hidden="true" />
|
|
307
307
|
<span class="flex-1 min-w-0">
|
|
308
|
-
<span class="block text-sm text-
|
|
309
|
-
<span class="block text-[11px] text-
|
|
308
|
+
<span class="block text-sm text-[var(--ks-fg,#ffffff)] truncate">{v.code}</span>
|
|
309
|
+
<span class="block text-[11px] text-[var(--ks-fg-subtle,#71717a)] truncate">
|
|
310
310
|
{formatVoucherDescription(v)}
|
|
311
311
|
</span>
|
|
312
312
|
</span>
|
|
313
|
-
<span class="text-xs text-
|
|
313
|
+
<span class="text-xs text-[var(--ks-success-fg,#34d399)] shrink-0 mt-0.5 font-mono">
|
|
314
314
|
{formatCurrency(discount())}
|
|
315
315
|
</span>
|
|
316
316
|
<Show when={selected()}>
|
|
317
|
-
<span class="text-
|
|
317
|
+
<span class="text-[var(--ks-accent,#fbbf24)] shrink-0 mt-0.5">✓</span>
|
|
318
318
|
</Show>
|
|
319
319
|
</button>
|
|
320
320
|
</li>
|
|
@@ -323,7 +323,7 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
323
323
|
</For>
|
|
324
324
|
<Show when={inapplicable().length > 0}>
|
|
325
325
|
<li>
|
|
326
|
-
<div class="px-3 pt-3 pb-1 text-[10px] uppercase tracking-widest text-
|
|
326
|
+
<div class="px-3 pt-3 pb-1 text-[10px] uppercase tracking-widest text-[var(--ks-fg-subtle,#71717a)] font-semibold border-t border-[var(--ks-border-strong,#3f3f46)] mt-1">
|
|
327
327
|
Not applicable to this cart
|
|
328
328
|
</div>
|
|
329
329
|
</li>
|
|
@@ -335,10 +335,10 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
335
335
|
class="w-full text-left px-3 py-2 flex items-start gap-2 opacity-50 cursor-not-allowed"
|
|
336
336
|
aria-disabled="true"
|
|
337
337
|
>
|
|
338
|
-
<Ticket size={14} class="shrink-0 mt-0.5 text-
|
|
338
|
+
<Ticket size={14} class="shrink-0 mt-0.5 text-[var(--ks-fg-subtle,#71717a)]" aria-hidden="true" />
|
|
339
339
|
<span class="flex-1 min-w-0">
|
|
340
|
-
<span class="block text-sm text-
|
|
341
|
-
<span class="block text-[11px] text-
|
|
340
|
+
<span class="block text-sm text-[var(--ks-fg,#ffffff)] truncate">{v.code}</span>
|
|
341
|
+
<span class="block text-[11px] text-[var(--ks-fg-subtle,#71717a)] truncate">
|
|
342
342
|
{formatVoucherDescription(v)}
|
|
343
343
|
</span>
|
|
344
344
|
</span>
|
|
@@ -349,12 +349,12 @@ export default function VoucherPicker(props: VoucherPickerProps): JSX.Element {
|
|
|
349
349
|
</Show>
|
|
350
350
|
</ul>
|
|
351
351
|
<Show when={props.selected}>
|
|
352
|
-
<div class="border-t border-
|
|
352
|
+
<div class="border-t border-[var(--ks-border-strong,#3f3f46)]">
|
|
353
353
|
<button
|
|
354
354
|
type="button"
|
|
355
355
|
data-testid="voucher-picker-clear-from-list"
|
|
356
356
|
onClick={() => select(null)}
|
|
357
|
-
class="w-full text-left px-3 py-2 text-xs text-
|
|
357
|
+
class="w-full text-left px-3 py-2 text-xs text-[var(--ks-danger-fg,#f87171)] hover:bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)] transition-colors flex items-center gap-2 cursor-pointer"
|
|
358
358
|
>
|
|
359
359
|
<X size={12} />
|
|
360
360
|
<span>Remove voucher</span>
|
|
@@ -76,7 +76,7 @@ export function ResourceForm(props: ResourceFormProps) {
|
|
|
76
76
|
<Show when={props.error}>
|
|
77
77
|
<div
|
|
78
78
|
data-testid={`${props.spec.testIdPrefix}-form-error`}
|
|
79
|
-
class="rounded-lg border border-
|
|
79
|
+
class="rounded-lg border border-[color-mix(in_srgb,var(--ks-danger,#ef4444)_30%,transparent)] bg-[color-mix(in_srgb,var(--ks-danger,#ef4444)_10%,transparent)] px-3 py-2 text-sm text-[var(--ks-danger-fg,#f87171)]"
|
|
80
80
|
>
|
|
81
81
|
{props.error}
|
|
82
82
|
</div>
|
|
@@ -316,7 +316,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
316
316
|
onChange={(e) =>
|
|
317
317
|
setFilterState(f.param, e.currentTarget.value)
|
|
318
318
|
}
|
|
319
|
-
class="rounded-lg border border-
|
|
319
|
+
class="rounded-lg border border-[var(--ks-border,rgba(39,39,42,0.5))] bg-[var(--ks-input-bg,#18181b)] px-3 py-1.5 text-xs text-[var(--ks-fg-muted,#a1a1aa)] cursor-pointer"
|
|
320
320
|
>
|
|
321
321
|
<For each={f.options}>
|
|
322
322
|
{(o) => <option value={o.value}>{o.label}</option>}
|
|
@@ -344,7 +344,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
344
344
|
>
|
|
345
345
|
<div data-testid={`${spec.testIdPrefix}-create-modal`}>
|
|
346
346
|
<div class="flex items-center justify-between mb-6">
|
|
347
|
-
<h2 class="text-lg font-semibold text-
|
|
347
|
+
<h2 class="text-lg font-semibold text-[var(--ks-fg,#ffffff)]">
|
|
348
348
|
{spec.labels.createTitle}
|
|
349
349
|
</h2>
|
|
350
350
|
<button
|
|
@@ -352,7 +352,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
352
352
|
setCreateOpen(false);
|
|
353
353
|
resetForm();
|
|
354
354
|
}}
|
|
355
|
-
class="text-
|
|
355
|
+
class="text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-fg,#ffffff)] cursor-pointer"
|
|
356
356
|
aria-label="Close"
|
|
357
357
|
>
|
|
358
358
|
<X size={20} />
|
|
@@ -387,7 +387,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
387
387
|
>
|
|
388
388
|
<div data-testid={`${spec.testIdPrefix}-detail-modal`}>
|
|
389
389
|
<div class="flex items-center justify-between mb-6">
|
|
390
|
-
<h2 class="text-lg font-semibold text-
|
|
390
|
+
<h2 class="text-lg font-semibold text-[var(--ks-fg,#ffffff)]">
|
|
391
391
|
{editing()
|
|
392
392
|
? spec.labels.editTitle
|
|
393
393
|
: String(row()[spec.labels.titleField] ?? "")}
|
|
@@ -396,7 +396,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
396
396
|
<Show when={!editing() && canEdit()}>
|
|
397
397
|
<button
|
|
398
398
|
onClick={startEdit}
|
|
399
|
-
class="text-
|
|
399
|
+
class="text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-accent,#fbbf24)] cursor-pointer p-1"
|
|
400
400
|
title="Edit"
|
|
401
401
|
aria-label="Edit"
|
|
402
402
|
>
|
|
@@ -407,7 +407,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
407
407
|
{row()[spec.softDeleteField] ? (
|
|
408
408
|
<button
|
|
409
409
|
onClick={() => handleArchive(row().id)}
|
|
410
|
-
class="text-
|
|
410
|
+
class="text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-danger-fg,#f87171)] cursor-pointer p-1"
|
|
411
411
|
title="Archive"
|
|
412
412
|
aria-label="Archive"
|
|
413
413
|
>
|
|
@@ -416,7 +416,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
416
416
|
) : (
|
|
417
417
|
<button
|
|
418
418
|
onClick={() => handleRestore(row().id)}
|
|
419
|
-
class="text-
|
|
419
|
+
class="text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-success-fg,#34d399)] cursor-pointer p-1"
|
|
420
420
|
title="Restore"
|
|
421
421
|
aria-label="Restore"
|
|
422
422
|
>
|
|
@@ -429,7 +429,7 @@ export function ResourcePage<T extends ResourceRow>(
|
|
|
429
429
|
setDetailRow(null);
|
|
430
430
|
setEditing(false);
|
|
431
431
|
}}
|
|
432
|
-
class="text-
|
|
432
|
+
class="text-[var(--ks-fg-subtle,#71717a)] hover:text-[var(--ks-fg,#ffffff)] cursor-pointer p-1"
|
|
433
433
|
aria-label="Close"
|
|
434
434
|
>
|
|
435
435
|
<X size={20} />
|
|
@@ -18,7 +18,7 @@ export function renderCell(
|
|
|
18
18
|
return (
|
|
19
19
|
<button
|
|
20
20
|
data-testid={`${spec.testIdPrefix}-row-${row.id}`}
|
|
21
|
-
class="text-left text-
|
|
21
|
+
class="text-left text-[var(--ks-fg,#ffffff)] hover:text-[var(--ks-accent,#fbbf24)] transition-colors cursor-pointer"
|
|
22
22
|
onClick={() => onTitleClick(row.id)}
|
|
23
23
|
>
|
|
24
24
|
{String(raw ?? "")}
|
|
@@ -26,7 +26,7 @@ export function renderCell(
|
|
|
26
26
|
);
|
|
27
27
|
case "enum": {
|
|
28
28
|
const key = String(raw ?? "");
|
|
29
|
-
return <span class="text-
|
|
29
|
+
return <span class="text-[var(--ks-fg-muted,#a1a1aa)] text-sm capitalize">{r.labels[key] || key}</span>;
|
|
30
30
|
}
|
|
31
31
|
case "status": {
|
|
32
32
|
const active = Boolean(raw);
|
|
@@ -35,7 +35,10 @@ export function renderCell(
|
|
|
35
35
|
}
|
|
36
36
|
case "text":
|
|
37
37
|
default: {
|
|
38
|
-
const cls =
|
|
38
|
+
const cls =
|
|
39
|
+
r.type === "text" && r.muted
|
|
40
|
+
? "text-[var(--ks-fg-subtle,#71717a)] text-sm"
|
|
41
|
+
: "text-[var(--ks-fg-muted,#a1a1aa)] text-sm";
|
|
39
42
|
const text = raw === null || raw === undefined || raw === "" ? "—" : String(raw);
|
|
40
43
|
return <span class={cls}>{text}</span>;
|
|
41
44
|
}
|
package/src/utils/INPUT_CLASS.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Shared Tailwind class string for text inputs across plugin forms: a
|
|
2
|
-
// full-width rounded input with the
|
|
2
|
+
// full-width rounded input with the themed input surface and the amber focus
|
|
3
3
|
// ring. Copied verbatim from the packages remote UI atoms so the shared kit
|
|
4
4
|
// owns the single source of truth. Pure string constant, no dependencies.
|
|
5
|
-
|
|
5
|
+
//
|
|
6
|
+
// bg reads --ks-input-bg (not --ks-border, THEME-SPEC §1.2's dedicated input
|
|
7
|
+
// surface token) — the border token happens to be a plausible dark literal so
|
|
8
|
+
// this silently looked right in the `dark` theme and only broke elsewhere.
|
|
6
9
|
export const INPUT_CLASS =
|
|
7
|
-
"w-full rounded-lg border border-
|
|
10
|
+
"w-full rounded-lg border border-[var(--ks-input-border,#3f3f46)] bg-[var(--ks-input-bg,#18181b)] px-3 py-2 text-sm text-[var(--ks-fg,#ffffff)] focus:border-[color-mix(in_srgb,var(--ks-focus-ring,#c9a961)_50%,transparent)] focus:outline-none";
|
|
@@ -74,17 +74,39 @@ export function getAccountIcon(account: { icon?: string | null; type: string }):
|
|
|
74
74
|
return DEFAULT_BY_TYPE[account.type] ?? Banknote;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// bank has no exact-hue token (blue-400/500 family isn't in §1.2), so it
|
|
78
|
+
// drifts to the nearest semantic bucket (--ks-info, sky). e_wallet/capital's
|
|
79
|
+
// bg (amber-500) and border (amber-400) are each an EXACT hex match to
|
|
80
|
+
// --ks-warning and --ks-accent respectively, so those keep zero-regression
|
|
81
|
+
// color-mix rather than sharing one base. Same for cash: bg is an exact
|
|
82
|
+
// --ks-success match, border an exact --ks-success-fg match.
|
|
77
83
|
const DEFAULT_TONE_BY_TYPE: Record<string, { text: string; bg: string; border: string }> = {
|
|
78
|
-
bank: {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
bank: {
|
|
85
|
+
text: "text-[var(--ks-info,#38bdf8)]",
|
|
86
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-info,#38bdf8)_10%,transparent)]",
|
|
87
|
+
border: "border-[color-mix(in_srgb,var(--ks-info,#38bdf8)_40%,transparent)]",
|
|
88
|
+
},
|
|
89
|
+
e_wallet: {
|
|
90
|
+
text: "text-[var(--ks-accent,#fbbf24)]",
|
|
91
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_10%,transparent)]",
|
|
92
|
+
border: "border-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_40%,transparent)]",
|
|
93
|
+
},
|
|
94
|
+
cash: {
|
|
95
|
+
text: "text-[var(--ks-success-fg,#34d399)]",
|
|
96
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-success,#10b981)_10%,transparent)]",
|
|
97
|
+
border: "border-[color-mix(in_srgb,var(--ks-success-fg,#34d399)_40%,transparent)]",
|
|
98
|
+
},
|
|
99
|
+
capital: {
|
|
100
|
+
text: "text-[var(--ks-accent,#fbbf24)]",
|
|
101
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-warning,#f59e0b)_10%,transparent)]",
|
|
102
|
+
border: "border-[color-mix(in_srgb,var(--ks-accent,#fbbf24)_40%,transparent)]",
|
|
103
|
+
},
|
|
82
104
|
};
|
|
83
105
|
|
|
84
106
|
const FALLBACK_TONE = {
|
|
85
|
-
text: "text-
|
|
86
|
-
bg: "bg-
|
|
87
|
-
border: "border-
|
|
107
|
+
text: "text-[var(--ks-fg-muted,#a1a1aa)]",
|
|
108
|
+
bg: "bg-[color-mix(in_srgb,var(--ks-border-strong,#3f3f46)_30%,transparent)]",
|
|
109
|
+
border: "border-[color-mix(in_srgb,var(--ks-border-strong,#3f3f46)_60%,transparent)]",
|
|
88
110
|
};
|
|
89
111
|
|
|
90
112
|
// Per-type accent tone for an account chip, or the account's own custom color.
|
package/src/utils/highlight.tsx
CHANGED
|
@@ -9,16 +9,15 @@
|
|
|
9
9
|
// still pass their own `markClass` to override.
|
|
10
10
|
|
|
11
11
|
import { type JSX } from "solid-js";
|
|
12
|
+
import { injectCSS } from "./inject-css";
|
|
12
13
|
|
|
13
14
|
const MARK_STYLE_ID = "ksui-mark-style";
|
|
14
15
|
|
|
15
16
|
function ensureMarkStyle(): void {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
style.textContent = `.ksui-mark{background-color:rgba(245,158,11,0.3);color:inherit;border-radius:2px;}`;
|
|
21
|
-
document.head.appendChild(style);
|
|
17
|
+
injectCSS(
|
|
18
|
+
MARK_STYLE_ID,
|
|
19
|
+
`.ksui-mark{background-color:color-mix(in srgb, var(--ks-warning, #f59e0b) 30%, transparent);color:inherit;border-radius:2px;}`,
|
|
20
|
+
);
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
/** Case-insensitive substring test. Empty query matches everything. */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Shared dedup so a component's CSS-in-JS string is appended to <head> once
|
|
2
|
+
// per id, no matter how many instances mount — replaces the copy-pasted
|
|
3
|
+
// createElement('style') pattern across ~14 components.
|
|
4
|
+
const injected = new Set<string>();
|
|
5
|
+
|
|
6
|
+
export function injectCSS(id: string, css: string): void {
|
|
7
|
+
if (injected.has(id) || typeof document === "undefined") return;
|
|
8
|
+
injected.add(id);
|
|
9
|
+
const style = document.createElement("style");
|
|
10
|
+
style.id = id;
|
|
11
|
+
style.textContent = css;
|
|
12
|
+
document.head.appendChild(style);
|
|
13
|
+
}
|
package/tailwind.js
CHANGED
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
/** @type {import('tailwindcss').Plugin} */
|
|
20
20
|
export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
21
21
|
// ─── Button intents ────────────────────────────────────────────────
|
|
22
|
+
// Amber/red/slate intent palette mirrors Button.tsx's own documented
|
|
23
|
+
// exception (component-authored tone system, not a 1:1 §1.2 surface/
|
|
24
|
+
// text/border mapping) — none of these rgba alpha values byte-match a
|
|
25
|
+
// §1.2 dark default, so left literal rather than guessed.
|
|
22
26
|
// Primary (amber)
|
|
23
27
|
addUtilities({
|
|
24
28
|
".ks-btn-primary": {
|
|
@@ -134,7 +138,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
134
138
|
"align-items": "center",
|
|
135
139
|
gap: "0.5rem",
|
|
136
140
|
padding: "0.375rem 0.75rem",
|
|
137
|
-
"background-color": "rgb(24 24 27)",
|
|
141
|
+
"background-color": "var(--ks-input-bg, rgb(24 24 27))",
|
|
138
142
|
border: "1px solid rgb(63 63 70 / 0.6)",
|
|
139
143
|
"border-radius": "0.25rem",
|
|
140
144
|
color: "rgb(228 228 231)",
|
|
@@ -152,7 +156,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
152
156
|
padding: "0.5rem 1rem",
|
|
153
157
|
"text-align": "left",
|
|
154
158
|
"font-weight": 500,
|
|
155
|
-
color: "rgb(161 161 170)",
|
|
159
|
+
color: "var(--ks-fg-muted, rgb(161 161 170))",
|
|
156
160
|
"font-size": "0.75rem",
|
|
157
161
|
"text-transform": "uppercase",
|
|
158
162
|
"letter-spacing": "0.05em",
|
|
@@ -168,7 +172,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
168
172
|
".ks-dt-empty": {
|
|
169
173
|
padding: "2rem",
|
|
170
174
|
"text-align": "center",
|
|
171
|
-
color: "rgb(113 113 122)",
|
|
175
|
+
color: "var(--ks-fg-subtle, rgb(113 113 122))",
|
|
172
176
|
},
|
|
173
177
|
".ks-dt-pager": {
|
|
174
178
|
display: "flex",
|
|
@@ -177,7 +181,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
177
181
|
padding: "0.75rem 1rem",
|
|
178
182
|
"border-top": "1px solid rgb(39 39 42 / 0.6)",
|
|
179
183
|
"font-size": "0.8125rem",
|
|
180
|
-
color: "rgb(161 161 170)",
|
|
184
|
+
color: "var(--ks-fg-muted, rgb(161 161 170))",
|
|
181
185
|
},
|
|
182
186
|
});
|
|
183
187
|
|
|
@@ -194,7 +198,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
194
198
|
padding: "1rem",
|
|
195
199
|
},
|
|
196
200
|
".ks-modal-card": {
|
|
197
|
-
"background-color": "rgb(24 24 27)",
|
|
201
|
+
"background-color": "var(--ks-overlay-surface, rgb(24 24 27))",
|
|
198
202
|
border: "1px solid rgb(63 63 70 / 0.6)",
|
|
199
203
|
"max-height": "calc(100vh - 2rem)",
|
|
200
204
|
overflow: "auto",
|
|
@@ -246,7 +250,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
246
250
|
"background-color": "rgba(59 130 246 / 0.1)",
|
|
247
251
|
},
|
|
248
252
|
".ks-pill-neutral": {
|
|
249
|
-
color: "rgb(161 161 170)",
|
|
253
|
+
color: "var(--ks-fg-muted, rgb(161 161 170))",
|
|
250
254
|
"background-color": "rgb(63 63 70 / 0.3)",
|
|
251
255
|
},
|
|
252
256
|
});
|
|
@@ -259,7 +263,7 @@ export default function ksuiTailwindPlugin({ addUtilities, addComponents }) {
|
|
|
259
263
|
gap: "0.25rem",
|
|
260
264
|
padding: "0.25rem 0.5rem",
|
|
261
265
|
"font-size": "0.75rem",
|
|
262
|
-
color: "rgb(161 161 170)",
|
|
266
|
+
color: "var(--ks-fg-muted, rgb(161 161 170))",
|
|
263
267
|
"background-color": "transparent",
|
|
264
268
|
border: "1px solid rgb(63 63 70 / 0.6)",
|
|
265
269
|
"border-radius": "0.25rem",
|