@rokkit/ui 1.0.0-next.125 → 1.0.0-next.127

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.
Files changed (146) hide show
  1. package/README.md +198 -101
  2. package/package.json +52 -34
  3. package/src/components/BreadCrumbs.svelte +82 -0
  4. package/src/components/Button.svelte +87 -0
  5. package/src/components/ButtonGroup.svelte +18 -0
  6. package/src/components/Card.svelte +61 -0
  7. package/src/components/Carousel.svelte +169 -0
  8. package/src/components/Code.svelte +185 -0
  9. package/src/components/Connector.svelte +46 -0
  10. package/src/components/FloatingAction.svelte +331 -0
  11. package/src/components/FloatingNavigation.svelte +228 -0
  12. package/src/components/ItemContent.svelte +24 -0
  13. package/src/components/List.svelte +476 -0
  14. package/src/components/Menu.svelte +421 -0
  15. package/src/components/MultiSelect.svelte +521 -0
  16. package/src/components/PaletteManager.svelte +354 -0
  17. package/src/components/Pill.svelte +78 -0
  18. package/src/components/ProgressBar.svelte +31 -0
  19. package/src/components/Range.svelte +325 -0
  20. package/src/components/Rating.svelte +91 -0
  21. package/src/components/Reveal.svelte +58 -0
  22. package/src/components/SearchFilter.svelte +80 -0
  23. package/src/components/Select.svelte +585 -0
  24. package/src/{Shine.svelte → components/Shine.svelte} +29 -21
  25. package/src/components/Stepper.svelte +169 -0
  26. package/src/components/Switch.svelte +75 -0
  27. package/src/components/Table.svelte +243 -0
  28. package/src/components/Tabs.svelte +268 -0
  29. package/src/components/Tilt.svelte +68 -0
  30. package/src/components/Timeline.svelte +61 -0
  31. package/src/components/Toggle.svelte +157 -0
  32. package/src/components/Toolbar.svelte +307 -0
  33. package/src/components/ToolbarGroup.svelte +17 -0
  34. package/src/components/Tree.svelte +613 -0
  35. package/src/components/index.ts +33 -0
  36. package/src/index.ts +41 -0
  37. package/src/types/button.ts +83 -0
  38. package/src/types/code.ts +46 -0
  39. package/src/types/floating-action.ts +118 -0
  40. package/src/types/floating-navigation.ts +68 -0
  41. package/src/types/index.ts +53 -0
  42. package/src/types/item-proxy.ts +358 -0
  43. package/src/types/list.ts +196 -0
  44. package/src/types/menu.ts +195 -0
  45. package/src/types/palette.ts +143 -0
  46. package/src/types/range.ts +51 -0
  47. package/src/types/search-filter.ts +67 -0
  48. package/src/types/select.ts +206 -0
  49. package/src/types/switch.ts +64 -0
  50. package/src/types/table.ts +210 -0
  51. package/src/types/tabs.ts +124 -0
  52. package/src/types/timeline.ts +51 -0
  53. package/src/types/toggle.ts +109 -0
  54. package/src/types/toolbar.ts +164 -0
  55. package/src/types/tree.ts +259 -0
  56. package/src/utils/palette.ts +582 -0
  57. package/src/utils/shiki.ts +122 -0
  58. package/dist/constants.d.ts +0 -2
  59. package/dist/index.d.ts +0 -41
  60. package/dist/lib/fields.d.ts +0 -16
  61. package/dist/lib/form.d.ts +0 -95
  62. package/dist/lib/index.d.ts +0 -6
  63. package/dist/lib/layout.d.ts +0 -7
  64. package/dist/lib/nested.d.ts +0 -48
  65. package/dist/lib/schema.d.ts +0 -7
  66. package/dist/lib/select.d.ts +0 -8
  67. package/dist/lib/tree.d.ts +0 -9
  68. package/dist/tree/List.spec.svelte.d.ts +0 -1
  69. package/dist/tree/Node.spec.svelte.d.ts +0 -1
  70. package/dist/tree/Root.spec.svelte.d.ts +0 -1
  71. package/dist/types.d.ts +0 -5
  72. package/dist/wrappers/index.d.ts +0 -3
  73. package/src/Accordion.svelte +0 -118
  74. package/src/BreadCrumbs.svelte +0 -32
  75. package/src/Button.svelte +0 -57
  76. package/src/Calendar.svelte +0 -93
  77. package/src/Card.svelte +0 -45
  78. package/src/Carousel.svelte +0 -49
  79. package/src/CheckBox.svelte +0 -56
  80. package/src/Connector.svelte +0 -40
  81. package/src/DropDown.svelte +0 -68
  82. package/src/DropSearch.svelte +0 -37
  83. package/src/Fillable.svelte +0 -19
  84. package/src/GraphPaper.svelte +0 -43
  85. package/src/Icon.svelte +0 -81
  86. package/src/Item.svelte +0 -25
  87. package/src/Link.svelte +0 -21
  88. package/src/List.svelte +0 -89
  89. package/src/ListBody.svelte +0 -43
  90. package/src/Message.svelte +0 -11
  91. package/src/MultiSelect.svelte +0 -48
  92. package/src/NestedList.svelte +0 -78
  93. package/src/NestedPaginator.svelte +0 -63
  94. package/src/Node.svelte +0 -76
  95. package/src/Overlay.svelte +0 -21
  96. package/src/PageNavigator.svelte +0 -94
  97. package/src/PickOne.svelte +0 -60
  98. package/src/Pill.svelte +0 -41
  99. package/src/ProgressBar.svelte +0 -21
  100. package/src/ProgressDots.svelte +0 -53
  101. package/src/RadioGroup.svelte +0 -52
  102. package/src/Range.svelte +0 -45
  103. package/src/RangeMinMax.svelte +0 -124
  104. package/src/RangeSlider.svelte +0 -79
  105. package/src/RangeTick.svelte +0 -28
  106. package/src/Rating.svelte +0 -95
  107. package/src/ResponsiveGrid.svelte +0 -88
  108. package/src/Scrollable.svelte +0 -7
  109. package/src/Select.svelte +0 -114
  110. package/src/Separator.svelte +0 -1
  111. package/src/Slider.svelte +0 -14
  112. package/src/SlidingColumns.svelte +0 -50
  113. package/src/Stage.svelte +0 -41
  114. package/src/Stepper.svelte +0 -66
  115. package/src/Summary.svelte +0 -22
  116. package/src/Switch.svelte +0 -106
  117. package/src/TableCell.svelte +0 -51
  118. package/src/TableHeaderCell.svelte +0 -54
  119. package/src/Tabs.svelte +0 -176
  120. package/src/Tilt.svelte +0 -66
  121. package/src/Toggle.svelte +0 -58
  122. package/src/ToggleThemeMode.svelte +0 -23
  123. package/src/Tree.svelte +0 -80
  124. package/src/TreeTable.svelte +0 -171
  125. package/src/ValidationReport.svelte +0 -23
  126. package/src/constants.js +0 -4
  127. package/src/index.js +0 -48
  128. package/src/lib/fields.js +0 -118
  129. package/src/lib/form.js +0 -72
  130. package/src/lib/index.js +0 -13
  131. package/src/lib/layout.js +0 -63
  132. package/src/lib/nested.js +0 -192
  133. package/src/lib/schema.js +0 -32
  134. package/src/lib/select.js +0 -38
  135. package/src/lib/tree.js +0 -22
  136. package/src/tree/List.spec.svelte.js +0 -84
  137. package/src/tree/List.svelte +0 -78
  138. package/src/tree/Node.spec.svelte.js +0 -104
  139. package/src/tree/Node.svelte +0 -80
  140. package/src/tree/Root.spec.svelte.js +0 -63
  141. package/src/tree/Root.svelte +0 -81
  142. package/src/types.js +0 -9
  143. package/src/wrappers/Category.svelte +0 -27
  144. package/src/wrappers/Section.svelte +0 -16
  145. package/src/wrappers/Wrapper.svelte +0 -12
  146. package/src/wrappers/index.js +0 -3
