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

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 (53) hide show
  1. package/package.json +6 -16
  2. package/src/components/BreadCrumbs.svelte +25 -17
  3. package/src/components/Button.svelte +11 -5
  4. package/src/components/Carousel.svelte +11 -6
  5. package/src/components/Code.svelte +6 -2
  6. package/src/components/FloatingAction.svelte +24 -21
  7. package/src/components/FloatingNavigation.svelte +36 -29
  8. package/src/components/Grid.svelte +128 -0
  9. package/src/components/ItemContent.svelte +21 -20
  10. package/src/components/LazyTree.svelte +165 -0
  11. package/src/components/List.svelte +147 -435
  12. package/src/components/Menu.svelte +195 -346
  13. package/src/components/MultiSelect.svelte +238 -390
  14. package/src/components/PaletteManager.svelte +15 -5
  15. package/src/components/Pill.svelte +19 -14
  16. package/src/components/Range.svelte +8 -3
  17. package/src/components/Rating.svelte +19 -9
  18. package/src/components/SearchFilter.svelte +11 -3
  19. package/src/components/Select.svelte +265 -454
  20. package/src/components/Stepper.svelte +9 -6
  21. package/src/components/Switch.svelte +11 -11
  22. package/src/components/Table.svelte +0 -1
  23. package/src/components/Tabs.svelte +96 -172
  24. package/src/components/Timeline.svelte +5 -5
  25. package/src/components/Toggle.svelte +55 -119
  26. package/src/components/Toolbar.svelte +24 -23
  27. package/src/components/Tree.svelte +115 -584
  28. package/src/components/UploadFileStatus.svelte +83 -0
  29. package/src/components/UploadProgress.svelte +131 -0
  30. package/src/components/UploadTarget.svelte +124 -0
  31. package/src/components/index.ts +5 -0
  32. package/src/index.ts +6 -1
  33. package/src/types/button.ts +3 -0
  34. package/src/types/code.ts +4 -4
  35. package/src/types/floating-action.ts +13 -8
  36. package/src/types/floating-navigation.ts +14 -2
  37. package/src/types/index.ts +5 -3
  38. package/src/types/list.ts +10 -6
  39. package/src/types/menu.ts +38 -138
  40. package/src/types/palette.ts +17 -0
  41. package/src/types/select.ts +33 -63
  42. package/src/types/switch.ts +9 -5
  43. package/src/types/table.ts +6 -6
  44. package/src/types/tabs.ts +13 -34
  45. package/src/types/timeline.ts +5 -3
  46. package/src/types/toggle.ts +15 -56
  47. package/src/types/toolbar.ts +1 -1
  48. package/src/types/tree.ts +9 -18
  49. package/src/types/upload-file-status.ts +45 -0
  50. package/src/types/upload-progress.ts +111 -0
  51. package/src/types/upload-target.ts +68 -0
  52. package/src/utils/upload.js +128 -0
  53. package/src/types/item-proxy.ts +0 -358
@@ -2,44 +2,11 @@
2
2
  * Toggle Component Types
3
3
  *
4
4
  * Provides types for the data-driven Toggle component.
5
- * Field mapping and data access is handled by ItemProxy.
5
+ * Field mapping and data access is handled by ProxyItem from @rokkit/states.
6
6
  */
7
7
 
8
- // =============================================================================
9
- // Field Mapping Types
10
- // =============================================================================
11
-
12
- /**
13
- * Field mapping configuration for toggle data.
14
- * Maps custom data field names to the component's expected properties.
15
- */
16
- export interface ToggleFields {
17
- /** Field for display text - default: 'text' */
18
- text?: string
19
-
20
- /** Field for the value to emit on select - default: 'value' */
21
- value?: string
22
-
23
- /** Field for icon class name - default: 'icon' */
24
- icon?: string
25
-
26
- /** Field for disabled state - default: 'disabled' */
27
- disabled?: string
28
-
29
- /** Field for tooltip/description text - default: 'description' */
30
- description?: string
31
- }
32
-
33
- /**
34
- * Default field mapping values
35
- */
36
- export const defaultToggleFields: Required<ToggleFields> = {
37
- text: 'text',
38
- value: 'value',
39
- icon: 'icon',
40
- disabled: 'disabled',
41
- description: 'description'
42
- }
8
+ import type { Snippet } from 'svelte'
9
+ import type { ProxyItem } from '@rokkit/states'
43
10
 
