@rokkit/ui 1.0.0-next.106 → 1.0.0-next.108

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 (64) hide show
  1. package/dist/index.d.ts +12 -1
  2. package/dist/input/types.d.ts +9 -0
  3. package/dist/lib/fields.d.ts +16 -0
  4. package/dist/lib/form.d.ts +95 -0
  5. package/dist/lib/index.d.ts +5 -0
  6. package/dist/lib/layout.d.ts +7 -0
  7. package/dist/lib/nested.d.ts +48 -0
  8. package/dist/lib/schema.d.ts +7 -0
  9. package/dist/lib/select.d.ts +8 -0
  10. package/dist/wrappers/index.d.ts +3 -0
  11. package/package.json +1 -1
  12. package/src/Accordion.svelte +61 -33
  13. package/src/BreadCrumbs.svelte +9 -5
  14. package/src/Carousel.svelte +49 -0
  15. package/src/DataEditor.svelte +31 -0
  16. package/src/DropDown.svelte +68 -0
  17. package/src/DropSearch.svelte +37 -0
  18. package/src/FieldLayout.svelte +48 -0
  19. package/src/Fillable.svelte +19 -0
  20. package/src/Form.svelte +17 -0
  21. package/src/Icon.svelte +1 -0
  22. package/src/Item.svelte +12 -11
  23. package/src/Link.svelte +2 -2
  24. package/src/List.svelte +52 -31
  25. package/src/ListBody.svelte +43 -0
  26. package/src/ListEditor.svelte +44 -0
  27. package/src/MultiSelect.svelte +48 -0
  28. package/src/NestedEditor.svelte +88 -0
  29. package/src/NestedList.svelte +45 -35
  30. package/src/NestedPaginator.svelte +59 -0
  31. package/src/Node.svelte +36 -23
  32. package/src/PageNavigator.svelte +94 -0
  33. package/src/ProgressDots.svelte +53 -0
  34. package/src/RadioGroup.svelte +3 -2
  35. package/src/ResponsiveGrid.svelte +1 -1
  36. package/src/Select.svelte +108 -0
  37. package/src/Slider.svelte +14 -0
  38. package/src/Stage.svelte +41 -0
  39. package/src/Stepper.svelte +66 -0
  40. package/src/Summary.svelte +5 -8
  41. package/src/Switch.svelte +8 -5
  42. package/src/TableCell.svelte +1 -1
  43. package/src/Tabs.svelte +23 -15
  44. package/src/Toggle.svelte +7 -7
  45. package/src/Tree.svelte +46 -19
  46. package/src/TreeTable.svelte +6 -5
  47. package/src/index.js +14 -1
  48. package/src/input/Input.svelte +17 -0
  49. package/src/input/InputField.svelte +69 -0
  50. package/src/input/InputSelect.svelte +23 -0
  51. package/src/input/InputSwitch.svelte +19 -0
  52. package/src/input/types.js +29 -0
  53. package/src/lib/fields.js +118 -0
  54. package/src/lib/form.js +72 -0
  55. package/src/lib/index.js +12 -0
  56. package/src/lib/layout.js +63 -0
  57. package/src/lib/nested.js +192 -0
  58. package/src/lib/schema.js +32 -0
  59. package/src/lib/select.js +38 -0
  60. package/src/lib/tree.js +1 -1
  61. package/src/wrappers/Category.svelte +27 -0
  62. package/src/wrappers/Section.svelte +16 -0
  63. package/src/wrappers/Wrapper.svelte +12 -0
  64. package/src/wrappers/index.js +3 -0