package/README.md CHANGED
@@ -1,123 +1,220 @@
1
1
  # @rokkit/ui
2
2
 
3
- A collection of data-driven UI components for Svelte applications that improve developer experience.
3
+ Data driven UI components for Rokkit applications.
4
4
 
5
5
  ## Installation
6
6
 
7
- ```sh
8
- npm install @rokkit/ui
7
+ ```bash
8
+ bun add @rokkit/ui
9
9
  ```
10
10
 
11
- or
11
+ ## Components
12
+
13
+ ### Menu
12
14
 
13
- ```sh
14
- bun add @rokkit/ui
15
+ A flexible, data-driven dropdown menu component with support for:
16
+
17
+ - Flat or grouped menu items
18
+ - Custom field mapping for any data structure
19
+ - Icons, descriptions, and disabled states
20
+ - Size variants (sm, md, lg)
21
+ - Keyboard navigation
22
+ - Full accessibility (ARIA)
23
+
24
+ ```svelte
25
+ <script>
26
+ import { Menu } from '@rokkit/ui'
27
+
28
+ const options = [
29
+ { text: 'Copy', icon: 'i-solar:copy-bold', value: 'copy' },
30
+ { text: 'Paste', icon: 'i-solar:clipboard-bold', value: 'paste' },
31
+ { text: 'Delete', icon: 'i-solar:trash-bold', value: 'delete', disabled: true }
32
+ ]
33
+
34
+ function handleSelect(value, item) {
35
+ console.log('Selected:', value, item)
36
+ }
37
+ </script>
38
+
39
+ <Menu {options} label="Actions" icon="i-solar:menu-dots-bold" onselect={handleSelect} />
15
40
  ```