44
11
  // =============================================================================
45
12
  // Toggle Item Types
@@ -55,22 +22,11 @@ export type ToggleItem = Record<string, unknown>
55
22
  // =============================================================================
56
23
 
57
24
  /**
58
- * Handlers passed to custom item snippets
25
+ * Snippet for rendering a toggle option.
26
+ * The component renders the button wrapper; the snippet renders inner content.
27
+ * Receives the ProxyItem and whether this item is currently selected.
59
28
  */
60
- export interface ToggleItemHandlers {
61
- /** Call to trigger item selection */
62
- onclick: () => void
63
- /** Forward keyboard events for accessibility */
64
- onkeydown: (event: KeyboardEvent) => void
65
- }
66
-
67
- /**
68
- * Snippet type for rendering toggle items.
69
- * Fourth parameter is whether the item is currently selected.
70
- */
71
- export type ToggleItemSnippet = import('svelte').Snippet<
72
- [ToggleItem, ToggleFields, ToggleItemHandlers, boolean]
73
- >
29
+ export type ToggleItemSnippet = Snippet<[ProxyItem, boolean]>
74
30
 
75
31
  // =============================================================================
76
32
  // Component Props Types
@@ -80,13 +36,13 @@ export type ToggleItemSnippet = import('svelte').Snippet<
80
36
  * Props for the Toggle component
81
37
  */
