@rokkit/ui 1.0.0-next.100 → 1.0.0-next.106
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/constants.d.ts +2 -0
- package/dist/index.d.ts +30 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/tree.d.ts +9 -0
- package/dist/types.d.ts +5 -0
- package/package.json +16 -40
- package/src/Accordion.svelte +78 -0
- package/src/BreadCrumbs.svelte +33 -0
- package/src/Calendar.svelte +93 -0
- package/src/CheckBox.svelte +56 -0
- package/src/Connector.svelte +39 -0
- package/src/Icon.svelte +71 -0
- package/src/Item.svelte +24 -0
- package/src/Link.svelte +21 -0
- package/src/List.svelte +67 -0
- package/src/Message.svelte +11 -0
- package/src/NestedList.svelte +68 -0
- package/src/Node.svelte +64 -0
- package/src/Overlay.svelte +21 -0
- package/src/Pill.svelte +41 -0
- package/src/ProgressBar.svelte +21 -0
- package/src/RadioGroup.svelte +51 -0
- package/src/Range.svelte +45 -0
- package/src/RangeMinMax.svelte +124 -0
- package/src/RangeSlider.svelte +79 -0
- package/src/RangeTick.svelte +28 -0
- package/src/Rating.svelte +95 -0
- package/src/ResponsiveGrid.svelte +88 -0
- package/src/Scrollable.svelte +7 -0
- package/src/Separator.svelte +1 -0
- package/src/SlidingColumns.svelte +52 -0
- package/src/Summary.svelte +26 -0
- package/src/Switch.svelte +86 -0
- package/src/TableCell.svelte +51 -0
- package/src/TableHeaderCell.svelte +54 -0
- package/src/Tabs.svelte +88 -0
- package/src/Toggle.svelte +54 -0
- package/src/ToggleThemeMode.svelte +19 -0
- package/src/Tree.svelte +53 -0
- package/src/TreeTable.svelte +170 -0
- package/src/ValidationReport.svelte +23 -0
- package/src/constants.js +4 -0
- package/src/index.js +34 -8
- package/src/lib/index.js +1 -0
- package/src/lib/tree.js +22 -0
- package/src/types.js +9 -0
- package/LICENSE +0 -21
- package/README.md +0 -3
- package/src/wrappers.js +0 -2
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { default as Icon } from "./Icon.svelte";
|
|
3
|
+
export { default as Item } from "./Item.svelte";
|
|
4
|
+
export { default as Pill } from "./Pill.svelte";
|
|
5
|
+
export { default as ProgressBar } from "./ProgressBar.svelte";
|
|
6
|
+
export { default as Separator } from "./Separator.svelte";
|
|
7
|
+
export { default as Connector } from "./Connector.svelte";
|
|
8
|
+
export { default as RangeTick } from "./RangeTick.svelte";
|
|
9
|
+
export { default as RangeSlider } from "./RangeSlider.svelte";
|
|
10
|
+
export { default as Node } from "./Node.svelte";
|
|
11
|
+
export { default as Summary } from "./Summary.svelte";
|
|
12
|
+
export { default as BreadCrumbs } from "./BreadCrumbs.svelte";
|
|
13
|
+
export { default as Rating } from "./Rating.svelte";
|
|
14
|
+
export { default as RangeMinMax } from "./RangeMinMax.svelte";
|
|
15
|
+
export { default as Range } from "./Range.svelte";
|
|
16
|
+
export { default as RadioGroup } from "./RadioGroup.svelte";
|
|
17
|
+
export { default as CheckBox } from "./CheckBox.svelte";
|
|
18
|
+
export { default as Calendar } from "./Calendar.svelte";
|
|
19
|
+
export { default as ValidationReport } from "./ValidationReport.svelte";
|
|
20
|
+
export { default as ResponsiveGrid } from "./ResponsiveGrid.svelte";
|
|
21
|
+
export { default as Toggle } from "./Toggle.svelte";
|
|
22
|
+
export { default as Switch } from "./Switch.svelte";
|
|
23
|
+
export { default as List } from "./List.svelte";
|
|
24
|
+
export { default as Accordion } from "./Accordion.svelte";
|
|
25
|
+
export { default as NestedList } from "./NestedList.svelte";
|
|
26
|
+
export { default as Tree } from "./Tree.svelte";
|
|
27
|
+
export { default as ToggleThemeMode } from "./ToggleThemeMode.svelte";
|
|
28
|
+
export { default as Overlay } from "./Overlay.svelte";
|
|
29
|
+
export { default as Message } from "./Message.svelte";
|
|
30
|
+
export { default as SlidingColumns } from "./SlidingColumns.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { addRootNode } from "./tree";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds a root node to the items array converting it into a nested tree with one root node
|
|
3
|
+
*
|
|
4
|
+
* @param {Array<Object>} items
|
|
5
|
+
* @param {string} root
|
|
6
|
+
* @param {import('@rokkit/core').FieldMapping} fields
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export function addRootNode(items: Array<Object>, root?: string, mapping?: import("@rokkit/core").FieldMapper): Object[];
|
package/dist/types.d.ts
ADDED
package/package.json
CHANGED
|
@@ -1,63 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/ui",
|
|
3
|
-
"version": "1.0.0-next.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0-next.106",
|
|
4
|
+
"description": "Organisms are larger, more complex building blocks that are composed of multiple molecules",
|
|
5
5
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "index.js",
|
|
8
7
|
"module": "src/index.js",
|
|
9
|
-
"types": "dist/index.d.ts",
|
|
10
8
|
"type": "module",
|
|
11
9
|
"publishConfig": {
|
|
12
10
|
"access": "public"
|
|
13
11
|
},
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"@vitest/ui": "~2.1.1",
|
|
19
|
-
"jsdom": "^25.0.0",
|
|
20
|
-
"svelte": "^4.2.19",
|
|
21
|
-
"typescript": "^5.6.2",
|
|
22
|
-
"vite": "^5.4.6",
|
|
23
|
-
"vitest": "~2.1.1",
|
|
24
|
-
"shared-config": "1.0.0-next.100",
|
|
25
|
-
"validators": "1.0.0-next.100"
|
|
12
|
+
"scripts": {
|
|
13
|
+
"prepublishOnly": "tsc --project tsconfig.build.json",
|
|
14
|
+
"clean": "rm -rf dist",
|
|
15
|
+
"build": "bun clean && bun prepublishOnly"
|
|
26
16
|
},
|
|
27
17
|
"files": [
|
|
28
18
|
"src/**/*.js",
|
|
29
19
|
"src/**/*.svelte",
|
|
30
|
-
"
|
|
31
|
-
"!src/**/*.spec.js"
|
|
20
|
+
"dist/**/*.d.ts"
|
|
32
21
|
],
|
|
33
22
|
"exports": {
|
|
34
23
|
"./src": "./src",
|
|
35
|
-
"./wrappers": "./src/wrappers/index.js",
|
|
36
24
|
"./package.json": "./package.json",
|
|
25
|
+
"./utils": "./src/lib/index.js",
|
|
37
26
|
".": {
|
|
38
27
|
"types": "./dist/index.d.ts",
|
|
39
|
-
"import": "./src/index.js"
|
|
40
|
-
"svelte": "./src/index.js"
|
|
28
|
+
"import": "./src/index.js"
|
|
41
29
|
}
|
|
42
30
|
},
|
|
43
31
|
"dependencies": {
|
|
44
|
-
"
|
|
45
|
-
"@rokkit/
|
|
46
|
-
"@rokkit/
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"@rokkit/organisms": "1.0.0-next.100"
|
|
51
|
-
},
|
|
52
|
-
"scripts": {
|
|
53
|
-
"format": "prettier --write .",
|
|
54
|
-
"lint": "eslint --fix .",
|
|
55
|
-
"test:ct": "playwright test -c playwright-ct.config.ts",
|
|
56
|
-
"test:ci": "vitest run",
|
|
57
|
-
"test:ui": "vitest --ui",
|
|
58
|
-
"test": "vitest",
|
|
59
|
-
"coverage": "vitest run --coverage",
|
|
60
|
-
"latest": "pnpm upgrade --latest && pnpm test:ci",
|
|
61
|
-
"release": "pnpm publish --access public"
|
|
32
|
+
"@rokkit/actions": "latest",
|
|
33
|
+
"@rokkit/core": "latest",
|
|
34
|
+
"@rokkit/states": "latest",
|
|
35
|
+
"d3-scale": "^4.0.2",
|
|
36
|
+
"date-fns": "^4.1.0",
|
|
37
|
+
"ramda": "^0.30.1"
|
|
62
38
|
}
|
|
63
|
-
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { equals } from 'ramda'
|
|
3
|
+
import { createEmitter, noop, getKeyFromPath } from '@rokkit/core'
|
|
4
|
+
import { DataWrapper } from '@rokkit/states'
|
|
5
|
+
import { navigator } from '@rokkit/actions'
|
|
6
|
+
import { defaultMapping } from './constants'
|
|
7
|
+
import Summary from './Summary.svelte'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} Props
|
|
11
|
+
* @property {string} [class]
|
|
12
|
+
* @property {any} [items]
|
|
13
|
+
* @property {import('@rokkit/core').FieldMapper} [mapping]
|
|
14
|
+
* @property {boolean} [autoCloseSiblings]
|
|
15
|
+
* @property {boolean} [multiselect]
|
|
16
|
+
* @property {any} [value]
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @type {Props} */
|
|
20
|
+
let {
|
|
21
|
+
class: classes = '',
|
|
22
|
+
items = $bindable([]),
|
|
23
|
+
mapping = defaultMapping,
|
|
24
|
+
autoCloseSiblings = false,
|
|
25
|
+
multiselect = false,
|
|
26
|
+
value = $bindable(null),
|
|
27
|
+
...events
|
|
28
|
+
} = $props()
|
|
29
|
+
|
|
30
|
+
let emitter = $derived(
|
|
31
|
+
createEmitter(events, ['collapse', 'change', 'expand', 'click', 'select', 'move'])
|
|
32
|
+
)
|
|
33
|
+
let wrapper = new DataWrapper(items, mapping, value, { events, multiselect, autoCloseSiblings })
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
{#snippet listItems(items, wrapper, hierarchy = [], onchange = noop)}
|
|
37
|
+
{@const mapping = wrapper.mapping}
|
|
38
|
+
{#each items as item, index}
|
|
39
|
+
{@const Template = mapping.getComponent(item)}
|
|
40
|
+
{@const path = getKeyFromPath([...hierarchy, index])}
|
|
41
|
+
{@const props = mapping.getAttribute(item, 'props') || {}}
|
|
42
|
+
<rk-list-item
|
|
43
|
+
role="option"
|
|
44
|
+
data-path={path}
|
|
45
|
+
aria-selected={wrapper.selected.has(path)}
|
|
46
|
+
aria-current={equals(wrapper.currentNode, item)}
|
|
47
|
+
>
|
|
48
|
+
<Template bind:value={items[index]} {mapping} onchange={events.change} {...props} />
|
|
49
|
+
</rk-list-item>
|
|
50
|
+
{/each}
|
|
51
|
+
{/snippet}
|
|
52
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
53
|
+
<rk-accordion
|
|
54
|
+
class={classes}
|
|
55
|
+
tabindex="0"
|
|
56
|
+
use:navigator={{ wrapper }}
|
|
57
|
+
onactivate={() => (value = wrapper.value)}
|
|
58
|
+
>
|
|
59
|
+
{#each wrapper.data as item, index}
|
|
60
|
+
{@const hasItems = mapping.hasChildren(item)}
|
|
61
|
+
{@const id = 'id-' + index}
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
{id}
|
|
65
|
+
class="flex flex-col"
|
|
66
|
+
class:is-expanded={mapping.isExpanded(item)}
|
|
67
|
+
class:is-selected={equals(item, value)}
|
|
68
|
+
data-path={index}
|
|
69
|
+
>
|
|
70
|
+
<Summary {mapping} bind:value={items[index]} expanded={mapping.isExpanded(item)} />
|
|
71
|
+
{#if hasItems && mapping.isExpanded(item)}
|
|
72
|
+
<rk-list role="listbox" tabindex="-1">
|
|
73
|
+
{@render listItems(item[mapping.fields.children], wrapper, [index], events.change)}
|
|
74
|
+
</rk-list>
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
77
|
+
{/each}
|
|
78
|
+
</rk-accordion>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { defaultMapping } from './constants'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} Props
|
|
6
|
+
* @property {string} [class]
|
|
7
|
+
* @property {any} [items]
|
|
8
|
+
* @property {string} [separator]
|
|
9
|
+
* @property {any} [fields]
|
|
10
|
+
* @property {any} [using]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** @type {Props} */
|
|
14
|
+
let { class: classes = '', items = [], separator = '/', mapping = defaultMapping } = $props()
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<rk-crumbs class={classes}>
|
|
18
|
+
{#each items as item, index}
|
|
19
|
+
{@const Component = mapping.getComponent(item)}
|
|
20
|
+
{#if index > 0}
|
|
21
|
+
<span>
|
|
22
|
+
{#if separator.length === 1}
|
|
23
|
+
{separator}
|
|
24
|
+
{:else}
|
|
25
|
+
<icon class={separator}></icon>
|
|
26
|
+
{/if}
|
|
27
|
+
</span>
|
|
28
|
+
{/if}
|
|
29
|
+
<rk-crumb class:is-selected={index === items.length - 1}>
|
|
30
|
+
<Component value={item} {mapping} />
|
|
31
|
+
</rk-crumb>
|
|
32
|
+
{/each}
|
|
33
|
+
</rk-crumbs>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { format, isSameDay, addMonths } from 'date-fns'
|
|
3
|
+
import { weekdays, getCalendarDays } from '@rokkit/core'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} Props
|
|
7
|
+
* @property {any} [value]
|
|
8
|
+
* @property {any} [holidays]
|
|
9
|
+
* @property {boolean} [fixed]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {Props} */
|
|
13
|
+
let { value = $bindable(new Date()), holidays = [], fixed = true } = $props()
|
|
14
|
+
|
|
15
|
+
function handleChange(event) {
|
|
16
|
+
value = new Date(event.target.value, value.getMonth(), value.getDate())
|
|
17
|
+
}
|
|
18
|
+
function nextMonth() {
|
|
19
|
+
value = addMonths(value, 1)
|
|
20
|
+
}
|
|
21
|
+
function previousMonth() {
|
|
22
|
+
value = addMonths(value, -1)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let year = $derived(value.getFullYear())
|
|
26
|
+
let days = $derived(getCalendarDays(value, holidays, fixed))
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<calendar class="mx-auto flex select-none flex-col items-center">
|
|
30
|
+
<month-year class="flex h-10 w-full flex-row items-center">
|
|
31
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
32
|
+
<square
|
|
33
|
+
class="cursor-pointer select-none"
|
|
34
|
+
onclick={previousMonth}
|
|
35
|
+
role="option"
|
|
36
|
+
aria-selected={false}
|
|
37
|
+
tabindex="0"
|
|
38
|
+
>
|
|
39
|
+
<icon class="chevron-left"></icon>
|
|
40
|
+
</square>
|
|
41
|
+
<span class="gap-1px flex flex-grow items-center justify-center">
|
|
42
|
+
<p>{format(value, 'MMMM')}</p>
|
|
43
|
+
<input
|
|
44
|
+
type="number"
|
|
45
|
+
value={year}
|
|
46
|
+
class="flex w-14 flex-grow-0 border-none bg-transparent"
|
|
47
|
+
onchange={handleChange}
|
|
48
|
+
/>
|
|
49
|
+
</span>
|
|
50
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
51
|
+
<square
|
|
52
|
+
class="cursor-pointer select-none"
|
|
53
|
+
onclick={nextMonth}
|
|
54
|
+
role="option"
|
|
55
|
+
aria-selected={false}
|
|
56
|
+
tabindex="0"
|
|
57
|
+
>
|
|
58
|
+
<icon class="chevron-right"></icon>
|
|
59
|
+
</square>
|
|
60
|
+
</month-year>
|
|
61
|
+
<cal-body class="flex w-full cursor-pointer flex-col p-1">
|
|
62
|
+
<days-of-week class="grid grid-cols-7">
|
|
63
|
+
{#each weekdays as day, index}
|
|
64
|
+
<p class:weekend={index % 6 === 0}>
|
|
65
|
+
{day.slice(0, 1)}
|
|
66
|
+
</p>
|
|
67
|
+
{/each}
|
|
68
|
+
</days-of-week>
|
|
69
|
+
<days-of-month class="grid grid-cols-7 grid-rows-5">
|
|
70
|
+
{#each days as { day, offset, date, weekend }}
|
|
71
|
+
{@const start = offset > 0 ? offset : 'auto'}
|
|
72
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
73
|
+
<day-of-month
|
|
74
|
+
class:weekend
|
|
75
|
+
style:grid-column-start={start}
|
|
76
|
+
onclick={() => (value = date)}
|
|
77
|
+
role="option"
|
|
78
|
+
aria-selected={isSameDay(date, value)}
|
|
79
|
+
tabindex="0"
|
|
80
|
+
>
|
|
81
|
+
{day}
|
|
82
|
+
</day-of-month>
|
|
83
|
+
{/each}
|
|
84
|
+
</days-of-month>
|
|
85
|
+
</cal-body>
|
|
86
|
+
</calendar>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
days-of-week p,
|
|
90
|
+
day-of-month {
|
|
91
|
+
@apply flex items-center justify-center;
|
|
92
|
+
}
|
|
93
|
+
</style>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { defaultStateIcons } from '@rokkit/core'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} Props
|
|
6
|
+
* @property {string} [class]
|
|
7
|
+
* @property {any} [id]
|
|
8
|
+
* @property {any} name
|
|
9
|
+
* @property {boolean} [value]
|
|
10
|
+
* @property {boolean} [readOnly]
|
|
11
|
+
* @property {any} [stateIcons]
|
|
12
|
+
* @property {number} [tabindex]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** @type {Props} */
|
|
16
|
+
let {
|
|
17
|
+
class: classes = '',
|
|
18
|
+
id = null,
|
|
19
|
+
name,
|
|
20
|
+
value = $bindable(false),
|
|
21
|
+
readOnly = false,
|
|
22
|
+
stateIcons = defaultStateIcons.checkbox,
|
|
23
|
+
tabindex = 0
|
|
24
|
+
} = $props()
|
|
25
|
+
|
|
26
|
+
let state = $derived(value === null ? 'unknown' : value ? 'checked' : 'unchecked')
|
|
27
|
+
|
|
28
|
+
function toggle(event) {
|
|
29
|
+
event.preventDefault()
|
|
30
|
+
event.stopPropagation()
|
|
31
|
+
value = !value
|
|
32
|
+
}
|
|
33
|
+
function handleClick(event) {
|
|
34
|
+
if (!readOnly) toggle(event)
|
|
35
|
+
}
|
|
36
|
+
function handleKeydown(event) {
|
|
37
|
+
if (readOnly) return
|
|
38
|
+
if (event.key === 'Enter' || event.key === ' ') toggle(event)
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<rk-checkbox
|
|
43
|
+
{id}
|
|
44
|
+
class={classes}
|
|
45
|
+
class:disabled={readOnly}
|
|
46
|
+
role="checkbox"
|
|
47
|
+
aria-checked={state}
|
|
48
|
+
aria-disabled={readOnly}
|
|
49
|
+
onclick={handleClick}
|
|
50
|
+
onkeydown={handleKeydown}
|
|
51
|
+
{tabindex}
|
|
52
|
+
>
|
|
53
|
+
<input hidden type="checkbox" {name} {readOnly} bind:checked={value} />
|
|
54
|
+
|
|
55
|
+
<icon class={stateIcons[state]}></icon>
|
|
56
|
+
</rk-checkbox>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} Props
|
|
4
|
+
* @property {import('./types.js').ConnectionType} [type]
|
|
5
|
+
* @property {boolean} [rtl=false]
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @type {Props} */
|
|
9
|
+
let { type = $bindable('empty'), rtl = false } = $props()
|
|
10
|
+
let validatedType = $derived(['last', 'child', 'sibling'].includes(type) ? type : 'empty')
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<span
|
|
14
|
+
class="grid h-full w-4 min-w-4 grid-cols-2 grid-rows-2"
|
|
15
|
+
class:line-empty={validatedType === 'empty'}
|
|
16
|
+
class:line-child={validatedType === 'child'}
|
|
17
|
+
class:line-sibling={validatedType === 'sibling'}
|
|
18
|
+
class:line-last={validatedType === 'last'}
|
|
19
|
+
>
|
|
20
|
+
{#if validatedType === 'last'}
|
|
21
|
+
{#if rtl}
|
|
22
|
+
<i class="border-b border-r"></i>
|
|
23
|
+
{:else}
|
|
24
|
+
<i class="border-r"></i>
|
|
25
|
+
<i class="border-b"></i>
|
|
26
|
+
{/if}
|
|
27
|
+
{:else if validatedType === 'child'}
|
|
28
|
+
{#if rtl}
|
|
29
|
+
<i class="row-span-2 grid grid-rows-2 border-r">
|
|
30
|
+
<i class="border-b"></i>
|
|
31
|
+
</i>
|
|
32
|
+
{:else}
|
|
33
|
+
<i class="col-span-1 row-span-2 border-r"></i>
|
|
34
|
+
<i class="border-b"></i>
|
|
35
|
+
{/if}
|
|
36
|
+
{:else if validatedType === 'sibling'}
|
|
37
|
+
<i class="row-span-2 border-r"></i>
|
|
38
|
+
{/if}
|
|
39
|
+
</span>
|
package/src/Icon.svelte
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { createEmitter } from '@rokkit/core'
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} Props
|
|
5
|
+
* @property {string} [class]
|
|
6
|
+
* @property {any} name
|
|
7
|
+
* @property {any} [state]
|
|
8
|
+
* @property {string} [size]
|
|
9
|
+
* @property {string} [role]
|
|
10
|
+
* @property {any} [label]
|
|
11
|
+
* @property {boolean} [disabled]
|
|
12
|
+
* @property {number} [tabindex]
|
|
13
|
+
* @property {any} [checked]
|
|
14
|
+
* @event {MouseEvent} [onclick]
|
|
15
|
+
* @event {CustomEvent} [onchange]
|
|
16
|
+
* @event {MouseEvent} [onmouseenter]
|
|
17
|
+
* @event {MouseEvent} [onmouseleave]
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** @type {Props} */
|
|
21
|
+
let {
|
|
22
|
+
class: classes = '',
|
|
23
|
+
name,
|
|
24
|
+
state = null,
|
|
25
|
+
size = 'base',
|
|
26
|
+
role = 'img',
|
|
27
|
+
label = null,
|
|
28
|
+
disabled = false,
|
|
29
|
+
tabindex = $bindable(0),
|
|
30
|
+
checked = $bindable(null),
|
|
31
|
+
...events
|
|
32
|
+
} = $props()
|
|
33
|
+
|
|
34
|
+
let emitter = $derived(createEmitter(events, ['click', 'change', 'mouseenter', 'mouseleave']))
|
|
35
|
+
function handleClick(e) {
|
|
36
|
+
e.preventDefault()
|
|
37
|
+
|
|
38
|
+
if (!disabled) {
|
|
39
|
+
if (role === 'checkbox' || role === 'option') {
|
|
40
|
+
checked = !checked
|
|
41
|
+
emitter?.change(checked)
|
|
42
|
+
}
|
|
43
|
+
emitter?.click()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let validatedTabindex = $derived(role === 'img' || disabled ? -1 : tabindex)
|
|
48
|
+
let ariaChecked = $derived(
|
|
49
|
+
['checkbox', 'option'].includes(role) ? (checked !== null ? checked : false) : null
|
|
50
|
+
)
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
54
|
+
<rk-icon
|
|
55
|
+
class={classes}
|
|
56
|
+
class:small={size === 'small'}
|
|
57
|
+
class:medium={size === 'medium'}
|
|
58
|
+
class:large={size === 'large'}
|
|
59
|
+
class:disabled
|
|
60
|
+
{role}
|
|
61
|
+
aria-label={label ?? name}
|
|
62
|
+
aria-checked={ariaChecked}
|
|
63
|
+
onclick={handleClick}
|
|
64
|
+
onkeydown={(e) => e.key === 'Enter' && e.currentTarget.click()}
|
|
65
|
+
data-state={state}
|
|
66
|
+
tabindex={validatedTabindex}
|
|
67
|
+
onmouseenter={emitter.mouseenter}
|
|
68
|
+
onnmouseleave={emitter.nmouseleave}
|
|
69
|
+
>
|
|
70
|
+
<i class={name} aria-hidden="true"></i>
|
|
71
|
+
</rk-icon>
|
package/src/Item.svelte
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import Icon from './Icon.svelte'
|
|
3
|
+
import { FieldMapper } from '@rokkit/core'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} Props
|
|
7
|
+
* @property {string|Object} [value]
|
|
8
|
+
* @property {FieldMapper} [mapping]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** @type {Props} */
|
|
12
|
+
let { value, mapping = new FieldMapper() } = $props()
|
|
13
|
+
let content = $derived(mapping.getText(value))
|
|
14
|
+
let ariaLabel = $derived(mapping.getLabel(value) ?? content)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
{#if mapping.hasIcon(value)}
|
|
18
|
+
<Icon name={mapping.getIcon(value)} label={ariaLabel} />
|
|
19
|
+
{:else if mapping.hasImage(value)}
|
|
20
|
+
<img src={mapping.getImage(value)} alt={ariaLabel} />
|
|
21
|
+
{/if}
|
|
22
|
+
{#if content}
|
|
23
|
+
<p>{content}</p>
|
|
24
|
+
{/if}
|
package/src/Link.svelte
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { FieldMapper } from '@rokkit/core'
|
|
3
|
+
import Item from './Item.svelte'
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} Props
|
|
6
|
+
* @property {string} [class]
|
|
7
|
+
* @property {string|Object} [value]
|
|
8
|
+
* @property {FieldMapper} [mapping]
|
|
9
|
+
* @property {string} [href]
|
|
10
|
+
* @property {Object} [rest]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** @type {Props} */
|
|
14
|
+
let { class: classes = '', value, mapping = new FieldMapper(), href = '#', ...rest } = $props()
|
|
15
|
+
let url = $derived(mapping.getAttribute(value, 'url') ?? href)
|
|
16
|
+
let props = $derived({ ...mapping.getAttribute(value, 'props'), ...rest })
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<a href={url} class={classes} {...props}>
|
|
20
|
+
<Item {value} {mapping} />
|
|
21
|
+
</a>
|
package/src/List.svelte
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { createEmitter, noop, getKeyFromPath } from '@rokkit/core'
|
|
3
|
+
import { equals } from 'ramda'
|
|
4
|
+
import { defaultMapping } from './constants'
|
|
5
|
+
// import { listItems } from './snippets.svelte'
|
|
6
|
+
import { onMount } from 'svelte'
|
|
7
|
+
import { DataWrapper } from '@rokkit/states'
|
|
8
|
+
import { navigator } from '@rokkit/actions'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} Props
|
|
12
|
+
* @property {string} [class]
|
|
13
|
+
* @property {string} [name]
|
|
14
|
+
* @property {any} [items]
|
|
15
|
+
* @property {import('@rokkit/core').FieldMapper} [mapping]
|
|
16
|
+
* @property {any} [value]
|
|
17
|
+
* @property {number} [tabindex]
|
|
18
|
+
* @property {any} [hierarchy]
|
|
19
|
+
* @property {import('svelte').Snippet} [children]
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** @type {Props} */
|
|
23
|
+
let {
|
|
24
|
+
class: classes = '',
|
|
25
|
+
name = 'list',
|
|
26
|
+
items = $bindable([]),
|
|
27
|
+
value = $bindable(null),
|
|
28
|
+
mapping = defaultMapping,
|
|
29
|
+
tabindex = 0,
|
|
30
|
+
hierarchy = [],
|
|
31
|
+
children,
|
|
32
|
+
...events
|
|
33
|
+
} = $props()
|
|
34
|
+
|
|
35
|
+
let emitter = createEmitter(events, ['select', 'change', 'move'])
|
|
36
|
+
let wrapper = new DataWrapper(items, mapping, value, { events: emitter })
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
{#snippet listItems(items, wrapper, hierarchy = [], onchange = noop)}
|
|
40
|
+
{@const mapping = wrapper.mapping}
|
|
41
|
+
{#each items as item, index}
|
|
42
|
+
{@const Template = mapping.getComponent(item)}
|
|
43
|
+
{@const path = getKeyFromPath([...hierarchy, index])}
|
|
44
|
+
{@const props = mapping.getAttribute(item, 'props') || {}}
|
|
45
|
+
<rk-list-item
|
|
46
|
+
role="option"
|
|
47
|
+
data-path={path}
|
|
48
|
+
aria-selected={wrapper.selected.has(path)}
|
|
49
|
+
aria-current={equals(wrapper.currentNode, item)}
|
|
50
|
+
>
|
|
51
|
+
<Template bind:value={items[index]} {mapping} {onchange} {...props} />
|
|
52
|
+
</rk-list-item>
|
|
53
|
+
{/each}
|
|
54
|
+
{/snippet}
|
|
55
|
+
|
|
56
|
+
<rk-list
|
|
57
|
+
class={classes}
|
|
58
|
+
role="listbox"
|
|
59
|
+
aria-label={name}
|
|
60
|
+
use:navigator={{ wrapper }}
|
|
61
|
+
{tabindex}
|
|
62
|
+
onactivate={() => (value = wrapper.value)}
|
|
63
|
+
>
|
|
64
|
+
{@render children?.()}
|
|
65
|
+
{@render listItems(wrapper.data, wrapper, hierarchy, emitter.change)}
|
|
66
|
+
<!-- <ListItems bind:items {mapping} {hierarchy} /> -->
|
|
67
|
+
</rk-list>
|