16
41
 
17
- ## Components
42
+ #### Grouped Options
18
43
 
19
- ### Basic Components
20
-
21
- - **Button** - Versatile button component with support for variants, icons, and different button types
22
- - **Icon** - Display icons with consistent styling
23
- - **Item** - Base component for displaying individual items within lists
24
- - **Pill** - Compact label component for status indicators or tags
25
- - **ProgressBar** - Visual indicator of progress or completion
26
- - **Separator** - Visual divider between content sections
27
- - **Connector** - Visual connector between elements
28
- - **Summary** - Display summary information
29
- - **ValidationReport** - Display validation results and error messages
30
-
31
- ### Layout Components
32
-
33
- - **Accordion** - Collapsible content panels
34
- - **Card** - Container for displaying content with consistent styling
35
- - **ResponsiveGrid** - Grid layout that adjusts based on screen size
36
- - **SlidingColumns** - Multi-column layout with sliding navigation
37
- - **Tabs** - Tabbed interface for organizing content
38
- - **Overlay** - Modal overlay for dialogs and popups
39
- - **Message** - Styled message display
40
-
41
- ### Navigation Components
42
-
43
- - **BreadCrumbs** - Navigation breadcrumb trail
44
- - **Link** - Styled link component
45
- - **PageNavigator** - Component for paginating through content
46
- - **NestedPaginator** - Pagination for nested data structures
47
-
48
- ### Form Components
49
-
50
- - **InputField** - Base component for form inputs
51
- - **Form** - Container for form elements with built-in validation
52
- - **FieldLayout** - Layout component for form fields
53
- - **CheckBox** - Checkbox input component
54
- - **RadioGroup** - Group of radio button options
55
- - **Toggle** - Toggle switch component
56
- - **Switch** - Switch control component
57
- - **Select** - Dropdown select component
58
- - **MultiSelect** - Multiple selection dropdown component
59
- - **DropDown** - Dropdown menu component
60
- - **DropSearch** - Searchable dropdown component
61
- - **Range** - Range selection component
62
- - **RangeMinMax** - Range with minimum and maximum values
63
- - **RangeSlider** - Slider control for selecting from a range
64
- - **RangeTick** - Range with tick marks
65
- - **Calendar** - Date selection calendar
66
- - **Rating** - Star rating component
67
-
68
- ### Data Components
69
-
70
- - **DataEditor** - Component for editing structured data
71
- - **ListEditor** - Component for editing lists
72
- - **NestedEditor** - Component for editing nested data structures
73
- - **List** - Display a list of items
74
- - **ListBody** - Container for list items
75
- - **NestedList** - Display nested/hierarchical lists
76
- - **Table** - Table component for structured data (alias for TreeTable)
77
- - **TreeTable** - Table component with support for hierarchical data
78
- - **Tree** - Tree view for hierarchical data
79
- - **Node** - Individual node within a tree
80
-
81
- ### Progress & Navigation Components
82
-
83
- - **Stepper** - Step-by-step process indicator
84
- - **ProgressDots** - Progress indicator using dots
85
- - **Carousel** - Image or content carousel
86
-
87
- ### Visual Effect Components
88
-
89
- - **Shine** - Add shine effect to elements
90
- - **Tilt** - Add tilt effect to elements
91
- - **Scrollable** - Scrollable container with custom scrollbars
92
-
93
- ### Theme Components
94
-
95
- - **ToggleThemeMode** - Toggle between light and dark themes
96
-
97
- ## Usage Example
44
+ ```svelte
45
+ <script>
46
+ const groupedOptions = [
47
+ {
48
+ text: 'Image',
49
+ children: [
50
+ { text: 'Export as PNG', value: 'png' },
51
+ { text: 'Export as SVG', value: 'svg' }
52
+ ]
53
+ },
54
+ {
55
+ text: 'Data',
56
+ children: [
57
+ { text: 'Export as CSV', value: 'csv' },
58
+ { text: 'Export as JSON', value: 'json' }
59
+ ]
60
+ }
61
+ ]
62
+ </script>
63
+
64
+ <Menu options={groupedOptions} label="Export" />
65
+ ```
66
+
67
+ #### Custom Field Mapping
68
+
69
+ Use the `fields` prop to map your data structure to Menu's expected fields:
98
70
 