@@ -0,0 +1,192 @@
1
+ import { omit, pick } from 'ramda'
2
+ import { isObject } from '@rokkit/core'
3
+ import { typeOf } from '@rokkit/data'
4
+ import { deriveSchemaFromValue } from './schema'
5
+ import { deriveLayoutFromValue } from './layout'
6
+
7
+ /**
8
+ * Flattens an object into a flat object
9
+ *
10
+ * @param {Object} input - The object to flatten
11
+ * @param {String} scope - The scope of the object
12
+ */
13
+ export function flattenObject(input, scope = '#') {
14
+ // eslint-disable-next-line no-use-before-define
15
+ return flattenAttributes(input, scope).reduce(
16
+ // eslint-disable-next-line no-use-before-define
17
+ (acc, item) => ({ ...acc, ...flattenElement(item) }),
18
+ {
19
+ [scope]: {
20
+ type: 'object',
21
+ value: input,
22
+ scope,
23
+ key: scope.split('/').slice(-1)[0]
24
+ }
25
+ }
26
+ )
27
+ }
28
+ /**
29
+ * Flattens an object into an array of key-value pairs
30
+ *
31
+ * @param {Object} input - The object to flatten
32
+ * @param {String} scope - The scope of the object
33
+ */
34
+ export function flattenAttributes(input, scope = '#') {
35
+ return Object.entries(input).map(([key, value]) => ({
36
+ key,
37
+ value,
38
+ type: typeOf(value),
39
+ scope: [scope, key].join('/')
40
+ }))
41
+ }
42
+
43
+ /**
44
+ * Derives a nested schema from an object
45
+ *
46
+ * @param {Object} input - The object to derive the schema from
47
+ * @param {String} scope - The scope of the object
48
+ * @returns {Object} The derived schema
49
+ */
50
+ export function deriveNestedSchema(input, scope = '#') {
51
+ const elements = flattenAttributes(input)
52
+ const atoms = elements.filter(({ type }) => !['object', 'array'].includes(type))
53
+
54
+ const schema = {
55
+ type: 'object'
56
+ }
57
+
58
+ if (atoms.length > 0) {
59
+ schema.properties = atoms.reduce(
60
+ (acc, { key, type, value }) => ({
61
+ ...acc,
62
+ [key]: {
63
+ type,
64
+ default: value
65
+ }
66
+ }),
67
+ {}
68
+ )
69
+ schema.layout = {
70
+ type: 'vertical',
71
+ elements: atoms.map((el) => ({ label: el.key, scope: el.scope }))
72
+ }
73
+ }
74
+
75
+ if (atoms.length < elements.length) {
76
+ // eslint-disable-next-line no-use-before-define
77
+ schema.children = deriveSchemaForChildren(elements, scope)
78
+ }
79
+
80
+ if (scope !== '#') return schema
81
+ return schema.properties ? [schema] : schema.children
82
+ }
83
+
84
+ /**
85
+ * Derives the children of an object
86
+ *
87
+ * @param {Array} elements - The elements to derive children from
88
+ * @param {String} scope - The scope of the object
89
+ * @returns {Array} The derived children
90
+ */
91
+ function deriveSchemaForChildren(elements, scope) {
92
+ return [
93
+ ...elements
94
+ .filter(({ type }) => type === 'object')
95
+ .map((item) => ({
96
+ ...omit(['value', 'scope'], item),
97
+ scope: [scope, item.key].join('/'),
98
+ ...deriveNestedSchema(item.value, [scope, item.key].join('/'))
99
+ })),
100
+ ...elements
101
+ .filter(({ type }) => type === 'array')
102
+ .map((item) => ({
103
+ ...omit(['value'], item),
104
+ default: [],
105
+ scope: [scope, item.key].join('/'),
106
+ items: deriveSchemaFromValue(item.value.length ? item.value[0] : null),
107
+ layout: deriveLayoutFromValue(item.value.length ? item.value[0] : null)
108
+ }))
109
+ ]
110
+ }
111
+
112
+ /**
113
+ * Flattens an element into a flat object
114
+ *
115
+ * @param {Object} element - The element to flatten
116
+ */
117
+ export function flattenElement(element) {
118
+ if (element.type === 'object') {
119
+ return flattenObject(element.value, element.scope)
120
+ } else if (element.type === 'array') {
121
+ return element.value
122
+ .map((item, index) => ({
123
+ value: item,
124
+ scope: [element.scope, `[${index}]`].join('/'),
125
+ key: `[${index}]`,
126
+ type: typeOf(item)
127
+ }))
128
+ .reduce((acc, item) => ({ ...acc, ...flattenElement(item) }), {
129
+ [element.scope]: pick(['key', 'type', 'scope', 'value'], element)
130
+ })
131
+ }
132
+ return { [element.scope]: element }
133
+ }
134
+
135
+ /**
136
+ * Generates an index array referencing the input data
137
+ *
138
+ * @param {Object} data - The flat object to index
139
+ * @param {String} key - The key to use as index
140
+ */
141
+ export function generateIndex(data, key = 'scope') {
142
+ const index = data
143
+ .map((item) => ({
144
+ ...item,
145
+ _path: item[key],
146
+ _isParent: false,
147
+ _isExpanded: true,
148
+ _levels: []
149
+ }))
150
+ .sort((a, b) => a[key].localeCompare(b[key]))
151
+ .filter((item) => item[key] !== '#')
152
+
153
+ let levels = [0]
154
+ let current = 0
155
+
156
+ index.forEach((item, row) => {
157
+ const path = item._path.split('/').slice(1)
158
+ item._depth = path.length - 1
159
+ if (row === 0) {
160
+ item._levels = [0]
161
+ } else if (path.length > levels.length) {
162
+ index[row - 1]._isParent = true
163
+ item._levels = [...levels, 0]
164
+ } else {
165
+ current = levels[path.length - 1] + 1
166
+ item._levels = [...levels.slice(0, path.length - 1), current]
167
+ }
168
+ levels = item._levels
169
+ })
170
+ return index
171
+ }
172
+
173
+ /**
174
+ * Generates a tree table from the input data
175
+ *
176
+ * @param {Object} data - The data to generate the tree table from
177
+ * @param {String} key - The key to use as index
178
+ * @param {Boolean} ellipsis - Whether to truncate the value
179
+ */
180
+ export function generateTreeTable(data, key = 'scope', ellipsis = false) {
181
+ let result = []
182
+ if (Array.isArray(data)) result = generateIndex(data, key)
183
+ if (isObject(data)) result = generateIndex(Object.values(flattenObject(data)), key)
184
+
185
+ if (ellipsis) {
186
+ result = result.map((item) => ({
187
+ ...omit(['value'], item),
188
+ value: ['array', 'object'].includes(item.type) ? '...' : item.value
189
+ }))
190
+ }
191
+ return result
192
+ }
@@ -0,0 +1,32 @@
1
+ import { typeOf } from '@rokkit/data'
2
+
3
+ /**
4
+ * Derives a schema for properties of an object.
5
+ *
6
+ * @param {Object} data
7
+ * @returns
8
+ */
9
+ function deriveObjectProperties(data) {
10
+ const properties = {}
11
+ for (const [key, value] of Object.entries(data)) {
12
+ // eslint-disable-next-line no-use-before-define
13
+ properties[key] = deriveSchemaFromValue(value)
14
+ }
15
+ return properties
16
+ }
17
+
18
+ /**
19
+ * Derives a schema from a given value.
20
+ *
21
+ * @param {any} data
22
+ * @returns {import('../types').DataSchema}
23
+ */
24
+ export function deriveSchemaFromValue(data) {
25
+ const schema = { type: typeOf(data) }
26
+ if (schema.type === 'array') {
27
+ schema.items = deriveSchemaFromValue(data.length > 0 ? data[0] : {})
28
+ } else if (schema.type === 'object') {
29
+ schema.properties = deriveObjectProperties(data)
30
+ }
31
+ return schema
32
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Generate the CSS position properties and values based on the anchor and viewport dimensions.
3
+ *
4
+ * @param {DOMRect} bounds - The bounding rectangle of the anchor element.
5
+ * @param {HTMLElement} viewport - The viewport element that determines the position.
6
+ * @returns {string} - A string with CSS position properties and values.
7
+ */
8
+ function generatePositionCSS(bounds, viewport) {
9
+ const { width: viewportWidth, height: viewportHeight } = viewport.getBoundingClientRect()
10
+ let pos = ''
11
+ if (bounds.left + viewportWidth > window.innerWidth) {
12
+ pos += `right: ${window.innerWidth - bounds.left - bounds.width}px;`
13
+ } else pos += `left: ${bounds.left}px;`
14
+
15
+ if (bounds.top + viewportHeight > window.innerHeight) {
16
+ pos += `bottom: ${window.innerHeight - bounds.top}px;`
17
+ } else pos += `top: ${bounds.top + bounds.height}px;`
18
+
19
+ return pos
20
+ }
21
+
22
+ /**
23
+ * Get the optimal position for the list based on anchor and viewport dimensions.
24
+ *
25
+ * @param {HTMLElement} anchor - The anchor element to position the list relative to.
26
+ * @param {HTMLElement} viewport - The viewport element that determines the position.
27
+ * @returns {string} - A string with CSS position properties and values.
28
+ */
29
+ export function getListPosition(anchor, viewport) {
30
+ if (typeof window === 'undefined' || !anchor || !viewport) return ''
31
+
32
+ const bounds = anchor.getBoundingClientRect()
33
+
34
+ bounds.top += window.scrollX
35
+ bounds.left += window.scrollY
36
+
37
+ return generatePositionCSS(bounds, viewport)
38
+ }
package/src/lib/tree.js CHANGED
@@ -13,7 +13,7 @@ export function addRootNode(items, root = '/', mapping = defaultMapping) {
13
13
  return [
14
14
  {
15
15
  [mapping.fields.text]: root,
16
- [mapping.fields.isOpen]: true,
16
+ [mapping.fields.expanded]: true,
17
17
  [mapping.fields.children]: items
18
18
  }
19
19
  ]
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import { getContext } from 'svelte'
3
+
4
+ const registry = getContext('registry')
5
+
6
+ let {
7
+ class: className = '',
8
+ options = [],
9
+ fields,
10
+ navigator = 'tabs',
11
+ type = 'vertical',
12
+ category = null,
13
+ children,
14
+ ...restProps
15
+ } = $props()
16
+
17
+ let Template = $derived(registry.navigators[navigator] ?? registry.navigators.default)
18
+ </script>
19
+
20
+ <section class={className}>
21
+ {#if Template}
22
+ <Template {options} {fields} bind:value={category} {...restProps} />
23
+ {/if}
24
+ <field-layout class={type}>
25
+ {@render children?.()}
26
+ </field-layout>
27
+ </section>
@@ -0,0 +1,16 @@
1
+ <script>
2
+ let {
3
+ class: className = '',
4
+ title = 'Section',
5
+ open = true,
6
+ type = 'vertical',
7
+ children
8
+ } = $props()
9
+ </script>
10
+
11
+ <details bind:open class={className}>
12
+ <summary>{title}</summary>
13
+ <field-layout class={type}>
14
+ {@render children?.()}
15
+ </field-layout>
16
+ </details>
@@ -0,0 +1,12 @@
1
+ <script>
2
+ let { class: className = '', type = 'vertical', title = '', children } = $props()
3
+ </script>
4
+
5
+ <field-layout
6
+ class={className}
7
+ class:vertical={type === 'vertical'}
8
+ class:horizontal={type === 'horizontal'}
9
+ aria-label={title}
10
+ >
11
+ {@render children?.()}
12
+ </field-layout>
@@ -0,0 +1,3 @@
1
+ export { default as Wrapper } from './Wrapper.svelte'
2
+ export { default as Section } from './Section.svelte'
3
+ export { default as Category } from './Category.svelte'