@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.
- package/dist/index.d.ts +12 -1
- package/dist/input/types.d.ts +9 -0
- package/dist/lib/fields.d.ts +16 -0
- package/dist/lib/form.d.ts +95 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/layout.d.ts +7 -0
- package/dist/lib/nested.d.ts +48 -0
- package/dist/lib/schema.d.ts +7 -0
- package/dist/lib/select.d.ts +8 -0
- package/dist/wrappers/index.d.ts +3 -0
- package/package.json +1 -1
- package/src/Accordion.svelte +61 -33
- package/src/BreadCrumbs.svelte +9 -5
- package/src/Carousel.svelte +49 -0
- package/src/DataEditor.svelte +31 -0
- package/src/DropDown.svelte +68 -0
- package/src/DropSearch.svelte +37 -0
- package/src/FieldLayout.svelte +48 -0
- package/src/Fillable.svelte +19 -0
- package/src/Form.svelte +17 -0
- package/src/Icon.svelte +1 -0
- package/src/Item.svelte +12 -11
- package/src/Link.svelte +2 -2
- package/src/List.svelte +52 -31
- package/src/ListBody.svelte +43 -0
- package/src/ListEditor.svelte +44 -0
- package/src/MultiSelect.svelte +48 -0
- package/src/NestedEditor.svelte +88 -0
- package/src/NestedList.svelte +45 -35
- package/src/NestedPaginator.svelte +59 -0
- package/src/Node.svelte +36 -23
- package/src/PageNavigator.svelte +94 -0
- package/src/ProgressDots.svelte +53 -0
- package/src/RadioGroup.svelte +3 -2
- package/src/ResponsiveGrid.svelte +1 -1
- package/src/Select.svelte +108 -0
- package/src/Slider.svelte +14 -0
- package/src/Stage.svelte +41 -0
- package/src/Stepper.svelte +66 -0
- package/src/Summary.svelte +5 -8
- package/src/Switch.svelte +8 -5
- package/src/TableCell.svelte +1 -1
- package/src/Tabs.svelte +23 -15
- package/src/Toggle.svelte +7 -7
- package/src/Tree.svelte +46 -19
- package/src/TreeTable.svelte +6 -5
- package/src/index.js +14 -1
- package/src/input/Input.svelte +17 -0
- package/src/input/InputField.svelte +69 -0
- package/src/input/InputSelect.svelte +23 -0
- package/src/input/InputSwitch.svelte +19 -0
- package/src/input/types.js +29 -0
- package/src/lib/fields.js +118 -0
- package/src/lib/form.js +72 -0
- package/src/lib/index.js +12 -0
- package/src/lib/layout.js +63 -0
- package/src/lib/nested.js +192 -0
- package/src/lib/schema.js +32 -0
- package/src/lib/select.js +38 -0
- package/src/lib/tree.js +1 -1
- package/src/wrappers/Category.svelte +27 -0
- package/src/wrappers/Section.svelte +16 -0
- package/src/wrappers/Wrapper.svelte +12 -0
- 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
|
@@ -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>
|