99
71
  ```svelte
100
72
  <script>
101
- import { Button, Icon, Card } from '@rokkit/ui'
73
+ const items = [
74
+ { name: 'Option A', id: 'a' },
75
+ { name: 'Option B', id: 'b' }
76
+ ]
77
+
78
+ const fields = {
79
+ text: 'name',
80
+ value: 'id'
81
+ }
102
82
  </script>
103
83
 
104
- <Card>
105
- <h2>Example Card</h2>
106
- <p>This is an example of using the Card component</p>
107
- <Button variant="primary" label="Click Me" leftIcon="check" />
108
- </Card>
84
+ <Menu options={items} {fields} label="Select" />
109
85
  ```
110
86
 
111
- ## Dependencies
87
+ ### Custom Item Rendering
88
+
89
+ Use snippets to customize how menu items and group labels are rendered:
90
+
91
+ ```svelte
92
+ <script>
93
+ import { Menu } from '@rokkit/ui'
112
94
 
113
- - @rokkit/actions
114
- - @rokkit/core
115
- - @rokkit/data
116
- - @rokkit/forms
117
- - @rokkit/states
118
- - d3-scale
119
- - date-fns
120
- - ramda
95
+ const options = [
96
+ { text: 'Normal Item', value: 'normal' },
97
+ { text: 'Premium Feature', value: 'premium', snippet: 'premium' },
98
+ { text: 'Special Offer', value: 'special', snippet: 'special' }
99
+ ]
100
+ </script>
101
+
102
+ <Menu {options}>
103
+ {#snippet item(menuItem, fields, handlers)}
104
+ <button onclick={handlers.onclick} onkeydown={handlers.onkeydown}>
105
+ 🎯 {menuItem.text}
106
+ </button>
107
+ {/snippet}
108
+
109
+ {#snippet groupLabel(group, fields)}
110
+ <div class="custom-header">
111
+ 📁 {group.text}
112
+ </div>
113
+ {/snippet}
114
+
115
+ {#snippet premium(menuItem, fields, handlers)}
116
+ <button onclick={handlers.onclick} onkeydown={handlers.onkeydown}>
117
+ 🔒 Premium: {menuItem.text}
118
+ </button>
119
+ {/snippet}
120
+
121
+ {#snippet special(menuItem, fields, handlers)}
122
+ <button onclick={handlers.onclick} onkeydown={handlers.onkeydown}>
123
+ ⭐ {menuItem.text}
124
+ </button>
125
+ {/snippet}
126
+ </Menu>
127
+ ```
128
+
129
+ #### Snippet Resolution Order
130
+
131
+ 1. **Per-item snippet**: If an item has a `snippet` field (e.g., `snippet: 'premium'`), the named snippet is used
132
+ 2. **Generic `item` snippet**: Falls back to the `item` snippet if provided
133
+ 3. **Default rendering**: Uses the built-in rendering if no custom snippet matches
134
+
135
+ #### Handlers Object
136
+
137
+ Custom snippets receive a `handlers` object with:
138
+
139
+ - `onclick: () => void` - Call to trigger item selection
140
+ - `onkeydown: (event) => void` - Forward keyboard events for accessibility
141
+
142
+ ### Props
143
+
144
+ | Prop | Type | Default | Description |
145
+ | ------------ | ----------------------- | -------- | ---------------------------------- |
146
+ | `options` | `MenuItem[]` | `[]` | Array of menu items or groups |
147
+ | `fields` | `MenuFields` | `{}` | Field mapping configuration |
148
+ | `label` | `string` | `'Menu'` | Button label text |
149
+ | `icon` | `string` | - | Button icon class |
150
+ | `showArrow` | `boolean` | `true` | Show dropdown arrow indicator |
151
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant |
152
+ | `align` | `'left' \| 'right'` | `'left'` | Dropdown alignment |
153
+ | `disabled` | `boolean` | `false` | Disable the menu |
154
+ | `onselect` | `(value, item) => void` | - | Selection callback |
155
+ | `item` | `Snippet` | - | Custom snippet for rendering items |
156
+ | `groupLabel` | `Snippet` | - | Custom snippet for group headers |
157
+ | `class` | `string` | `''` | Additional CSS classes |
158
+
159
+ ### CSS Custom Properties
160
+
161
+ ```css
162
+ /* Trigger button */
163
+ --menu-trigger-bg
164
+ --menu-trigger-bg-hover
165
+ --menu-trigger-bg-active
166
+ --menu-trigger-border
167
+ --menu-trigger-border-hover
168
+ --menu-trigger-border-active
169
+ --menu-trigger-text
170
+ --menu-trigger-text-hover
171
+ --menu-focus-ring
172
+
173
+ /* Dropdown */
174
+ --menu-dropdown-bg
175
+ --menu-dropdown-border
176
+ --menu-dropdown-shadow
177
+
178
+ /* Items */
179
+ --menu-item-text
180
+ --menu-item-text-hover
181
+ --menu-item-bg-hover
182
+ --menu-item-bg-focus
183
+ --menu-item-icon
184
+ --menu-item-icon-hover
185
+ --menu-item-description
186
+
187
+ /* Groups */
188
+ --menu-group-label-color
189
+ --menu-divider-color
190
+ ```
191
+
192
+ ### Field Mapping
193
+
194
+ | Field | Default | Description |
195
+ | ------------- | --------------- | ------------------------- |
196
+ | `text` | `'text'` | Display text field |
197
+ | `value` | `'value'` | Value to emit on select |
198
+ | `icon` | `'icon'` | Icon class field |
199
+ | `description` | `'description'` | Secondary text field |
200
+ | `disabled` | `'disabled'` | Disabled state field |
201
+ | `children` | `'children'` | Children array for groups |
202
+ | `snippet` | `'snippet'` | Custom snippet name field |
203
+
204
+ ## Types
205
+
206
+ All types are exported from the package:
207
+
208
+ ```typescript
209
+ import type {
210
+ MenuProps,
211
+ MenuFields,
212
+ MenuItem,
213
+ MenuItemSnippet,
214
+ MenuGroupLabelSnippet,
215
+ MenuItemHandlers
216
+ } from '@rokkit/ui'
217
+ ```
121
218
 