82
38
  export interface ToggleProps {
83
- /** Array of toggle options */
39
+ /** Array of toggle options (strings, numbers, or objects) */
84
40
  options?: ToggleItem[]
85
41
 
86
- /** Field mapping configuration */
87
- fields?: ToggleFields
42
+ /** Field mapping overrides BASE_FIELDS defaults (text → 'label', value → 'value', …) */
43
+ fields?: Record<string, string>
88
44
 
89
- /** Currently selected value */
45
+ /** Currently selected value (bindable) */
90
46
  value?: unknown
91
47
 
92
48
  /** Called when selection changes */
@@ -101,9 +57,12 @@ export interface ToggleProps {
101
57
  /** Whether the entire toggle is disabled */
102
58
  disabled?: boolean
103
59
 
60
+ /** Accessible label for the radiogroup. Default: messages.current.toggle.label */
61
+ label?: string
62
+
104
63
  /** Additional CSS classes */
105
64
  class?: string
106
65
 
107
- /** Custom snippet for rendering toggle items */
66
+ /** Custom snippet for rendering toggle options */
108
67
  item?: ToggleItemSnippet
109
68
  }
@@ -2,7 +2,7 @@
2
2
  * Toolbar Component Types
3
3
  *
4
4
  * Provides types for the data-driven Toolbar component.
5
- * Field mapping and data access is handled by ItemProxy.
5
+ * Field mapping and data access is handled by ProxyItem from @rokkit/states.
6
6
  */
7
7
 
8
8
  // =============================================================================
package/src/types/tree.ts CHANGED
@@ -3,12 +3,11 @@
3
3
  *
4
4
  * Provides types for the data-driven Tree component.
5
5
  * Supports hierarchical data with expand/collapse, tree lines, and custom icons.
6
- * Field mapping and data access is handled by ItemProxy.
6
+ * Field mapping and data access is handled by ProxyItem from @rokkit/states.
7
7
  */
8
8
 
9
9
  import type { Snippet } from 'svelte'
10
- import type { ItemProxy, ItemFields } from './item-proxy.js'
11
- import { defaultStateIcons } from '@rokkit/core'
10
+ import { DEFAULT_STATE_ICONS } from '@rokkit/core'
12
11
 
13
12
  // =============================================================================
14
13
  // Field Mapping Types
@@ -16,9 +15,9 @@ import { defaultStateIcons } from '@rokkit/core'
16
15
 
17
16
  /**
18
17
  * Field mapping configuration for tree data.
19
- * Extends ItemFields with tree-specific fields.
18
+ * Extends base field mapping with tree-specific fields.
20
19
  */
21
- export interface TreeFields extends ItemFields {
20
+ export interface TreeFields extends Record<string, string> {
22
21
  /** Field for expanded state - default: 'expanded' */
23
22
  expanded?: string
24
23
 
@@ -72,7 +71,7 @@ export type ConnectorType = 'child' | 'last' | 'sibling' | 'empty'
72
71
 
73
72
  /**
74
73
  * Icons configuration for tree expand/collapse states.
75
- * Keys match the naming convention in @rokkit/core defaultStateIcons.
74
+ * Keys match the naming convention in @rokkit/core DEFAULT_STATE_ICONS.
76
75
  */
77
76
  export interface TreeStateIcons {
78
77
  /** Icon class for expanded/opened state */
@@ -86,8 +85,8 @@ export interface TreeStateIcons {
86
85
  * that get resolved to actual icon classes via UnoCSS shortcuts.
87
86
  */
88
87
  export const defaultTreeStateIcons: TreeStateIcons = {
89
- opened: defaultStateIcons.node.opened,
90
- closed: defaultStateIcons.node.closed
88
+ opened: DEFAULT_STATE_ICONS.node.opened,
89
+ closed: DEFAULT_STATE_ICONS.node.closed
91
90
  }
92
91
 
93
92
  // =============================================================================
@@ -146,8 +145,8 @@ export interface TreeProps {
146
145
  /** Size variant */
147
146
  size?: 'sm' | 'md' | 'lg'
148
147
 
149
- /** Whether to show tree lines/connectors */
150
- showLines?: boolean
148
+ /** Tree line connector style — 'none' hides lines, 'solid'|'dashed'|'dotted' set the line style */
149
+ lineStyle?: 'none' | 'solid' | 'dashed' | 'dotted'
151
150
 
152
151
  /** Enable multiple item selection (Ctrl+click toggle, Shift+click range) */
153
152
  multiselect?: boolean
@@ -248,12 +247,4 @@ export function getLineTypes(
248
247
  // Keep old name as alias for backward compatibility
249
248
  export const getTreeLineTypes = getLineTypes
250
249
 
251
- /**
252
- * Get the key for a node (for expanded state tracking)
253
- */
254
- export function getNodeKey(proxy: ItemProxy): string {
255
- const val = proxy.itemValue
256
- return typeof val === 'string' || typeof val === 'number' ? String(val) : proxy.text
257
- }
258
-
259
250
  export { getSnippet } from './menu.js'
@@ -0,0 +1,45 @@
1
+ /**
2
+ * UploadFileStatus Component Types
3
+ *
4
+ * Renders a single file's upload status with progress bar, action buttons
5
+ * (cancel/retry/remove), and file metadata (icon, name, size, status label).
6
+ * Action button visibility driven by cancelWhen/retryWhen/removeWhen arrays.
7
+ */
8
+
9
+ import type { ProxyItem } from '@rokkit/states'
10
+
11
+ // =============================================================================
12
+ // Component Props
13
+ // =============================================================================
14
+
15
+ /**
16
+ * Props for the UploadFileStatus component
17
+ */
18
+ export interface UploadFileStatusProps {
19
+ /** ProxyItem wrapping the file data */
20
+ proxy: ProxyItem
21
+
22
+ /** Status values that show the cancel button */
23
+ cancelWhen?: string[]
24
+
25
+ /** Status values that show the retry button */
26
+ retryWhen?: string[]
27
+
28
+ /** Status values that show the remove button */
29
+ removeWhen?: string[]
30
+
31
+ /** Called when cancel is clicked */
32
+ oncancel?: (proxy: ProxyItem) => void
33
+
34
+ /** Called when retry is clicked */
35
+ onretry?: (proxy: ProxyItem) => void
36
+
37
+ /** Called when remove is clicked */
38
+ onremove?: (proxy: ProxyItem) => void
39
+
40
+ /** Label overrides merged with messages.current.uploadProgress */
41
+ labels?: Record<string, string>
42
+
43
+ /** Icon class overrides for action buttons (cancel, retry, remove) */
44
+ icons?: Record<string, string>
45
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * UploadProgress Component Types
3
+ *
4
+ * File upload status orchestrator. Thin composition layer that renders
5
+ * a header + List or Grid layout. Default itemContent snippet renders
6
+ * UploadFileStatus for each file. Consumer can override with custom snippet.
7
+ */
8
+
9
+ import type { Snippet } from 'svelte'
10
+ import type { ProxyItem } from '@rokkit/states'
11
+
12
+ // =============================================================================
13
+ // Item Types
14
+ // =============================================================================
15
+
16
+ /**
17
+ * Generic upload file item — any object with mapped fields
18
+ */
19
+ export type UploadItem = Record<string, unknown>
20
+
21
+ // =============================================================================
22
+ // Field Mapping Types
23
+ // =============================================================================
24
+
25
+ /**
26
+ * Field mapping for upload file data.
27
+ * Maps semantic field names to keys in the data objects.
28
+ */
29
+ export interface UploadFields {
30
+ /** Field for display text - default: 'text' */
31
+ label?: string
32
+
33
+ /** Field for unique value - default: 'value' */
34
+ value?: string
35
+
36
+ /** Field for icon class - default: 'icon' */
37
+ icon?: string
38
+
39
+ /** Field for upload status key - default: 'status' */
40
+ status?: string
41
+
42
+ /** Field for progress percentage (0-100) - default: 'progress' */
43
+ progress?: string
44
+
45
+ /** Field for file size in bytes - default: 'size' */
46
+ size?: string
47
+
48
+ /** Field for MIME type - default: 'type' */
49
+ type?: string
50
+
51
+ /** Field for error message - default: 'error' */
52
+ error?: string
53
+ }
54
+
55
+ // =============================================================================
56
+ // Snippet Types
57
+ // =============================================================================
58
+
59
+ /**
60
+ * Snippet for custom per-file rendering inside List/Grid.
61
+ * Receives the ProxyItem wrapping the file data.
62
+ */
63
+ export type UploadItemSnippet = Snippet<[proxy: ProxyItem]>
64
+
65
+ // =============================================================================
66
+ // Component Props
67
+ // =============================================================================
68
+
69
+ /**
70
+ * Props for the UploadProgress component
71
+ */
72
+ export interface UploadProgressProps {
73
+ /** Array of file data objects */
74
+ files?: UploadItem[]
75
+
76
+ /** Field mapping configuration */
77
+ fields?: UploadFields
78
+
79
+ /** Layout mode */
80
+ view?: 'list' | 'grid'
81
+
82
+ /** Status values that show the cancel button */
83
+ cancelWhen?: string[]
84
+
85
+ /** Status values that show the retry button */
86
+ retryWhen?: string[]
87
+
88
+ /** Status values that show the remove button */
89
+ removeWhen?: string[]
90
+
91
+ /** Called when cancel is clicked (receives raw file object) */
92
+ oncancel?: (file: UploadItem) => void
93
+
94
+ /** Called when retry is clicked (receives raw file object) */
95
+ onretry?: (file: UploadItem) => void
96
+
97
+ /** Called when remove is clicked (receives raw file object) */
98
+ onremove?: (file: UploadItem) => void
99
+
100
+ /** Called when clear all is clicked */
101
+ onclear?: () => void
102
+
103
+ /** Label overrides merged with messages.current.uploadProgress */
104
+ labels?: Record<string, string>
105
+
106
+ /** Additional CSS classes */
107
+ class?: string
108
+
109
+ /** Custom snippet for per-file rendering (overrides default UploadFileStatus) */
110
+ itemContent?: UploadItemSnippet
111
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * UploadTarget Component Types
3
+ *
4
+ * Drop zone with file validation. Accepts drag-and-drop or click-to-browse.
5
+ * Validates files against accept (MIME/extension) and maxSize constraints.
6
+ */
7
+
8
+ import type { Snippet } from 'svelte'
9
+
10
+ // =============================================================================
11
+ // Item Types
12
+ // =============================================================================
13
+
14
+ /**
15
+ * File validation error returned via onerror callback
16
+ */
17
+ export interface UploadError {
18
+ /** The file that failed validation */
19
+ file: File
20
+ /** Why the file was rejected */
21
+ reason: 'type' | 'size'
22
+ }
23
+
24
+ // =============================================================================
25
+ // Snippet Types
26
+ // =============================================================================
27
+
28
+ /**
29
+ * Snippet for custom drop zone content.
30
+ * Receives the current dragging state.
31
+ */
32
+ export type UploadTargetContentSnippet = Snippet<[dragging: boolean]>
33
+
34
+ // =============================================================================
35
+ // Component Props
36
+ // =============================================================================
37
+
38
+ /**
39
+ * Props for the UploadTarget component
40
+ */
41
+ export interface UploadTargetProps {
42
+ /** Accepted file types (MIME types or extensions, comma-separated) */
43
+ accept?: string
44
+
45
+ /** Maximum file size in bytes */
46
+ maxSize?: number
47
+
48
+ /** Allow multiple file selection */
49
+ multiple?: boolean
50
+
51
+ /** Disable the drop zone */
52
+ disabled?: boolean
53
+
54
+ /** Label overrides merged with messages.current.uploadTarget */
55
+ labels?: Record<string, string>
56
+
57
+ /** Called with validated files after drop or browse */
58
+ onfiles?: (files: File[]) => void
59
+
60
+ /** Called for each file that fails validation */
61
+ onerror?: (err: UploadError) => void
62
+
63
+ /** Additional CSS classes */
64
+ class?: string
65
+
66
+ /** Custom content snippet (replaces default drop zone UI) */
67
+ content?: UploadTargetContentSnippet
68
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Upload utility functions — shared helpers for UploadTarget and UploadProgress.
3
+ * Pure functions with no Svelte dependency.
4
+ */
5
+
6
+ const ARCHIVE_TYPES = new Set(['application/zip', 'application/gzip', 'application/x-tar'])
7
+
8
+ /**
9
+ * Check if a file matches an accept string (comma-separated MIME types and extensions).
10
+ * @param {{ type: string, name: string }} file
11
+ * @param {string | null | undefined} accept - e.g. 'image/*,.pdf,application/json'
12
+ * @returns {boolean}
13
+ */
14
+ export function matchesAccept(file, accept) {
15
+ if (!accept) return true
16
+
17
+ const tokens = accept.split(',').map((t) => t.trim()).filter(Boolean)
18
+ if (tokens.length === 0) return true
19
+
20
+ return tokens.some((token) => {
21
+ if (token.startsWith('.')) {
22
+ // Extension match (case-insensitive)
23
+ return file.name.toLowerCase().endsWith(token.toLowerCase())
24
+ }
25
+ if (token.endsWith('/*')) {
26
+ // Wildcard MIME type match (e.g. image/*)
27
+ const prefix = token.slice(0, -1) // 'image/'
28
+ return file.type.startsWith(prefix)
29
+ }
30
+ // Exact MIME type match
31
+ return file.type === token
32
+ })
33
+ }
34
+
35
+ /**
36
+ * Validate a file against accept and maxSize constraints.
37
+ * @param {{ type: string, name: string, size: number }} file
38
+ * @param {{ accept?: string, maxSize?: number }} constraints
39
+ * @returns {true | { reason: 'type' | 'size' }}
40
+ */
41
+ export function validateFile(file, { accept, maxSize } = {}) {
42
+ if (accept && !matchesAccept(file, accept)) {
43
+ return { reason: 'type' }
44
+ }
45
+ if (maxSize !== undefined && file.size > maxSize) {
46
+ return { reason: 'size' }
47
+ }
48
+ return true
49
+ }
50
+
51
+ /**
52
+ * Map a MIME type to an icon class string.
53
+ * @param {string | null | undefined} mimeType
54
+ * @returns {string}
55
+ */
56
+ export function inferIcon(mimeType) {
57
+ if (!mimeType) return 'i-lucide:file'
58
+
59
+ if (mimeType.startsWith('image/')) return 'i-lucide:image'
60
+ if (mimeType.startsWith('video/')) return 'i-lucide:video'
61
+ if (mimeType.startsWith('audio/')) return 'i-lucide:music'
62
+ if (mimeType === 'application/pdf') return 'i-lucide:file-text'
63
+ if (mimeType.startsWith('text/')) return 'i-lucide:file-text'
64
+ if (ARCHIVE_TYPES.has(mimeType)) return 'i-lucide:archive'
65
+
66
+ return 'i-lucide:file'
67
+ }
68
+
69
+ /**
70
+ * Format a byte count as a human-readable size string.
71
+ * @param {number} bytes
72
+ * @returns {string}
73
+ */
74
+ export function formatSize(bytes) {
75
+ if (bytes < 1024) return `${bytes} B`
76
+ if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`
77
+ if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`
78
+ return `${(bytes / 1024 ** 3).toFixed(1)} GB`
79
+ }
80
+
81
+ /**
82
+ * Group a flat file array by path field into a nested tree structure.
83
+ * Root-level items (empty path) stay ungrouped.
84
+ * Group nodes use `text` as the label field (ProxyItem compatibility).
85
+ *
86
+ * @param {Array<Record<string, any>>} items
87
+ * @param {string} pathField - field name containing the path (e.g. 'path')
88
+ * @param {string} childrenField - field name for children arrays (e.g. 'children')
89
+ * @returns {Array<Record<string, any>>}
90
+ */
91
+ export function groupByPath(items, pathField, childrenField) {
92
+ if (!items || items.length === 0) return []
93
+
94
+ const root = []
95
+ /** @type {Map<string, Record<string, any>>} */
96
+ const folderMap = new Map()
97
+
98
+ for (const item of items) {
99
+ const rawPath = item[pathField] || ''
100
+ if (!rawPath) {
101
+ root.push(item)
102
+ continue
103
+ }
104
+
105
+ // Split path into segments, filtering empty strings from trailing slashes
106
+ const segments = rawPath.split('/').filter(Boolean)
107
+ let parent = root
108
+ let fullPath = ''
109
+
110
+ for (let i = 0; i < segments.length; i++) {
111
+ const segment = segments[i]
112
+ fullPath = fullPath ? `${fullPath}/${segment}` : segment
113
+
114
+ let folder = folderMap.get(fullPath)
115
+ if (!folder) {
116
+ folder = { text: segment, [childrenField]: [] }
117
+ folderMap.set(fullPath, folder)
118
+ parent.push(folder)
119
+ }
120
+
121
+ parent = folder[childrenField]
122
+ }
123
+
124
+ parent.push(item)
125
+ }
126
+
127
+ return root
128
+ }