@miozu/jera 0.0.2 → 0.4.2
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/CLAUDE.md +734 -0
- package/README.md +219 -1
- package/llms.txt +97 -0
- package/package.json +54 -14
- package/src/actions/index.js +375 -0
- package/src/components/docs/CodeBlock.svelte +203 -0
- package/src/components/docs/DocSection.svelte +120 -0
- package/src/components/docs/PropsTable.svelte +136 -0
- package/src/components/docs/SplitPane.svelte +98 -0
- package/src/components/docs/index.js +14 -0
- package/src/components/feedback/Alert.svelte +234 -0
- package/src/components/feedback/EmptyState.svelte +179 -0
- package/src/components/feedback/ProgressBar.svelte +116 -0
- package/src/components/feedback/Skeleton.svelte +107 -0
- package/src/components/feedback/Spinner.svelte +77 -0
- package/src/components/feedback/Toast.svelte +261 -0
- package/src/components/forms/Checkbox.svelte +147 -0
- package/src/components/forms/Dropzone.svelte +248 -0
- package/src/components/forms/FileUpload.svelte +266 -0
- package/src/components/forms/IconInput.svelte +184 -0
- package/src/components/forms/Input.svelte +121 -0
- package/src/components/forms/NumberInput.svelte +225 -0
- package/src/components/forms/PinInput.svelte +169 -0
- package/src/components/forms/Radio.svelte +143 -0
- package/src/components/forms/RadioGroup.svelte +62 -0
- package/src/components/forms/RangeSlider.svelte +212 -0
- package/src/components/forms/SearchInput.svelte +175 -0
- package/src/components/forms/Select.svelte +324 -0
- package/src/components/forms/Switch.svelte +159 -0
- package/src/components/forms/Textarea.svelte +122 -0
- package/src/components/navigation/Accordion.svelte +65 -0
- package/src/components/navigation/AccordionItem.svelte +146 -0
- package/src/components/navigation/NavigationContainer.svelte +344 -0
- package/src/components/navigation/Sidebar.svelte +334 -0
- package/src/components/navigation/SidebarAccountGroup.svelte +495 -0
- package/src/components/navigation/SidebarAccountItem.svelte +492 -0
- package/src/components/navigation/SidebarGroup.svelte +230 -0
- package/src/components/navigation/SidebarGroupSwitcher.svelte +262 -0
- package/src/components/navigation/SidebarItem.svelte +210 -0
- package/src/components/navigation/SidebarNavigationItem.svelte +470 -0
- package/src/components/navigation/SidebarPopover.svelte +145 -0
- package/src/components/navigation/SidebarSearch.svelte +236 -0
- package/src/components/navigation/SidebarSection.svelte +158 -0
- package/src/components/navigation/SidebarToggle.svelte +86 -0
- package/src/components/navigation/Tabs.svelte +239 -0
- package/src/components/navigation/WorkspaceMenu.svelte +416 -0
- package/src/components/navigation/blocks/NavigationAccountGroup.svelte +396 -0
- package/src/components/navigation/blocks/NavigationCustomBlock.svelte +74 -0
- package/src/components/navigation/blocks/NavigationGroupSwitcher.svelte +277 -0
- package/src/components/navigation/blocks/NavigationSearch.svelte +300 -0
- package/src/components/navigation/blocks/NavigationSection.svelte +230 -0
- package/src/components/navigation/index.js +22 -0
- package/src/components/overlays/ConfirmDialog.svelte +272 -0
- package/src/components/overlays/Dropdown.svelte +153 -0
- package/src/components/overlays/DropdownDivider.svelte +23 -0
- package/src/components/overlays/DropdownItem.svelte +97 -0
- package/src/components/overlays/Modal.svelte +232 -0
- package/src/components/overlays/Popover.svelte +206 -0
- package/src/components/primitives/Avatar.svelte +132 -0
- package/src/components/primitives/Badge.svelte +118 -0
- package/src/components/primitives/Button.svelte +214 -0
- package/src/components/primitives/Card.svelte +104 -0
- package/src/components/primitives/Divider.svelte +105 -0
- package/src/components/primitives/LazyImage.svelte +104 -0
- package/src/components/primitives/Link.svelte +122 -0
- package/src/components/primitives/Stat.svelte +197 -0
- package/src/components/primitives/StatusBadge.svelte +122 -0
- package/src/index.js +183 -0
- package/src/tokens/colors.css +157 -0
- package/src/tokens/effects.css +128 -0
- package/src/tokens/index.css +81 -0
- package/src/tokens/spacing.css +49 -0
- package/src/tokens/typography.css +79 -0
- package/src/utils/cn.svelte.js +175 -0
- package/src/utils/highlighter.js +124 -0
- package/src/utils/index.js +22 -0
- package/src/utils/navigation.svelte.js +423 -0
- package/src/utils/reactive.svelte.js +328 -0
- package/src/utils/sidebar.svelte.js +211 -0
- package/jera.js +0 -135
- package/www/components/jera/Input/Input.svelte +0 -63
- package/www/components/jera/Input/index.js +0 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component PropsTable
|
|
3
|
+
|
|
4
|
+
Display component/function props in a clean, readable table.
|
|
5
|
+
Commonly used for API documentation.
|
|
6
|
+
|
|
7
|
+
@example
|
|
8
|
+
```svelte
|
|
9
|
+
<PropsTable props={[
|
|
10
|
+
{ name: 'variant', type: 'string', default: '"default"', description: 'Visual style' },
|
|
11
|
+
{ name: 'disabled', type: 'boolean', default: 'false', description: 'Disable interactions' },
|
|
12
|
+
{ name: 'onclick', type: 'function', required: true, description: 'Click handler' }
|
|
13
|
+
]} />
|
|
14
|
+
```
|
|
15
|
+
-->
|
|
16
|
+
<script>
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} PropDef
|
|
19
|
+
* @property {string} name - Property name
|
|
20
|
+
* @property {string} type - Type annotation
|
|
21
|
+
* @property {string} [default] - Default value
|
|
22
|
+
* @property {string} description - Description
|
|
23
|
+
* @property {boolean} [required] - Whether prop is required
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** @type {{ props: PropDef[], class?: string }} */
|
|
27
|
+
let {
|
|
28
|
+
props = [],
|
|
29
|
+
class: className = ''
|
|
30
|
+
} = $props();
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div class="jera-props-table {className}">
|
|
34
|
+
<table>
|
|
35
|
+
<thead>
|
|
36
|
+
<tr>
|
|
37
|
+
<th>Prop</th>
|
|
38
|
+
<th>Type</th>
|
|
39
|
+
<th>Default</th>
|
|
40
|
+
<th>Description</th>
|
|
41
|
+
</tr>
|
|
42
|
+
</thead>
|
|
43
|
+
<tbody>
|
|
44
|
+
{#each props as prop}
|
|
45
|
+
<tr>
|
|
46
|
+
<td class="prop-name">
|
|
47
|
+
<code>{prop.name}</code>
|
|
48
|
+
{#if prop.required}
|
|
49
|
+
<span class="required" title="Required">*</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</td>
|
|
52
|
+
<td class="prop-type">
|
|
53
|
+
<code>{prop.type}</code>
|
|
54
|
+
</td>
|
|
55
|
+
<td class="prop-default">
|
|
56
|
+
{#if prop.default !== undefined}
|
|
57
|
+
<code>{prop.default}</code>
|
|
58
|
+
{:else}
|
|
59
|
+
<span class="muted">-</span>
|
|
60
|
+
{/if}
|
|
61
|
+
</td>
|
|
62
|
+
<td class="prop-desc">{prop.description}</td>
|
|
63
|
+
</tr>
|
|
64
|
+
{/each}
|
|
65
|
+
</tbody>
|
|
66
|
+
</table>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<style>
|
|
70
|
+
.jera-props-table {
|
|
71
|
+
overflow-x: auto;
|
|
72
|
+
border: 1px solid var(--color-border, var(--base2, #2d3748));
|
|
73
|
+
border-radius: var(--radius-md, 0.5rem);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
table {
|
|
77
|
+
width: 100%;
|
|
78
|
+
border-collapse: collapse;
|
|
79
|
+
font-size: 0.875rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
th {
|
|
83
|
+
text-align: left;
|
|
84
|
+
padding: 0.75rem 1rem;
|
|
85
|
+
background: var(--color-base1, var(--base1, #1a1f26));
|
|
86
|
+
border-bottom: 1px solid var(--color-border, var(--base2, #2d3748));
|
|
87
|
+
font-weight: 500;
|
|
88
|
+
color: var(--color-base5, var(--base5, #e2e8f0));
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
td {
|
|
93
|
+
padding: 0.75rem 1rem;
|
|
94
|
+
border-bottom: 1px solid var(--color-border, var(--base2, #2d3748));
|
|
95
|
+
vertical-align: top;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
tr:last-child td {
|
|
99
|
+
border-bottom: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.prop-name code {
|
|
103
|
+
font-family: var(--font-mono, monospace);
|
|
104
|
+
font-weight: 500;
|
|
105
|
+
color: var(--color-primary, var(--magenta, #c974e6));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.required {
|
|
109
|
+
color: var(--color-error, var(--red, #eb3137));
|
|
110
|
+
margin-left: 0.25rem;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.prop-type code {
|
|
114
|
+
font-family: var(--font-mono, monospace);
|
|
115
|
+
font-size: 0.8125rem;
|
|
116
|
+
color: var(--color-base4, var(--base4, #a0aec0));
|
|
117
|
+
background: var(--color-base1, var(--base1, #1a1f26));
|
|
118
|
+
padding: 0.125rem 0.375rem;
|
|
119
|
+
border-radius: 0.25rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.prop-default code {
|
|
123
|
+
font-family: var(--font-mono, monospace);
|
|
124
|
+
font-size: 0.8125rem;
|
|
125
|
+
color: var(--color-success, var(--green, #6dd672));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.muted {
|
|
129
|
+
color: var(--color-base3, var(--base3, #4a5568));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.prop-desc {
|
|
133
|
+
color: var(--color-base4, var(--base4, #a0aec0));
|
|
134
|
+
line-height: 1.5;
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component SplitPane
|
|
3
|
+
|
|
4
|
+
Two-pane layout component for documentation with description on the left
|
|
5
|
+
and code/examples on the right. Responsive - stacks vertically on mobile.
|
|
6
|
+
|
|
7
|
+
@example
|
|
8
|
+
```svelte
|
|
9
|
+
<SplitPane>
|
|
10
|
+
{#snippet left()}
|
|
11
|
+
<h2>Function Description</h2>
|
|
12
|
+
<p>This function does something amazing...</p>
|
|
13
|
+
{/snippet}
|
|
14
|
+
{#snippet right()}
|
|
15
|
+
<CodeBlock code={exampleCode} lang="javascript" />
|
|
16
|
+
{/snippet}
|
|
17
|
+
</SplitPane>
|
|
18
|
+
```
|
|
19
|
+
-->
|
|
20
|
+
<script>
|
|
21
|
+
let {
|
|
22
|
+
/** Left pane content (description, prose) */
|
|
23
|
+
left,
|
|
24
|
+
/** Right pane content (code, examples) */
|
|
25
|
+
right,
|
|
26
|
+
/** Ratio of left:right pane widths (e.g., '1:1', '2:1', '3:2') */
|
|
27
|
+
ratio = '1:1',
|
|
28
|
+
/** Gap between panes */
|
|
29
|
+
gap = '2rem',
|
|
30
|
+
/** Minimum height */
|
|
31
|
+
minHeight = 'auto',
|
|
32
|
+
/** Whether both panes should independently scroll */
|
|
33
|
+
stickyRight = false,
|
|
34
|
+
/** Additional CSS classes */
|
|
35
|
+
class: className = ''
|
|
36
|
+
} = $props();
|
|
37
|
+
|
|
38
|
+
// Parse ratio into flex values
|
|
39
|
+
const [leftFlex, rightFlex] = $derived.by(() => {
|
|
40
|
+
const parts = ratio.split(':').map(Number);
|
|
41
|
+
return [parts[0] || 1, parts[1] || 1];
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div
|
|
46
|
+
class="jera-split-pane {className}"
|
|
47
|
+
class:sticky-right={stickyRight}
|
|
48
|
+
style:--gap={gap}
|
|
49
|
+
style:--left-flex={leftFlex}
|
|
50
|
+
style:--right-flex={rightFlex}
|
|
51
|
+
style:--min-height={minHeight}
|
|
52
|
+
>
|
|
53
|
+
<div class="pane pane-left">
|
|
54
|
+
{@render left?.()}
|
|
55
|
+
</div>
|
|
56
|
+
<div class="pane pane-right">
|
|
57
|
+
{@render right?.()}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<style>
|
|
62
|
+
.jera-split-pane {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: var(--gap);
|
|
66
|
+
min-height: var(--min-height);
|
|
67
|
+
width: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (min-width: 1024px) {
|
|
71
|
+
.jera-split-pane {
|
|
72
|
+
flex-direction: row;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.pane {
|
|
77
|
+
min-width: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.pane-left {
|
|
81
|
+
flex: var(--left-flex);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.pane-right {
|
|
85
|
+
flex: var(--right-flex);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Sticky right pane for scrollable docs */
|
|
89
|
+
@media (min-width: 1024px) {
|
|
90
|
+
.sticky-right .pane-right {
|
|
91
|
+
position: sticky;
|
|
92
|
+
top: 1rem;
|
|
93
|
+
align-self: flex-start;
|
|
94
|
+
max-height: calc(100vh - 2rem);
|
|
95
|
+
overflow-y: auto;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation components for @miozu/jera
|
|
3
|
+
*
|
|
4
|
+
* Components for building documentation sites with syntax highlighting,
|
|
5
|
+
* split-pane layouts, and structured content sections.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { CodeBlock, PropsTable, SplitPane, DocSection } from '@miozu/jera';
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { default as CodeBlock } from './CodeBlock.svelte';
|
|
12
|
+
export { default as PropsTable } from './PropsTable.svelte';
|
|
13
|
+
export { default as SplitPane } from './SplitPane.svelte';
|
|
14
|
+
export { default as DocSection } from './DocSection.svelte';
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component Alert
|
|
3
|
+
|
|
4
|
+
An inline alert/notice banner for displaying messages.
|
|
5
|
+
Different from Toast (which is transient) - Alert stays visible in the layout.
|
|
6
|
+
|
|
7
|
+
@example Basic
|
|
8
|
+
<Alert variant="info">This is an informational message.</Alert>
|
|
9
|
+
|
|
10
|
+
@example With title
|
|
11
|
+
<Alert variant="warning" title="Warning">
|
|
12
|
+
Please review your settings before continuing.
|
|
13
|
+
</Alert>
|
|
14
|
+
|
|
15
|
+
@example Dismissible
|
|
16
|
+
<Alert variant="error" dismissible onclose={() => showAlert = false}>
|
|
17
|
+
An error occurred while processing your request.
|
|
18
|
+
</Alert>
|
|
19
|
+
|
|
20
|
+
@example With icon snippet
|
|
21
|
+
<Alert variant="success">
|
|
22
|
+
{#snippet icon()}
|
|
23
|
+
<CheckIcon size={16} />
|
|
24
|
+
{/snippet}
|
|
25
|
+
Your changes have been saved successfully.
|
|
26
|
+
</Alert>
|
|
27
|
+
|
|
28
|
+
@example With actions
|
|
29
|
+
<Alert variant="info" title="New version available">
|
|
30
|
+
A new version is available for download.
|
|
31
|
+
{#snippet actions()}
|
|
32
|
+
<Button size="sm" variant="primary">Update Now</Button>
|
|
33
|
+
<Button size="sm" variant="ghost">Later</Button>
|
|
34
|
+
{/snippet}
|
|
35
|
+
</Alert>
|
|
36
|
+
-->
|
|
37
|
+
<script>
|
|
38
|
+
let {
|
|
39
|
+
variant = 'info',
|
|
40
|
+
title = '',
|
|
41
|
+
dismissible = false,
|
|
42
|
+
size = 'md',
|
|
43
|
+
class: className = '',
|
|
44
|
+
onclose,
|
|
45
|
+
icon,
|
|
46
|
+
actions,
|
|
47
|
+
children
|
|
48
|
+
} = $props();
|
|
49
|
+
|
|
50
|
+
let visible = $state(true);
|
|
51
|
+
|
|
52
|
+
function handleClose() {
|
|
53
|
+
visible = false;
|
|
54
|
+
onclose?.();
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
{#if visible}
|
|
59
|
+
<div
|
|
60
|
+
class="alert alert-{variant} alert-{size} {className}"
|
|
61
|
+
role="alert"
|
|
62
|
+
>
|
|
63
|
+
{#if icon}
|
|
64
|
+
<div class="alert-icon">
|
|
65
|
+
{@render icon()}
|
|
66
|
+
</div>
|
|
67
|
+
{:else}
|
|
68
|
+
<div class="alert-icon alert-icon-default">
|
|
69
|
+
{#if variant === 'success'}
|
|
70
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm2.354-7.354a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7 8.793l2.646-2.647a.5.5 0 0 1 .708 0z"/></svg>
|
|
71
|
+
{:else if variant === 'error'}
|
|
72
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM7.25 5v3.5a.75.75 0 0 0 1.5 0V5a.75.75 0 0 0-1.5 0zm.75 6a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/></svg>
|
|
73
|
+
{:else if variant === 'warning'}
|
|
74
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 1.5a.75.75 0 0 1 .65.38l6.25 10.75a.75.75 0 0 1-.65 1.12H1.75a.75.75 0 0 1-.65-1.12L7.35 1.88A.75.75 0 0 1 8 1.5zm0 1.73L2.57 12.25h10.86L8 3.23zM7.25 6v2.5a.75.75 0 0 0 1.5 0V6a.75.75 0 0 0-1.5 0zm.75 5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/></svg>
|
|
75
|
+
{:else}
|
|
76
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0-1.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM7.25 5a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0V5zm.75 6a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/></svg>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
{/if}
|
|
80
|
+
|
|
81
|
+
<div class="alert-content">
|
|
82
|
+
{#if title}
|
|
83
|
+
<div class="alert-title">{title}</div>
|
|
84
|
+
{/if}
|
|
85
|
+
<div class="alert-message">
|
|
86
|
+
{@render children?.()}
|
|
87
|
+
</div>
|
|
88
|
+
{#if actions}
|
|
89
|
+
<div class="alert-actions">
|
|
90
|
+
{@render actions()}
|
|
91
|
+
</div>
|
|
92
|
+
{/if}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{#if dismissible}
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
class="alert-close"
|
|
99
|
+
onclick={handleClose}
|
|
100
|
+
aria-label="Dismiss"
|
|
101
|
+
>
|
|
102
|
+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M4.28 3.22a.75.75 0 0 0-1.06 1.06L6.94 8l-3.72 3.72a.75.75 0 1 0 1.06 1.06L8 9.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L9.06 8l3.72-3.72a.75.75 0 0 0-1.06-1.06L8 6.94 4.28 3.22z"/></svg>
|
|
103
|
+
</button>
|
|
104
|
+
{/if}
|
|
105
|
+
</div>
|
|
106
|
+
{/if}
|
|
107
|
+
|
|
108
|
+
<style>
|
|
109
|
+
.alert {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: flex-start;
|
|
112
|
+
gap: var(--space-3);
|
|
113
|
+
border-radius: var(--radius-lg);
|
|
114
|
+
border: 1px solid;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Size variants */
|
|
118
|
+
.alert-sm {
|
|
119
|
+
padding: var(--space-2) var(--space-3);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.alert-md {
|
|
123
|
+
padding: var(--space-3) var(--space-4);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.alert-lg {
|
|
127
|
+
padding: var(--space-4) var(--space-5);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Variant colors */
|
|
131
|
+
.alert-info {
|
|
132
|
+
background: color-mix(in srgb, var(--color-base0D) 8%, transparent);
|
|
133
|
+
border-color: color-mix(in srgb, var(--color-base0D) 25%, transparent);
|
|
134
|
+
color: var(--color-base0D);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.alert-success {
|
|
138
|
+
background: color-mix(in srgb, var(--color-base0B) 8%, transparent);
|
|
139
|
+
border-color: color-mix(in srgb, var(--color-base0B) 25%, transparent);
|
|
140
|
+
color: var(--color-base0B);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.alert-warning {
|
|
144
|
+
background: color-mix(in srgb, var(--color-base0A) 8%, transparent);
|
|
145
|
+
border-color: color-mix(in srgb, var(--color-base0A) 25%, transparent);
|
|
146
|
+
color: var(--color-base0A);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.alert-error {
|
|
150
|
+
background: color-mix(in srgb, var(--color-base08) 8%, transparent);
|
|
151
|
+
border-color: color-mix(in srgb, var(--color-base08) 25%, transparent);
|
|
152
|
+
color: var(--color-base08);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Icon */
|
|
156
|
+
.alert-icon {
|
|
157
|
+
flex-shrink: 0;
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: center;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.alert-icon-default svg {
|
|
164
|
+
width: 16px;
|
|
165
|
+
height: 16px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.alert-lg .alert-icon-default svg {
|
|
169
|
+
width: 20px;
|
|
170
|
+
height: 20px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Content */
|
|
174
|
+
.alert-content {
|
|
175
|
+
flex: 1;
|
|
176
|
+
min-width: 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.alert-title {
|
|
180
|
+
font-weight: 600;
|
|
181
|
+
font-size: var(--text-sm);
|
|
182
|
+
margin-bottom: var(--space-1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.alert-info .alert-title { color: var(--color-base0D); }
|
|
186
|
+
.alert-success .alert-title { color: var(--color-base0B); }
|
|
187
|
+
.alert-warning .alert-title { color: var(--color-base0A); }
|
|
188
|
+
.alert-error .alert-title { color: var(--color-base08); }
|
|
189
|
+
|
|
190
|
+
.alert-message {
|
|
191
|
+
font-size: var(--text-sm);
|
|
192
|
+
color: var(--color-base05);
|
|
193
|
+
line-height: 1.5;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.alert-lg .alert-message {
|
|
197
|
+
font-size: var(--text-base);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.alert-actions {
|
|
201
|
+
display: flex;
|
|
202
|
+
gap: var(--space-2);
|
|
203
|
+
margin-top: var(--space-3);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Close button */
|
|
207
|
+
.alert-close {
|
|
208
|
+
flex-shrink: 0;
|
|
209
|
+
display: flex;
|
|
210
|
+
align-items: center;
|
|
211
|
+
justify-content: center;
|
|
212
|
+
width: 24px;
|
|
213
|
+
height: 24px;
|
|
214
|
+
padding: 0;
|
|
215
|
+
margin: calc(var(--space-1) * -1);
|
|
216
|
+
border: none;
|
|
217
|
+
border-radius: var(--radius-md);
|
|
218
|
+
background: transparent;
|
|
219
|
+
color: currentColor;
|
|
220
|
+
opacity: 0.6;
|
|
221
|
+
cursor: pointer;
|
|
222
|
+
transition: opacity 0.2s ease, background 0.2s ease;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.alert-close:hover {
|
|
226
|
+
opacity: 1;
|
|
227
|
+
background: color-mix(in srgb, currentColor 10%, transparent);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.alert-close svg {
|
|
231
|
+
width: 14px;
|
|
232
|
+
height: 14px;
|
|
233
|
+
}
|
|
234
|
+
</style>
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component EmptyState
|
|
3
|
+
|
|
4
|
+
A complete empty state UI with icon, title, description, and action buttons.
|
|
5
|
+
|
|
6
|
+
@example Basic
|
|
7
|
+
<EmptyState
|
|
8
|
+
title="No items found"
|
|
9
|
+
description="Try adjusting your search or filters"
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
@example With icon and action
|
|
13
|
+
<EmptyState
|
|
14
|
+
title="No messages"
|
|
15
|
+
description="Start a conversation to see messages here"
|
|
16
|
+
>
|
|
17
|
+
{#snippet icon()}
|
|
18
|
+
<MessageIcon size={32} />
|
|
19
|
+
{/snippet}
|
|
20
|
+
{#snippet actions()}
|
|
21
|
+
<Button variant="primary" onclick={startChat}>New Message</Button>
|
|
22
|
+
{/snippet}
|
|
23
|
+
</EmptyState>
|
|
24
|
+
|
|
25
|
+
@example Compact size
|
|
26
|
+
<EmptyState size="compact" title="No results" />
|
|
27
|
+
-->
|
|
28
|
+
<script>
|
|
29
|
+
let {
|
|
30
|
+
title = 'No data found',
|
|
31
|
+
description = '',
|
|
32
|
+
size = 'default',
|
|
33
|
+
class: className = '',
|
|
34
|
+
icon,
|
|
35
|
+
actions
|
|
36
|
+
} = $props();
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div class="empty-state empty-state-{size} {className}">
|
|
40
|
+
<div class="empty-state-content">
|
|
41
|
+
{#if icon}
|
|
42
|
+
<div class="empty-state-icon">
|
|
43
|
+
{@render icon()}
|
|
44
|
+
</div>
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
<div class="empty-state-text">
|
|
48
|
+
<h3 class="empty-state-title">{title}</h3>
|
|
49
|
+
{#if description}
|
|
50
|
+
<p class="empty-state-description">{description}</p>
|
|
51
|
+
{/if}
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{#if actions}
|
|
55
|
+
<div class="empty-state-actions">
|
|
56
|
+
{@render actions()}
|
|
57
|
+
</div>
|
|
58
|
+
{/if}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<style>
|
|
63
|
+
.empty-state {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
width: 100%;
|
|
68
|
+
padding: var(--space-16) var(--space-6);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.empty-state-compact {
|
|
72
|
+
padding: var(--space-8) var(--space-4);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.empty-state-large {
|
|
76
|
+
padding: var(--space-24) var(--space-6);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.empty-state-content {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
align-items: center;
|
|
83
|
+
text-align: center;
|
|
84
|
+
max-width: 24rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.empty-state-icon {
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
justify-content: center;
|
|
91
|
+
width: 4rem;
|
|
92
|
+
height: 4rem;
|
|
93
|
+
margin-bottom: var(--space-6);
|
|
94
|
+
border-radius: 9999px;
|
|
95
|
+
background: var(--color-base01);
|
|
96
|
+
color: var(--color-base04);
|
|
97
|
+
transition: background 0.3s ease, color 0.3s ease, transform 0.3s ease;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.empty-state-compact .empty-state-icon {
|
|
101
|
+
width: 3rem;
|
|
102
|
+
height: 3rem;
|
|
103
|
+
margin-bottom: var(--space-4);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.empty-state-large .empty-state-icon {
|
|
107
|
+
width: 5rem;
|
|
108
|
+
height: 5rem;
|
|
109
|
+
margin-bottom: var(--space-8);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.empty-state:hover .empty-state-icon {
|
|
113
|
+
background: var(--color-base02);
|
|
114
|
+
color: var(--color-base05);
|
|
115
|
+
transform: scale(1.05);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.empty-state-text {
|
|
119
|
+
margin-bottom: var(--space-6);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.empty-state-compact .empty-state-text {
|
|
123
|
+
margin-bottom: var(--space-4);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.empty-state-large .empty-state-text {
|
|
127
|
+
margin-bottom: var(--space-8);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.empty-state-title {
|
|
131
|
+
margin: 0 0 var(--space-2) 0;
|
|
132
|
+
font-size: var(--text-lg);
|
|
133
|
+
font-weight: 600;
|
|
134
|
+
color: var(--color-base07);
|
|
135
|
+
line-height: 1.3;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.empty-state-compact .empty-state-title {
|
|
139
|
+
font-size: var(--text-base);
|
|
140
|
+
font-weight: 500;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.empty-state-large .empty-state-title {
|
|
144
|
+
font-size: var(--text-xl);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.empty-state-description {
|
|
148
|
+
margin: 0;
|
|
149
|
+
font-size: var(--text-sm);
|
|
150
|
+
color: var(--color-base04);
|
|
151
|
+
line-height: 1.5;
|
|
152
|
+
max-width: 20rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.empty-state-compact .empty-state-description {
|
|
156
|
+
font-size: var(--text-xs);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.empty-state-large .empty-state-description {
|
|
160
|
+
font-size: var(--text-base);
|
|
161
|
+
max-width: 24rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.empty-state-actions {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
gap: var(--space-3);
|
|
168
|
+
flex-wrap: wrap;
|
|
169
|
+
justify-content: center;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.empty-state-compact .empty-state-actions {
|
|
173
|
+
gap: var(--space-2);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.empty-state-large .empty-state-actions {
|
|
177
|
+
gap: var(--space-4);
|
|
178
|
+
}
|
|
179
|
+
</style>
|