@rokkit/ui 1.0.0-next.122 → 1.0.0-next.123
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 +1 -0
- package/package.json +3 -3
- package/src/Card.svelte +6 -2
- package/src/GraphPaper.svelte +43 -0
- package/src/PickOne.svelte +60 -0
- package/src/ProgressDots.svelte +1 -1
- package/src/Stepper.svelte +2 -2
- package/src/Summary.svelte +1 -1
- package/src/Tabs.svelte +45 -35
- package/src/index.js +1 -0
- package/src/TabsOld.svelte +0 -96
package/dist/index.d.ts
CHANGED
|
@@ -38,3 +38,4 @@ export { default as ProgressDots } from "./ProgressDots.svelte";
|
|
|
38
38
|
export { default as Card } from "./Card.svelte";
|
|
39
39
|
export { default as Shine } from "./Shine.svelte";
|
|
40
40
|
export { default as Tilt } from "./Tilt.svelte";
|
|
41
|
+
export { default as GraphPaper } from "./GraphPaper.svelte";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/ui",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.123",
|
|
4
4
|
"description": "Data driven UI components, improving DX",
|
|
5
5
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@rokkit/states": "latest",
|
|
39
39
|
"d3-scale": "^4.0.2",
|
|
40
40
|
"date-fns": "^4.1.0",
|
|
41
|
-
"ramda": "^0.
|
|
42
|
-
"typescript": "^5.
|
|
41
|
+
"ramda": "^0.31.3",
|
|
42
|
+
"typescript": "^5.9.2"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/Card.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { Proxy } from '@rokkit/states'
|
|
3
|
-
let { class: classNames = '', value = $bindable(), fields, child, onClick } = $props()
|
|
3
|
+
let { class: classNames = '', value = $bindable(), fields, child, children, onClick } = $props()
|
|
4
4
|
|
|
5
5
|
const proxy = $state(new Proxy(value, fields))
|
|
6
6
|
const childSnippet = $derived(child ?? defaultChild)
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
tabindex="-1"
|
|
37
37
|
class={classNames}
|
|
38
38
|
>
|
|
39
|
-
{
|
|
39
|
+
{#if children}
|
|
40
|
+
{@render children()}
|
|
41
|
+
{:else}
|
|
42
|
+
{@render childSnippet(proxy)}
|
|
43
|
+
{/if}
|
|
40
44
|
</div>
|
|
41
45
|
{/if}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let {
|
|
3
|
+
class: className = '',
|
|
4
|
+
unit = '.5rem',
|
|
5
|
+
majorGridSize = 5,
|
|
6
|
+
minorGridThickness = 0.5,
|
|
7
|
+
majorGridThickness = 0.5,
|
|
8
|
+
children
|
|
9
|
+
} = $props()
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
data-graph-paper
|
|
14
|
+
class="flex flex-col {className}"
|
|
15
|
+
style:--unit={unit}
|
|
16
|
+
style:--size="calc( {majorGridSize} * {unit})"
|
|
17
|
+
style:--minor-grid="{minorGridThickness}px"
|
|
18
|
+
style:--major-grid="{majorGridThickness}px"
|
|
19
|
+
>
|
|
20
|
+
<content class="flex flex-col">
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</content>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
[data-graph-paper] {
|
|
27
|
+
background-image:
|
|
28
|
+
linear-gradient(currentColor var(--major-grid), transparent var(--major-grid)),
|
|
29
|
+
linear-gradient(90deg, currentColor var(--major-grid), transparent var(--major-grid)),
|
|
30
|
+
linear-gradient(currentColor var(--minor-grid), transparent var(--minor-grid)),
|
|
31
|
+
linear-gradient(90deg, currentColor var(--minor-grid), transparent var(--minor-grid));
|
|
32
|
+
background-size:
|
|
33
|
+
var(--size) var(--size),
|
|
34
|
+
var(--size) var(--size),
|
|
35
|
+
var(--unit) var(--unit),
|
|
36
|
+
var(--unit) var(--unit);
|
|
37
|
+
background-position:
|
|
38
|
+
calc(-1 * var(--minor-grid)) calc(-1 * var(--minor-grid)),
|
|
39
|
+
calc(-1 * var(--minor-grid)) calc(-1 * var(--minor-grid)),
|
|
40
|
+
calc(-1 * var(--minor-grid)) calc(-1 * var(--minor-grid)),
|
|
41
|
+
calc(-1 * var(--minor-grid)) calc(-1 * var(--minor-grid));
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// import { onMount } from 'svelte'
|
|
3
|
+
// import { $state, $derived } from 'svelte'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} Props
|
|
7
|
+
* @property {any[]} options
|
|
8
|
+
* @property {any} value
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** @type Props */
|
|
12
|
+
let { options, value = $bindable() } = $props()
|
|
13
|
+
|
|
14
|
+
// const optionRefs = new Map<number, HTMLLabelElement>()
|
|
15
|
+
|
|
16
|
+
// const $indicatorStyle = $derived(() => {
|
|
17
|
+
// const el = optionRefs.get(value)
|
|
18
|
+
// if (!el) return ''
|
|
19
|
+
// const { offsetLeft, offsetWidth } = el
|
|
20
|
+
// return `transform: translateX(${offsetLeft}px); width: ${offsetWidth}px;`
|
|
21
|
+
// })
|
|
22
|
+
|
|
23
|
+
// onMount(() => {
|
|
24
|
+
// setTimeout(() => {
|
|
25
|
+
// optionRefs.get(value)?.offsetLeft
|
|
26
|
+
// })
|
|
27
|
+
// })
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div class="relative inline-flex rounded-md bg-gray-200 p-1" data-pickone-root role="radiogroup">
|
|
31
|
+
<!-- Indicator -->
|
|
32
|
+
<div
|
|
33
|
+
class="absolute h-full rounded-md bg-white shadow transition-all duration-300"
|
|
34
|
+
data-pickone-current
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
></div>
|
|
37
|
+
|
|
38
|
+
{#each options as option, index (index)}
|
|
39
|
+
<div
|
|
40
|
+
class="relative z-10 cursor-pointer text-sm font-medium text-gray-700"
|
|
41
|
+
data-pickone-option
|
|
42
|
+
aria-checked={option === value}
|
|
43
|
+
role="radio"
|
|
44
|
+
>
|
|
45
|
+
<label class="bg-red flex size-full px-4 py-2">
|
|
46
|
+
<input
|
|
47
|
+
type="radio"
|
|
48
|
+
name="pickone"
|
|
49
|
+
value={option}
|
|
50
|
+
class="peer hidden"
|
|
51
|
+
checked={option === value}
|
|
52
|
+
onchange={() => (value = option)}
|
|
53
|
+
/>
|
|
54
|
+
<span class="transition peer-checked:font-semibold peer-checked:text-black">
|
|
55
|
+
{option}
|
|
56
|
+
</span>
|
|
57
|
+
</label>
|
|
58
|
+
</div>
|
|
59
|
+
{/each}
|
|
60
|
+
</div>
|
package/src/ProgressDots.svelte
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
{#each steps as step, index (index)}
|
|
25
25
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
26
26
|
<dot
|
|
27
|
-
class="step flex h-3 w-3 rounded-full border-2
|
|
27
|
+
class="step border-surface-100 bg-surface-300 flex h-3 w-3 rounded-full border-2"
|
|
28
28
|
on:click={handleClick}
|
|
29
29
|
data-step={step}
|
|
30
30
|
data-active={step === current}
|
package/src/Stepper.svelte
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
{#each data as { label }, stage (stage)}
|
|
46
46
|
{#if label}
|
|
47
47
|
<p
|
|
48
|
-
class="col-span-3 flex w-full justify-center text-center font-medium leading-loose
|
|
48
|
+
class="text-surface-800 col-span-3 flex w-full justify-center text-center font-medium leading-loose"
|
|
49
49
|
class:pending={stage > currentStage}
|
|
50
50
|
>
|
|
51
51
|
{label}
|
|
@@ -61,6 +61,6 @@
|
|
|
61
61
|
grid-template-columns: repeat(var(--count), 2fr 6fr 2fr);
|
|
62
62
|
}
|
|
63
63
|
.pending {
|
|
64
|
-
@apply
|
|
64
|
+
@apply text-surface-500 font-light;
|
|
65
65
|
}
|
|
66
66
|
</style>
|
package/src/Summary.svelte
CHANGED
package/src/Tabs.svelte
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @typedef {Object} TabProps
|
|
19
19
|
* @property {string} [class] - Additional CSS class names
|
|
20
20
|
* @property {string} [name] - Name for accessibility
|
|
21
|
-
* @property {any[]} [
|
|
21
|
+
* @property {any[]} [options] - Array of tab options to display
|
|
22
22
|
* @property {FieldMapping} [fields] - Field mappings for extracting data
|
|
23
23
|
* @property {'horizontal'|'vertical'} [orientation] - Orientation of the tab bar
|
|
24
24
|
* @property {'before' | 'after' } [position] - Position of the tab bar
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* @property {number} [tabindex] - Tab index for keyboard navigation
|
|
28
28
|
* @property {boolean} [editable] - Whether tabs can be added/removed
|
|
29
29
|
* @property {string} [placeholder] - Placeholder text for input field
|
|
30
|
-
* @property {import('svelte').Snippet} [
|
|
31
|
-
* @property {import('svelte').Snippet} [
|
|
30
|
+
* @property {import('svelte').Snippet} [tabItem] - Snippet for rendering tab headers
|
|
31
|
+
* @property {import('svelte').Snippet} [tabPanel] - Snippet for rendering tab content
|
|
32
32
|
* @property {import('svelte').Snippet} [empty] - Snippet for rendering empty state
|
|
33
33
|
* @property {Function} [onselect] - Callback when tab is selected
|
|
34
34
|
* @property {Function} [onchange] - Callback when tab changes
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
let {
|
|
42
42
|
class: classes = '',
|
|
43
43
|
name = 'tabs',
|
|
44
|
-
|
|
44
|
+
options = $bindable([]),
|
|
45
45
|
fields = {},
|
|
46
46
|
value = $bindable(),
|
|
47
47
|
orientation = 'horizontal',
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
position = 'before',
|
|
50
50
|
tabindex = 0,
|
|
51
51
|
editable = false,
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
tabItem,
|
|
53
|
+
tabPanel,
|
|
54
54
|
empty,
|
|
55
55
|
placeholder = 'Select a tab to view its content.',
|
|
56
56
|
icons,
|
|
@@ -59,15 +59,14 @@
|
|
|
59
59
|
onmove,
|
|
60
60
|
onadd,
|
|
61
61
|
onremove,
|
|
62
|
-
...
|
|
62
|
+
...restProps
|
|
63
63
|
} = $props()
|
|
64
64
|
|
|
65
65
|
/** @type {Proxy[]} */
|
|
66
|
-
let proxyItems = $derived(
|
|
67
|
-
let
|
|
68
|
-
let
|
|
66
|
+
let proxyItems = $derived(options.map((item) => new Proxy(item, fields)))
|
|
67
|
+
let tabItemSnippet = $derived(tabItem ?? defaultItem)
|
|
68
|
+
let tabPanelSnippet = $derived(tabPanel ?? defaultPanel)
|
|
69
69
|
let emptyMessage = $derived(empty ?? defaultEmpty)
|
|
70
|
-
let activeItem = $derived(proxyItems.find((proxy) => equals(proxy.value, value)))
|
|
71
70
|
|
|
72
71
|
function handleAction(event) {
|
|
73
72
|
const { name, data } = event.detail
|
|
@@ -87,18 +86,17 @@
|
|
|
87
86
|
}
|
|
88
87
|
let tabIcons = $derived({ ...pick(['add', 'close'], defaultStateIcons.action), ...icons })
|
|
89
88
|
let emitter = createEmitter({ onchange, onmove, onselect }, ['select', 'change', 'move'])
|
|
90
|
-
let wrapper = new ListController(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
})
|
|
89
|
+
let wrapper = new ListController(options, value, fields)
|
|
90
|
+
|
|
91
|
+
$effect(() => wrapper.update(options))
|
|
94
92
|
</script>
|
|
95
93
|
|
|
96
|
-
{#snippet
|
|
94
|
+
{#snippet defaultItem(item)}
|
|
97
95
|
{item.get('text') || item.get('label') || item.get('name')}
|
|
98
96
|
{/snippet}
|
|
99
97
|
|
|
100
|
-
{#snippet
|
|
101
|
-
<div data-
|
|
98
|
+
{#snippet defaultPanel(item)}
|
|
99
|
+
<div data-tabs-content>
|
|
102
100
|
{item.get('content')}
|
|
103
101
|
</div>
|
|
104
102
|
{/snippet}
|
|
@@ -108,6 +106,7 @@
|
|
|
108
106
|
{/snippet}
|
|
109
107
|
|
|
110
108
|
<div
|
|
109
|
+
{...restProps}
|
|
111
110
|
data-tabs-root
|
|
112
111
|
data-orientation={orientation}
|
|
113
112
|
data-position={position}
|
|
@@ -119,12 +118,21 @@
|
|
|
119
118
|
{tabindex}
|
|
120
119
|
onaction={handleAction}
|
|
121
120
|
>
|
|
121
|
+
{#if proxyItems.length === 0}
|
|
122
|
+
<div data-tabs-empty>
|
|
123
|
+
{@render emptyMessage()}
|
|
124
|
+
</div>
|
|
125
|
+
{:else if wrapper.focusedKey === null && value === null}
|
|
126
|
+
<div data-tabs-placeholder>
|
|
127
|
+
{placeholder}
|
|
128
|
+
</div>
|
|
129
|
+
{/if}
|
|
122
130
|
<div data-tabs-list>
|
|
123
131
|
{#each proxyItems as item, index (index)}
|
|
124
132
|
{@const key = getKeyFromPath([index])}
|
|
125
133
|
{@const isSelected = equals(item.value, value)}
|
|
126
134
|
{@const isFocused = wrapper.focusedKey === key}
|
|
127
|
-
<
|
|
135
|
+
<button
|
|
128
136
|
data-tabs-trigger
|
|
129
137
|
data-path={getKeyFromPath([index])}
|
|
130
138
|
role="tab"
|
|
@@ -132,8 +140,10 @@
|
|
|
132
140
|
aria-controls="tab-panel-{index}"
|
|
133
141
|
class:selected={isSelected}
|
|
134
142
|
class:focused={isFocused}
|
|
143
|
+
tabindex="0"
|
|
144
|
+
id={`tab-${index}`}
|
|
135
145
|
>
|
|
136
|
-
{@render
|
|
146
|
+
{@render tabItemSnippet(item)}
|
|
137
147
|
{#if editable}
|
|
138
148
|
<Icon
|
|
139
149
|
data-icon-remove
|
|
@@ -142,25 +152,25 @@
|
|
|
142
152
|
onclick={() => handleRemove(item.value)}
|
|
143
153
|
/>
|
|
144
154
|
{/if}
|
|
145
|
-
</
|
|
155
|
+
</button>
|
|
146
156
|
{/each}
|
|
147
157
|
{#if editable}
|
|
148
158
|
<Icon data-icon-add name={tabIcons.add} role="button" onclick={handleAdd} />
|
|
149
159
|
{/if}
|
|
150
160
|
</div>
|
|
151
161
|
|
|
152
|
-
<!-- Tab
|
|
153
|
-
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
<!-- Tab Panels -->
|
|
163
|
+
{#each proxyItems as item, index (index)}
|
|
164
|
+
{@const isVisible = equals(item.value, value)}
|
|
165
|
+
|
|
166
|
+
<div
|
|
167
|
+
data-tabs-panel
|
|
168
|
+
role="tabpanel"
|
|
169
|
+
id="tab-panel-{index}"
|
|
170
|
+
aria-labelledby="tab-{index}"
|
|
171
|
+
data-panel-active={isVisible}
|
|
172
|
+
>
|
|
173
|
+
{@render tabPanelSnippet(item)}
|
|
174
|
+
</div>
|
|
175
|
+
{/each}
|
|
166
176
|
</div>
|
package/src/index.js
CHANGED
|
@@ -45,3 +45,4 @@ export { default as ProgressDots } from './ProgressDots.svelte'
|
|
|
45
45
|
export { default as Card } from './Card.svelte'
|
|
46
46
|
export { default as Shine } from './Shine.svelte'
|
|
47
47
|
export { default as Tilt } from './Tilt.svelte'
|
|
48
|
+
export { default as GraphPaper } from './GraphPaper.svelte'
|
package/src/TabsOld.svelte
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { defaultFields, defaultStateIcons, noop, getSnippet, FieldMapper } from '@rokkit/core'
|
|
3
|
-
import { ListController } from '@rokkit/states'
|
|
4
|
-
import { navigator } from '@rokkit/actions'
|
|
5
|
-
import Icon from './Icon.svelte'
|
|
6
|
-
import Item from './Item.svelte'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} Props
|
|
10
|
-
* @property {string} [class]
|
|
11
|
-
* @property {any} [options]
|
|
12
|
-
* @property {import('@rokkit/core').FieldMapping} [fields]
|
|
13
|
-
* @property {any} [value]
|
|
14
|
-
* @property {boolean} [below]
|
|
15
|
-
* @property {string} [align]
|
|
16
|
-
* @property {boolean} [editable]
|
|
17
|
-
* @property {any} [icons]
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/** @type {Props} */
|
|
21
|
-
let {
|
|
22
|
-
class: className = '',
|
|
23
|
-
options = $bindable([]),
|
|
24
|
-
value = $bindable(null),
|
|
25
|
-
icons = $bindable(defaultStateIcons.action),
|
|
26
|
-
fields = defaultFields,
|
|
27
|
-
below = false,
|
|
28
|
-
align = 'left',
|
|
29
|
-
editable = false,
|
|
30
|
-
onremove = noop,
|
|
31
|
-
onadd = noop,
|
|
32
|
-
onselect = noop,
|
|
33
|
-
stub,
|
|
34
|
-
...extra
|
|
35
|
-
} = $props()
|
|
36
|
-
|
|
37
|
-
function handleRemove(event) {
|
|
38
|
-
if (typeof event.detail === Object) {
|
|
39
|
-
event.detail[fields.isDeleted] = true
|
|
40
|
-
} else {
|
|
41
|
-
options = options.filter((i) => i !== event.detail)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
onremove({ item: event.detail })
|
|
45
|
-
}
|
|
46
|
-
function handleAdd(event) {
|
|
47
|
-
event.stopPropagation()
|
|
48
|
-
onadd()
|
|
49
|
-
}
|
|
50
|
-
function handleNav(event) {
|
|
51
|
-
value = event.detail.node
|
|
52
|
-
cursor = event.detail.path
|
|
53
|
-
|
|
54
|
-
onselect({ item: value, indices: cursor })
|
|
55
|
-
}
|
|
56
|
-
let stateIcons = $derived({ ...defaultStateIcons.action, ...icons })
|
|
57
|
-
let filtered = $derived(options.filter((item) => !item[fields.deleted]))
|
|
58
|
-
let wrapper = $derived(new ListController(options, value, fields))
|
|
59
|
-
let mapper = new FieldMapper(fields)
|
|
60
|
-
</script>
|
|
61
|
-
|
|
62
|
-
<rk-tabs
|
|
63
|
-
class="flex w-full {className}"
|
|
64
|
-
class:is-below={below}
|
|
65
|
-
class:justify-center={align === 'center'}
|
|
66
|
-
class:justify-end={align === 'right'}
|
|
67
|
-
tabindex="0"
|
|
68
|
-
role="listbox"
|
|
69
|
-
use:navigator={{ wrapper, horizontal: true }}
|
|
70
|
-
onaction={handleNav}
|
|
71
|
-
onremove={handleRemove}
|
|
72
|
-
onadd={handleAdd}
|
|
73
|
-
>
|
|
74
|
-
{#each filtered as item, index (index)}
|
|
75
|
-
{@const Template = getSnippet(extra, mapper.get('snippet', item), stub)}
|
|
76
|
-
<rk-tab>
|
|
77
|
-
{#if Template}
|
|
78
|
-
<Template value={item} {fields} />
|
|
79
|
-
{:else}
|
|
80
|
-
<Item value={item} {fields} />
|
|
81
|
-
{/if}
|
|
82
|
-
{#if editable}
|
|
83
|
-
<Icon
|
|
84
|
-
name="remove"
|
|
85
|
-
role="button"
|
|
86
|
-
label="Delete Tab"
|
|
87
|
-
size="small"
|
|
88
|
-
onclick={() => handleRemove(item)}
|
|
89
|
-
/>
|
|
90
|
-
{/if}
|
|
91
|
-
</rk-tab>
|
|
92
|
-
{/each}
|
|
93
|
-
{#if editable}
|
|
94
|
-
<Icon name="add" role="button" label="Add Tab" size="small" onclick={handleAdd} />
|
|
95
|
-
{/if}
|
|
96
|
-
</rk-tabs>
|