@zylem/ui 0.1.0 → 0.2.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/dist/chunk-VGOOWBSO.js +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +75 -2
- package/dist/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.js +1 -1
- package/package.json +13 -11
- package/src/components/Accordion/Accordion.tsx +60 -0
- package/src/components/Badge/Badge.css.ts +52 -0
- package/src/components/Badge/Badge.tsx +23 -0
- package/src/components/Button/Button.css.ts +127 -0
- package/src/components/Button/Button.tsx +34 -0
- package/src/components/Card/Card.css.ts +62 -0
- package/src/components/Card/Card.tsx +54 -0
- package/src/{global/checkbox.css.ts → components/Checkbox/Checkbox.css.ts} +23 -23
- package/src/components/Checkbox/Checkbox.tsx +41 -0
- package/src/components/Console/Console.css.ts +67 -0
- package/src/components/Console/Console.tsx +51 -0
- package/src/components/Dialog/Dialog.css.ts +86 -0
- package/src/components/Dialog/Dialog.tsx +89 -0
- package/src/components/DropdownMenu/DropdownMenu.css.ts +76 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +63 -0
- package/src/components/EmptyState/EmptyState.css.ts +29 -0
- package/src/components/EmptyState/EmptyState.tsx +32 -0
- package/src/components/Kbd/Kbd.css.ts +27 -0
- package/src/components/Kbd/Kbd.tsx +17 -0
- package/src/components/NumberField/NumberField.css.ts +47 -0
- package/src/components/NumberField/NumberField.tsx +66 -0
- package/src/components/Panel/Panel.css.ts +88 -0
- package/src/components/Panel/Panel.tsx +66 -0
- package/src/components/Progress/Progress.css.ts +51 -0
- package/src/components/Progress/Progress.tsx +43 -0
- package/src/components/Property/Property.css.ts +70 -0
- package/src/components/Property/Property.tsx +118 -0
- package/src/components/ScrollArea/ScrollArea.tsx +41 -0
- package/src/components/SearchInput/SearchInput.css.ts +25 -0
- package/src/components/SearchInput/SearchInput.tsx +31 -0
- package/src/components/Select/Select.css.ts +93 -0
- package/src/components/Select/Select.tsx +68 -0
- package/src/components/Separator/Separator.css.ts +22 -0
- package/src/components/Separator/Separator.tsx +18 -0
- package/src/components/Sidebar/Sidebar.css.ts +79 -0
- package/src/components/Sidebar/Sidebar.tsx +69 -0
- package/src/components/Slider/Slider.css.ts +91 -0
- package/src/components/Slider/Slider.tsx +51 -0
- package/src/components/Switch/Switch.css.ts +72 -0
- package/src/components/Switch/Switch.tsx +36 -0
- package/src/components/Tabs/Tabs.css.ts +70 -0
- package/src/components/Tabs/Tabs.tsx +79 -0
- package/src/components/TextField/TextField.css.ts +16 -0
- package/src/components/TextField/TextField.tsx +76 -0
- package/src/components/TitleBar/TitleBar.css.ts +46 -0
- package/src/components/TitleBar/TitleBar.tsx +60 -0
- package/src/components/Toolbar/Toolbar.css.ts +29 -0
- package/src/components/Toolbar/Toolbar.tsx +30 -0
- package/src/components/ToolbarButton/ToolbarButton.css.ts +67 -0
- package/src/components/ToolbarButton/ToolbarButton.tsx +42 -0
- package/src/components/Tooltip/Tooltip.css.ts +35 -0
- package/src/components/Tooltip/Tooltip.tsx +33 -0
- package/src/components/WindowControls/WindowControls.css.ts +59 -0
- package/src/components/WindowControls/WindowControls.tsx +51 -0
- package/src/components/index.ts +67 -6
- package/src/components/shared/field.css.ts +60 -0
- package/src/global/accordion.css.ts +32 -18
- package/src/global/common.css.ts +14 -17
- package/src/global/detached-panel.css.ts +31 -8
- package/src/global/editor-base.css.ts +96 -121
- package/src/global/entities.css.ts +19 -15
- package/src/global/hyperglass-base.css.ts +166 -0
- package/src/global/menu.css.ts +8 -3
- package/src/sprinkles.css.ts +22 -0
- package/src/styles.ts +32 -4
- package/src/theme.css.ts +190 -99
- package/dist/chunk-3R5KFBTB.js +0 -1
- package/src/components/Button.tsx +0 -19
- package/src/global/console.css.ts +0 -43
- package/src/global/sidebar.css.ts +0 -18
- package/src/global/toolbar.css.ts +0 -62
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Show, splitProps } from 'solid-js';
|
|
2
|
+
import type { JSX } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface PanelProps
|
|
5
|
+
extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
6
|
+
/** Optional glossy header title. */
|
|
7
|
+
title?: JSX.Element;
|
|
8
|
+
/** Extra content rendered on the right side of the header. */
|
|
9
|
+
headerActions?: JSX.Element;
|
|
10
|
+
/** Use the brighter window-glass treatment. */
|
|
11
|
+
window?: boolean;
|
|
12
|
+
/** Remove body padding (for consoles, lists, etc.). */
|
|
13
|
+
flush?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Drop the border, outer shadow, and background so the panel sits flush
|
|
16
|
+
* inside an already-outlined region.
|
|
17
|
+
*/
|
|
18
|
+
borderless?: boolean;
|
|
19
|
+
children?: JSX.Element;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* HyperGlass panel: glass card with a 1px electric-blue border, inner
|
|
24
|
+
* highlight, subtle blur, and an optional glossy header bar.
|
|
25
|
+
*/
|
|
26
|
+
export function Panel(props: PanelProps) {
|
|
27
|
+
const [local, rest] = splitProps(props, [
|
|
28
|
+
'title',
|
|
29
|
+
'headerActions',
|
|
30
|
+
'window',
|
|
31
|
+
'flush',
|
|
32
|
+
'borderless',
|
|
33
|
+
'children',
|
|
34
|
+
'class',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const classes = () =>
|
|
38
|
+
[
|
|
39
|
+
'zylem-panel',
|
|
40
|
+
local.window ? 'zylem-panel--window' : '',
|
|
41
|
+
local.borderless ? 'zylem-panel--borderless' : '',
|
|
42
|
+
local.class ?? '',
|
|
43
|
+
]
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.join(' ');
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div class={classes()} {...rest}>
|
|
49
|
+
<Show when={local.title || local.headerActions}>
|
|
50
|
+
<div class="zylem-panel-header">
|
|
51
|
+
<span class="zylem-panel-header-title">{local.title}</span>
|
|
52
|
+
{local.headerActions}
|
|
53
|
+
</div>
|
|
54
|
+
</Show>
|
|
55
|
+
<div
|
|
56
|
+
class={
|
|
57
|
+
local.flush
|
|
58
|
+
? 'zylem-panel-body zylem-panel-body--flush'
|
|
59
|
+
: 'zylem-panel-body'
|
|
60
|
+
}
|
|
61
|
+
>
|
|
62
|
+
{local.children}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
|
|
4
|
+
globalStyle('.zylem-progress-root', {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'column',
|
|
7
|
+
gap: vars.spacing.xxs,
|
|
8
|
+
width: '100%',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
globalStyle('.zylem-progress-header', {
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
justifyContent: 'space-between',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
globalStyle('.zylem-progress-label', {
|
|
18
|
+
fontSize: '11px',
|
|
19
|
+
letterSpacing: '0.08em',
|
|
20
|
+
textTransform: 'uppercase',
|
|
21
|
+
color: vars.colors.textSecondary,
|
|
22
|
+
fontFamily: vars.typography.fontFamily,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
globalStyle('.zylem-progress-value', {
|
|
26
|
+
fontSize: `calc(${vars.typography.fontSize} - 2px)`,
|
|
27
|
+
color: vars.colors.primary,
|
|
28
|
+
fontFamily: vars.typography.fontFamily,
|
|
29
|
+
fontVariantNumeric: 'tabular-nums',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
globalStyle('.zylem-progress-track', {
|
|
33
|
+
position: 'relative',
|
|
34
|
+
height: '10px',
|
|
35
|
+
width: '100%',
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
borderRadius: '999px',
|
|
38
|
+
border: '1px solid rgba(97, 166, 232, 0.28)',
|
|
39
|
+
background: vars.material.fieldGlass,
|
|
40
|
+
boxShadow: vars.effects.shadowInset,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
globalStyle('.zylem-progress-fill', {
|
|
44
|
+
height: '100%',
|
|
45
|
+
borderRadius: '999px',
|
|
46
|
+
background:
|
|
47
|
+
'linear-gradient(180deg, rgba(178, 219, 255, 0.7), rgba(48, 137, 220, 0.92))',
|
|
48
|
+
boxShadow: `inset 0 1px 0 rgba(255,255,255,0.5), ${vars.effects.glowPrimary}`,
|
|
49
|
+
transition: `width ${vars.motion.normal} ${vars.motion.easeOut}`,
|
|
50
|
+
width: 'var(--kb-progress-fill-width)',
|
|
51
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Progress as KProgress } from '@kobalte/core';
|
|
2
|
+
import { Show } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface ProgressProps {
|
|
5
|
+
value?: number;
|
|
6
|
+
minValue?: number;
|
|
7
|
+
maxValue?: number;
|
|
8
|
+
label?: string;
|
|
9
|
+
showValue?: boolean;
|
|
10
|
+
indeterminate?: boolean;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* HyperGlass progress bar: inset glass track with a glowing blue glass fill.
|
|
16
|
+
*/
|
|
17
|
+
export function Progress(props: ProgressProps) {
|
|
18
|
+
return (
|
|
19
|
+
<KProgress.Root
|
|
20
|
+
class={props.class ? `zylem-progress-root ${props.class}` : 'zylem-progress-root'}
|
|
21
|
+
value={props.value}
|
|
22
|
+
minValue={props.minValue}
|
|
23
|
+
maxValue={props.maxValue}
|
|
24
|
+
indeterminate={props.indeterminate}
|
|
25
|
+
>
|
|
26
|
+
<Show when={props.label || props.showValue}>
|
|
27
|
+
<div class="zylem-progress-header">
|
|
28
|
+
<Show when={props.label}>
|
|
29
|
+
<KProgress.Label class="zylem-progress-label">
|
|
30
|
+
{props.label}
|
|
31
|
+
</KProgress.Label>
|
|
32
|
+
</Show>
|
|
33
|
+
<Show when={props.showValue}>
|
|
34
|
+
<KProgress.ValueLabel class="zylem-progress-value" />
|
|
35
|
+
</Show>
|
|
36
|
+
</div>
|
|
37
|
+
</Show>
|
|
38
|
+
<KProgress.Track class="zylem-progress-track">
|
|
39
|
+
<KProgress.Fill class="zylem-progress-fill" />
|
|
40
|
+
</KProgress.Track>
|
|
41
|
+
</KProgress.Root>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HyperCard-style field list — owns the `.zylem-property-*` classes used by
|
|
6
|
+
* the editor.
|
|
7
|
+
*/
|
|
8
|
+
globalStyle('.zylem-property-list', {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
flexDirection: 'column',
|
|
11
|
+
borderRadius: vars.radii.control,
|
|
12
|
+
border: '1px solid rgba(97, 166, 232, 0.18)',
|
|
13
|
+
background: 'rgba(8, 19, 31, 0.35)',
|
|
14
|
+
overflow: 'hidden',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
globalStyle('.zylem-property-row', {
|
|
18
|
+
minHeight: '30px',
|
|
19
|
+
display: 'grid',
|
|
20
|
+
gridTemplateColumns: '112px 1fr',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
gap: vars.spacing.sm,
|
|
23
|
+
padding: `${vars.spacing.xs} ${vars.spacing.sm}`,
|
|
24
|
+
borderBottom: '1px solid rgba(97,166,232,0.12)',
|
|
25
|
+
background: 'linear-gradient(90deg, rgba(97,166,232,0.045), transparent 45%)',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
globalStyle('.zylem-property-row:last-child', {
|
|
29
|
+
borderBottom: 'none',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
globalStyle('.zylem-property-row:hover', {
|
|
33
|
+
background:
|
|
34
|
+
'linear-gradient(90deg, rgba(97,166,232,0.13), rgba(97,166,232,0.025) 65%)',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
globalStyle('.zylem-property-label', {
|
|
38
|
+
fontSize: '11px',
|
|
39
|
+
letterSpacing: '0.08em',
|
|
40
|
+
textTransform: 'uppercase',
|
|
41
|
+
color: vars.colors.textSecondary,
|
|
42
|
+
fontFamily: vars.typography.fontFamily,
|
|
43
|
+
overflow: 'hidden',
|
|
44
|
+
textOverflow: 'ellipsis',
|
|
45
|
+
whiteSpace: 'nowrap',
|
|
46
|
+
userSelect: 'none',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
globalStyle('.zylem-property-value', {
|
|
50
|
+
color: vars.colors.primary,
|
|
51
|
+
fontSize: vars.typography.fontSize,
|
|
52
|
+
fontFamily: vars.typography.fontFamily,
|
|
53
|
+
fontWeight: 500,
|
|
54
|
+
minWidth: 0,
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
textOverflow: 'ellipsis',
|
|
57
|
+
whiteSpace: 'nowrap',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
globalStyle('.zylem-property-value--clickable', {
|
|
61
|
+
cursor: 'pointer',
|
|
62
|
+
textDecoration: 'underline',
|
|
63
|
+
textDecorationStyle: 'dotted',
|
|
64
|
+
textUnderlineOffset: '2px',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
globalStyle('.zylem-property-value--clickable:hover', {
|
|
68
|
+
textDecorationStyle: 'solid',
|
|
69
|
+
color: vars.colors.active,
|
|
70
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Show, createMemo } from 'solid-js';
|
|
2
|
+
import type { JSX } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface PropertyListProps {
|
|
5
|
+
children: JSX.Element;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* HyperCard-style field list: wraps `PropertyRow`s in a bordered inset stack.
|
|
11
|
+
*/
|
|
12
|
+
export function PropertyList(props: PropertyListProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
class={
|
|
16
|
+
props.class
|
|
17
|
+
? `zylem-property-list ${props.class}`
|
|
18
|
+
: 'zylem-property-list'
|
|
19
|
+
}
|
|
20
|
+
>
|
|
21
|
+
{props.children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PropertyRowProps {
|
|
27
|
+
label: string;
|
|
28
|
+
/** The value to display. Can be a string, number, or JSX element. */
|
|
29
|
+
value?: string | number | JSX.Element;
|
|
30
|
+
/** If true, treat string values as paths and truncate long ones. */
|
|
31
|
+
isPath?: boolean;
|
|
32
|
+
/** Maximum path segments to show (default: 3). */
|
|
33
|
+
maxPathSegments?: number;
|
|
34
|
+
/** Called with the full value when a truncated path is clicked. */
|
|
35
|
+
onPathClick?: (fullValue: string) => void;
|
|
36
|
+
valueClass?: string;
|
|
37
|
+
/** Children can be used instead of value for complex content. */
|
|
38
|
+
children?: JSX.Element;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function truncatePath(
|
|
42
|
+
path: string,
|
|
43
|
+
maxSegments = 3,
|
|
44
|
+
): { truncated: string; isTruncated: boolean } {
|
|
45
|
+
const separator = path.includes('\\') ? '\\' : '/';
|
|
46
|
+
const segments = path.split(separator).filter(Boolean);
|
|
47
|
+
if (segments.length <= maxSegments) {
|
|
48
|
+
return { truncated: path, isTruncated: false };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
truncated: `...${separator}${segments.slice(-maxSegments).join(separator)}`,
|
|
52
|
+
isTruncated: true,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function looksLikePath(value: string): boolean {
|
|
57
|
+
return (
|
|
58
|
+
value.includes('/') ||
|
|
59
|
+
value.includes('\\') ||
|
|
60
|
+
/^[A-Za-z]:/.test(value) ||
|
|
61
|
+
value.startsWith('.') ||
|
|
62
|
+
value.startsWith('~')
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* HyperGlass property row: label/value inspector field with automatic path
|
|
68
|
+
* truncation and click-to-reveal.
|
|
69
|
+
*/
|
|
70
|
+
export function PropertyRow(props: PropertyRowProps) {
|
|
71
|
+
const display = createMemo(() => {
|
|
72
|
+
if (props.children !== undefined) {
|
|
73
|
+
return { element: props.children, isClickable: false, fullValue: null as string | null };
|
|
74
|
+
}
|
|
75
|
+
if (typeof props.value !== 'string') {
|
|
76
|
+
return { element: props.value, isClickable: false, fullValue: null as string | null };
|
|
77
|
+
}
|
|
78
|
+
const stringValue = props.value;
|
|
79
|
+
const shouldTruncate = props.isPath ?? looksLikePath(stringValue);
|
|
80
|
+
if (shouldTruncate) {
|
|
81
|
+
const { truncated, isTruncated } = truncatePath(
|
|
82
|
+
stringValue,
|
|
83
|
+
props.maxPathSegments ?? 3,
|
|
84
|
+
);
|
|
85
|
+
return {
|
|
86
|
+
element: truncated,
|
|
87
|
+
isClickable: isTruncated && !!props.onPathClick,
|
|
88
|
+
fullValue: isTruncated ? stringValue : null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { element: stringValue, isClickable: false, fullValue: null as string | null };
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div class="zylem-property-row">
|
|
96
|
+
<span class="zylem-property-label">{props.label}</span>
|
|
97
|
+
<Show
|
|
98
|
+
when={display().isClickable}
|
|
99
|
+
fallback={
|
|
100
|
+
<span class={`zylem-property-value ${props.valueClass ?? ''}`}>
|
|
101
|
+
{display().element}
|
|
102
|
+
</span>
|
|
103
|
+
}
|
|
104
|
+
>
|
|
105
|
+
<span
|
|
106
|
+
class={`zylem-property-value zylem-property-value--clickable ${props.valueClass ?? ''}`}
|
|
107
|
+
onClick={() => {
|
|
108
|
+
const fullValue = display().fullValue;
|
|
109
|
+
if (fullValue) props.onPathClick?.(fullValue);
|
|
110
|
+
}}
|
|
111
|
+
title="Click to reveal full path"
|
|
112
|
+
>
|
|
113
|
+
{display().element}
|
|
114
|
+
</span>
|
|
115
|
+
</Show>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { splitProps } from 'solid-js';
|
|
2
|
+
import type { JSX } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface ScrollAreaProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Scroll direction; defaults to vertical. */
|
|
6
|
+
direction?: 'vertical' | 'horizontal' | 'both';
|
|
7
|
+
children?: JSX.Element;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HyperGlass scroll area: wrapper that opts into the visible Aqua-glass
|
|
12
|
+
* scrollbar rail (`.zylem-scroll`, defined in hyperglass-base.css.ts).
|
|
13
|
+
*/
|
|
14
|
+
export function ScrollArea(props: ScrollAreaProps) {
|
|
15
|
+
const [local, rest] = splitProps(props, ['direction', 'children', 'class', 'style']);
|
|
16
|
+
|
|
17
|
+
const overflow = () => {
|
|
18
|
+
switch (local.direction) {
|
|
19
|
+
case 'horizontal':
|
|
20
|
+
return { 'overflow-x': 'auto', 'overflow-y': 'hidden' } as const;
|
|
21
|
+
case 'both':
|
|
22
|
+
return { overflow: 'auto' } as const;
|
|
23
|
+
default:
|
|
24
|
+
return { 'overflow-y': 'auto', 'overflow-x': 'hidden' } as const;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
class={local.class ? `zylem-scroll ${local.class}` : 'zylem-scroll'}
|
|
31
|
+
style={{
|
|
32
|
+
...overflow(),
|
|
33
|
+
'min-height': 0,
|
|
34
|
+
...(typeof local.style === 'object' ? local.style : {}),
|
|
35
|
+
}}
|
|
36
|
+
{...rest}
|
|
37
|
+
>
|
|
38
|
+
{local.children}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
import '../shared/field.css';
|
|
4
|
+
|
|
5
|
+
globalStyle('.zylem-search', {
|
|
6
|
+
position: 'relative',
|
|
7
|
+
display: 'flex',
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
width: '100%',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
globalStyle('.zylem-search-icon', {
|
|
13
|
+
position: 'absolute',
|
|
14
|
+
left: vars.spacing.sm,
|
|
15
|
+
width: '14px',
|
|
16
|
+
height: '14px',
|
|
17
|
+
color: vars.colors.textSecondary,
|
|
18
|
+
pointerEvents: 'none',
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
globalStyle('.zylem-search-input', {
|
|
22
|
+
width: '100%',
|
|
23
|
+
height: '28px',
|
|
24
|
+
paddingLeft: '28px',
|
|
25
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Search from 'lucide-solid/icons/search';
|
|
2
|
+
import type { JSX } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface SearchInputProps {
|
|
5
|
+
value?: string;
|
|
6
|
+
onInput?: (value: string) => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
class?: string;
|
|
9
|
+
'aria-label'?: string;
|
|
10
|
+
onKeyDown?: JSX.EventHandlerUnion<HTMLInputElement, KeyboardEvent>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* HyperGlass search input: inset glass field with a leading search icon.
|
|
15
|
+
*/
|
|
16
|
+
export function SearchInput(props: SearchInputProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div class={props.class ? `zylem-search ${props.class}` : 'zylem-search'}>
|
|
19
|
+
<Search class="zylem-search-icon" />
|
|
20
|
+
<input
|
|
21
|
+
type="search"
|
|
22
|
+
class="zylem-field zylem-search-input"
|
|
23
|
+
value={props.value ?? ''}
|
|
24
|
+
placeholder={props.placeholder ?? 'Search...'}
|
|
25
|
+
aria-label={props['aria-label'] ?? props.placeholder ?? 'Search'}
|
|
26
|
+
onInput={(event) => props.onInput?.(event.currentTarget.value)}
|
|
27
|
+
onKeyDown={props.onKeyDown}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
import '../shared/field.css';
|
|
4
|
+
|
|
5
|
+
globalStyle('.zylem-select-trigger', {
|
|
6
|
+
display: 'inline-flex',
|
|
7
|
+
alignItems: 'center',
|
|
8
|
+
justifyContent: 'space-between',
|
|
9
|
+
gap: vars.spacing.sm,
|
|
10
|
+
width: '100%',
|
|
11
|
+
height: '28px',
|
|
12
|
+
cursor: 'pointer',
|
|
13
|
+
textAlign: 'left',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
globalStyle('.zylem-select-value', {
|
|
17
|
+
overflow: 'hidden',
|
|
18
|
+
textOverflow: 'ellipsis',
|
|
19
|
+
whiteSpace: 'nowrap',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
globalStyle('.zylem-select-icon', {
|
|
23
|
+
flexShrink: 0,
|
|
24
|
+
fontSize: '9px',
|
|
25
|
+
color: vars.colors.primary,
|
|
26
|
+
transition: `transform ${vars.motion.fast} ${vars.motion.easeOut}`,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
globalStyle('.zylem-select-trigger[data-expanded] .zylem-select-icon', {
|
|
30
|
+
transform: 'rotate(180deg)',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
globalStyle('.zylem-select-content', {
|
|
34
|
+
zIndex: 10000,
|
|
35
|
+
borderRadius: vars.radii.control,
|
|
36
|
+
border: '1px solid rgba(97, 166, 232, 0.45)',
|
|
37
|
+
background: vars.material.glassPanelDark,
|
|
38
|
+
boxShadow: vars.effects.shadowPanel,
|
|
39
|
+
backdropFilter: `blur(${vars.effects.blurMd}) saturate(1.22)`,
|
|
40
|
+
overflow: 'hidden',
|
|
41
|
+
'@supports': {
|
|
42
|
+
'not (backdrop-filter: blur(1px))': {
|
|
43
|
+
background: vars.colors.surface,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
globalStyle('.zylem-select-listbox', {
|
|
49
|
+
maxHeight: '280px',
|
|
50
|
+
overflowY: 'auto',
|
|
51
|
+
padding: vars.spacing.xs,
|
|
52
|
+
margin: 0,
|
|
53
|
+
display: 'flex',
|
|
54
|
+
flexDirection: 'column',
|
|
55
|
+
gap: '2px',
|
|
56
|
+
outline: 'none',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
globalStyle('.zylem-select-item', {
|
|
60
|
+
display: 'flex',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
justifyContent: 'space-between',
|
|
63
|
+
gap: vars.spacing.sm,
|
|
64
|
+
padding: `${vars.spacing.xs} ${vars.spacing.sm}`,
|
|
65
|
+
borderRadius: '4px',
|
|
66
|
+
color: vars.colors.text,
|
|
67
|
+
fontFamily: vars.typography.fontFamily,
|
|
68
|
+
fontSize: vars.typography.fontSize,
|
|
69
|
+
cursor: 'pointer',
|
|
70
|
+
userSelect: 'none',
|
|
71
|
+
outline: 'none',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
globalStyle('.zylem-select-item[data-highlighted]', {
|
|
75
|
+
background: 'rgba(97, 166, 232, 0.22)',
|
|
76
|
+
color: '#F4FAFF',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
globalStyle('.zylem-select-item[data-selected]', {
|
|
80
|
+
background: vars.material.buttonGlass,
|
|
81
|
+
boxShadow: vars.effects.shadowButton,
|
|
82
|
+
color: '#F4FAFF',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
globalStyle('.zylem-select-item[data-disabled]', {
|
|
86
|
+
opacity: 0.45,
|
|
87
|
+
cursor: 'not-allowed',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
globalStyle('.zylem-select-item-indicator', {
|
|
91
|
+
color: vars.colors.active,
|
|
92
|
+
fontSize: '11px',
|
|
93
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Select as KSelect } from '@kobalte/core';
|
|
2
|
+
import { Show } from 'solid-js';
|
|
3
|
+
|
|
4
|
+
export interface SelectOption {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SelectProps {
|
|
11
|
+
options: SelectOption[];
|
|
12
|
+
value?: string;
|
|
13
|
+
onChange?: (value: string | null) => void;
|
|
14
|
+
label?: string;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
name?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* HyperGlass select: inset glass trigger opening a floating glass sheet.
|
|
23
|
+
*/
|
|
24
|
+
export function Select(props: SelectProps) {
|
|
25
|
+
const selected = () =>
|
|
26
|
+
props.options.find((option) => option.value === props.value) ?? null;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<KSelect.Root<SelectOption>
|
|
30
|
+
class={props.class ? `zylem-field-root ${props.class}` : 'zylem-field-root'}
|
|
31
|
+
options={props.options}
|
|
32
|
+
optionValue="value"
|
|
33
|
+
optionTextValue="label"
|
|
34
|
+
optionDisabled="disabled"
|
|
35
|
+
value={selected()}
|
|
36
|
+
onChange={(option) => props.onChange?.(option?.value ?? null)}
|
|
37
|
+
placeholder={props.placeholder}
|
|
38
|
+
disabled={props.disabled}
|
|
39
|
+
name={props.name}
|
|
40
|
+
itemComponent={(itemProps) => (
|
|
41
|
+
<KSelect.Item item={itemProps.item} class="zylem-select-item">
|
|
42
|
+
<KSelect.ItemLabel>{itemProps.item.rawValue.label}</KSelect.ItemLabel>
|
|
43
|
+
<KSelect.ItemIndicator class="zylem-select-item-indicator">
|
|
44
|
+
✓
|
|
45
|
+
</KSelect.ItemIndicator>
|
|
46
|
+
</KSelect.Item>
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
<Show when={props.label}>
|
|
50
|
+
<KSelect.Label class="zylem-field-label">{props.label}</KSelect.Label>
|
|
51
|
+
</Show>
|
|
52
|
+
<KSelect.Trigger
|
|
53
|
+
class="zylem-field zylem-select-trigger"
|
|
54
|
+
aria-label={props.label}
|
|
55
|
+
>
|
|
56
|
+
<KSelect.Value<SelectOption> class="zylem-select-value">
|
|
57
|
+
{(state) => state.selectedOption()?.label}
|
|
58
|
+
</KSelect.Value>
|
|
59
|
+
<KSelect.Icon class="zylem-select-icon">▼</KSelect.Icon>
|
|
60
|
+
</KSelect.Trigger>
|
|
61
|
+
<KSelect.Portal>
|
|
62
|
+
<KSelect.Content class="zylem-select-content">
|
|
63
|
+
<KSelect.Listbox class="zylem-select-listbox zylem-scroll" />
|
|
64
|
+
</KSelect.Content>
|
|
65
|
+
</KSelect.Portal>
|
|
66
|
+
</KSelect.Root>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
|
|
4
|
+
globalStyle('.zylem-separator', {
|
|
5
|
+
border: 'none',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
globalStyle('.zylem-separator[data-orientation="horizontal"]', {
|
|
9
|
+
height: '1px',
|
|
10
|
+
width: '100%',
|
|
11
|
+
margin: `${vars.spacing.sm} 0`,
|
|
12
|
+
background:
|
|
13
|
+
'linear-gradient(90deg, transparent, rgba(97, 166, 232, 0.4), transparent)',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
globalStyle('.zylem-separator[data-orientation="vertical"]', {
|
|
17
|
+
width: '1px',
|
|
18
|
+
alignSelf: 'stretch',
|
|
19
|
+
margin: `0 ${vars.spacing.sm}`,
|
|
20
|
+
background:
|
|
21
|
+
'linear-gradient(180deg, transparent, rgba(97, 166, 232, 0.4), transparent)',
|
|
22
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Separator as KSeparator } from '@kobalte/core';
|
|
2
|
+
|
|
3
|
+
export interface SeparatorProps {
|
|
4
|
+
orientation?: 'horizontal' | 'vertical';
|
|
5
|
+
class?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HyperGlass separator: soft electric-blue gradient rule.
|
|
10
|
+
*/
|
|
11
|
+
export function Separator(props: SeparatorProps) {
|
|
12
|
+
return (
|
|
13
|
+
<KSeparator.Root
|
|
14
|
+
class={props.class ? `zylem-separator ${props.class}` : 'zylem-separator'}
|
|
15
|
+
orientation={props.orientation}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|