122
219
  ## License
123
220
 
package/package.json CHANGED
@@ -1,44 +1,62 @@
1
1
  {
2
2
  "name": "@rokkit/ui",
3
- "version": "1.0.0-next.125",
4
- "description": "Data driven UI components, improving DX",
5
- "author": "Jerry Thomas <me@jerrythomas.name>",
6
- "license": "MIT",
7
- "module": "src/index.js",
3
+ "version": "1.0.0-next.127",
4
+ "description": "Data driven UI components for Rokkit applications",
8
5
  "type": "module",
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
- "scripts": {
13
- "prepublishOnly": "bun clean && bun tsc --project tsconfig.build.json",
14
- "clean": "rm -rf dist",
15
- "build": "bun prepublishOnly"
16
- },
17
- "files": [
18
- "src/**/*.js",
19
- "src/**/*.svelte",
20
- "dist/**/*.d.ts",
21
- "README.md",
22
- "package.json"
23
- ],
6
+ "svelte": "./src/index.ts",
7
+ "types": "./src/index.ts",
24
8
  "exports": {
25
- "./package.json": "./package.json",
26
- "./utils": "./src/lib/index.js",
27
9
  ".": {
28
- "types": "./dist/index.d.ts",
29
- "import": "./src/index.js",
30
- "svelte": "./src/index.js"
10
+ "types": "./src/index.ts",
11
+ "svelte": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "./types": {
15
+ "types": "./src/types/index.ts",
16
+ "default": "./src/types/index.ts"
17
+ },
18
+ "./utils/palette": {
19
+ "types": "./src/utils/palette.ts",
20
+ "default": "./src/utils/palette.ts"
31
21
  }
32
22
  },
23
+ "files": [
24
+ "src"
25
+ ],
26
+ "scripts": {
27
+ "check": "svelte-check --tsconfig ./tsconfig.json",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "build": "echo 'No build step needed for source-only package'"
31
+ },
32
+ "keywords": [
33
+ "ui",
34
+ "components",
35
+ "svelte",
36
+ "menu",
37
+ "dropdown"
38
+ ],
33
39
  "dependencies": {
34
- "@rokkit/actions": "latest",
35
- "@rokkit/core": "latest",
36
- "@rokkit/data": "latest",
37
- "@rokkit/forms": "latest",
38
- "@rokkit/states": "latest",
39
- "d3-scale": "^4.0.2",
40
- "date-fns": "^4.1.0",
41
- "ramda": "^0.31.3",
42
- "typescript": "^5.9.2"
40
+ "@rokkit/core": "1.0.0-next.127",
41
+ "@rokkit/data": "1.0.0-next.127",
42
+ "@rokkit/states": "1.0.0-next.127",
43
+ "@rokkit/actions": "1.0.0-next.127"
44
+ },
45
+ "peerDependencies": {
46
+ "shiki": "^3.23.0",
47
+ "svelte": "^5.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
51
+ "@testing-library/jest-dom": "^6.9.1",
52
+ "@testing-library/svelte": "^5.3.1",
53
+ "@testing-library/user-event": "^14.6.1",
54
+ "@vitest/browser": "^4.0.18",
55
+ "playwright": "^1.58.2",
56
+ "svelte": "^5.53.5",
57
+ "svelte-check": "^4.4.3",
58
+ "typescript": "^5.9.3",
59
+ "vite": "^7.3.1",
60
+ "vitest": "^4.0.18"
43
61
  }
44
62
  }
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import { ItemProxy, type ItemFields } from '../types/item-proxy.js'
4
+
5
+ interface BreadCrumbsProps {
6
+ /** Array of breadcrumb items */
7
+ items?: unknown[]
8
+ /** Custom field mappings */
9
+ fields?: Partial<ItemFields>
10
+ /** Separator icon class (default: 'i-lucide:chevron-right') */
11
+ separator?: string
12
+ /** Callback when a breadcrumb is clicked */
13
+ onclick?: (value: unknown, item: unknown) => void
14
+ /** Custom snippet for rendering each crumb */
15
+ crumb?: Snippet<[ItemProxy, boolean]>
16
+ /** Additional CSS class */
17
+ class?: string
18
+ }
19
+
20
+ const {
21
+ items = [],
22
+ fields,
23
+ separator = 'i-lucide:chevron-right',
24
+ onclick,
25
+ crumb,
26
+ class: className = ''
27
+ }: BreadCrumbsProps = $props()
28
+
29
+ function createProxy(item: unknown): ItemProxy {
30
+ return new ItemProxy(item as Record<string, unknown>, fields)
31
+ }
32
+
33
+ function handleClick(proxy: ItemProxy) {
34
+ onclick?.(proxy.itemValue, proxy.original)
35
+ }
36
+ </script>
37
+
38
+ {#snippet defaultCrumb(proxy: ItemProxy, _isLast: boolean)}
39
+ {#if proxy.icon}
40
+ <span data-breadcrumb-icon class={proxy.icon} aria-hidden="true"></span>
41
+ {/if}
42
+ <span data-breadcrumb-label>{proxy.text}</span>
43
+ {/snippet}
44
+
45
+ <nav data-breadcrumbs class={className || undefined} aria-label="Breadcrumb">
46
+ <ol data-breadcrumb-list>
47
+ {#each items as item, index (index)}
48
+ {@const proxy = createProxy(item)}
49
+ {@const isLast = index === items.length - 1}
50
+
51
+ {#if index > 0}
52
+ <li data-breadcrumb-separator aria-hidden="true">
53
+ <span class={separator}></span>
54
+ </li>
55
+ {/if}
56
+
57
+ <li data-breadcrumb-item data-current={isLast || undefined}>
58
+ {#if isLast}
59
+ <span data-breadcrumb-current aria-current="page">
60
+ {#if crumb}
61
+ {@render crumb(proxy, isLast)}
62
+ {:else}
63
+ {@render defaultCrumb(proxy, isLast)}
64
+ {/if}
65
+ </span>
66
+ {:else}
67
+ <button
68
+ type="button"
69
+ data-breadcrumb-link
70
+ onclick={() => handleClick(proxy)}
71
+ >
72
+ {#if crumb}
73
+ {@render crumb(proxy, isLast)}
74
+ {:else}
75
+ {@render defaultCrumb(proxy, isLast)}
76
+ {/if}
77
+ </button>
78
+ {/if}
79
+ </li>
80
+ {/each}
81
+ </ol>
82
+ </nav>
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ import type { ButtonProps } from '../types/button.js'
3
+ import { ItemProxy } from '../types/item-proxy.js'
4
+ import ItemContent from './ItemContent.svelte'
5
+
6
+ let {
7
+ variant = 'default',
8
+ style = 'default',
9
+ size = 'md',
10
+ type = 'button',
11
+ label,
12
+ icon,
13
+ iconRight,
14
+ href,
15
+ target,
16
+ disabled = false,
17
+ loading = false,
18
+ onclick,
19
+ class: className = '',
20
+ children
21
+ }: ButtonProps = $props()
22
+
23
+ const isIconOnly = $derived(Boolean(icon) && !label && !children)
24
+ const isDisabled = $derived(disabled || loading)
25
+
26
+ /**
27
+ * Create an ItemProxy for default content rendering.
28
+ * Constructs a minimal item from button props.
29
+ */
30
+ const proxy = $derived(
31
+ new ItemProxy({ text: label, icon, iconRight }, { text: 'text', icon: 'icon' })
32
+ )
33
+ </script>
34
+
35
+ {#snippet defaultContent()}
36
+ {#if loading}
37
+ <span data-button-spinner aria-hidden="true"></span>
38
+ {/if}
39
+ <ItemContent {proxy} />
40
+ {#if iconRight}
41
+ <span data-button-icon-right class={iconRight} aria-hidden="true"></span>
42
+ {/if}
43
+ {/snippet}
44
+
45
+ {#if href && !isDisabled}
46
+ <a
47
+ {href}
48
+ {target}
49
+ data-button
50
+ data-variant={variant}
51
+ data-style={style}
52
+ data-size={size}
53
+ data-icon-only={isIconOnly || undefined}
54
+ data-loading={loading || undefined}
55
+ class={className || undefined}
56
+ aria-label={label}
57
+ aria-busy={loading || undefined}
58
+ >
59
+ {#if children}
60
+ {@render children()}
61
+ {:else}
62
+ {@render defaultContent()}
63
+ {/if}
64
+ </a>
65
+ {:else}
66
+ <button
67
+ {type}
68
+ data-button
69
+ data-variant={variant}
70
+ data-style={style}
71
+ data-size={size}
72
+ data-disabled={isDisabled || undefined}
73
+ data-icon-only={isIconOnly || undefined}
74
+ data-loading={loading || undefined}
75
+ class={className || undefined}
76
+ disabled={isDisabled}
77
+ aria-label={label}
78
+ aria-busy={loading || undefined}
79
+ {onclick}
80
+ >
81
+ {#if children}
82
+ {@render children()}
83
+ {:else}
84
+ {@render defaultContent()}
85
+ {/if}
86
+ </button>
87
+ {/if}
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ import type { ButtonGroupProps } from '../types/button.js'
3
+
4
+ const {
5
+ size = 'md',
6
+ class: className = '',
7
+ children
8
+ }: ButtonGroupProps = $props()
9
+ </script>
10
+
11
+ <div
12
+ data-button-group
13
+ data-size={size}
14
+ class={className || undefined}
15
+ role="group"
16
+ >
17
+ {@render children?.()}
18
+ </div>
@@ -0,0 +1,61 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+
4
+ interface CardProps {
5
+ /** Optional href to render as a link */
6
+ href?: string
7
+ /** Click handler (only applies when no href) */
8
+ onclick?: () => void
9
+ /** Additional CSS class */
10
+ class?: string
11
+ /** Card header snippet */
12
+ header?: Snippet
13
+ /** Card footer snippet */
14
+ footer?: Snippet
15
+ /** Card body content */
16
+ children?: Snippet
17
+ }
18
+
19
+ const {
20
+ href,
21
+ onclick,
22
+ class: className = '',
23
+ header,
24
+ footer,
25
+ children
26
+ }: CardProps = $props()
27
+ </script>
28
+
29
+ {#snippet cardContent()}
30
+ {#if header}
31
+ <div data-card-header>
32
+ {@render header()}
33
+ </div>
34
+ {/if}
35
+
36
+ {#if children}
37
+ <div data-card-body>
38
+ {@render children()}
39
+ </div>
40
+ {/if}
41
+
42
+ {#if footer}
43
+ <div data-card-footer>
44
+ {@render footer()}
45
+ </div>
46
+ {/if}
47
+ {/snippet}
48
+
49
+ {#if href}
50
+ <a {href} data-card class={className || undefined}>
51
+ {@render cardContent()}
52
+ </a>
53
+ {:else if onclick}
54
+ <button type="button" data-card data-card-interactive class={className || undefined} {onclick}>
55
+ {@render cardContent()}
56
+ </button>
57
+ {:else}
58
+ <div data-card class={className || undefined}>
59
+ {@render cardContent()}
60
+ </div>
61
+ {/if}