@rokkit/core 1.0.0-next.11

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/package.json +59 -0
  4. package/src/Accordion.svelte +80 -0
  5. package/src/Alerts.svelte +39 -0
  6. package/src/DropDown.svelte +79 -0
  7. package/src/DropSearch.svelte +67 -0
  8. package/src/Icon.svelte +15 -0
  9. package/src/List-Discard.svelte +48 -0
  10. package/src/List.svelte +65 -0
  11. package/src/ListActions.svelte +35 -0
  12. package/src/NavTabs.svelte +0 -0
  13. package/src/NestedList.svelte +87 -0
  14. package/src/Overlay.svelte +4 -0
  15. package/src/PageNavigator.svelte +94 -0
  16. package/src/ResponsiveGrid.svelte +73 -0
  17. package/src/Scrollable.svelte +8 -0
  18. package/src/Searchable.svelte +19 -0
  19. package/src/Sidebar.svelte +5 -0
  20. package/src/Slider.svelte +17 -0
  21. package/src/SpinList.svelte +48 -0
  22. package/src/SplitPane.svelte +109 -0
  23. package/src/SplitView.svelte +44 -0
  24. package/src/Splitter.svelte +95 -0
  25. package/src/TabItem.svelte +27 -0
  26. package/src/TabItems.svelte +34 -0
  27. package/src/Tabs.svelte +31 -0
  28. package/src/Tree.svelte +19 -0
  29. package/src/actions/dismissable.js +24 -0
  30. package/src/actions/fillable.js +114 -0
  31. package/src/actions/hierarchy.js +189 -0
  32. package/src/actions/index.js +7 -0
  33. package/src/actions/navigable.js +42 -0
  34. package/src/actions/navigator.js +179 -0
  35. package/src/actions/pannable.js +50 -0
  36. package/src/actions/swipeable.js +56 -0
  37. package/src/actions/themeable.js +23 -0
  38. package/src/constants.js +149 -0
  39. package/src/index.js +27 -0
  40. package/src/items/Collapsible.svelte +51 -0
  41. package/src/items/Connector.svelte +26 -0
  42. package/src/items/Link.svelte +17 -0
  43. package/src/items/Node.svelte +52 -0
  44. package/src/items/Pill.svelte +19 -0
  45. package/src/items/Separator.svelte +1 -0
  46. package/src/items/Summary.svelte +27 -0
  47. package/src/items/Text.svelte +21 -0
  48. package/src/items/index.js +8 -0
  49. package/src/list.js +14 -0
  50. package/src/mocks/Custom.svelte +7 -0
  51. package/src/mocks/index.js +10 -0
  52. package/src/stores/alerts.js +3 -0
  53. package/src/stores/index.js +6 -0
  54. package/src/stores/persist.js +63 -0
  55. package/src/stores/theme.js +34 -0
@@ -0,0 +1,94 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { defaultFields, defaultStateIcons } from './constants'
4
+ import Icon from './Icon.svelte'
5
+
6
+ const dispatch = createEventDispatcher()
7
+
8
+ let className = ''
9
+ export { className as class }
10
+
11
+ export let items = []
12
+ export let value = items[0]
13
+ export let fields = {}
14
+ export let numbers = false
15
+
16
+ const navigate = defaultStateIcons.navigate
17
+
18
+ let previous
19
+ let next
20
+
21
+ function updateOnChange(value, items) {
22
+ let index = items.findIndex((x) => x == value)
23
+ if (index == -1) value = items[0]
24
+ // } else {
25
+ previous = index > 0 ? items[index - 1] : null
26
+ next = index < items.length - 1 ? items[index + 1] : null
27
+ // }
28
+ }
29
+
30
+ function handleClick(item) {
31
+ if (item) {
32
+ value = item
33
+ dispatch('select', value)
34
+ }
35
+ }
36
+
37
+ $: fields = { ...defaultFields, ...fields }
38
+ $: updateOnChange(value, items)
39
+
40
+ // $: value = items.findIndex((x) => x == value) ? value : items[0]
41
+ </script>
42
+
43
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
44
+ <nav-pages class="grid grid-cols-3 select-none {className}" tabindex="0">
45
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
46
+ <span
47
+ class="flex items-center cursor-pointer"
48
+ on:click={() => handleClick(previous)}
49
+ tabIndex={previous ? 0 : -1}
50
+ >
51
+ {#if previous}
52
+ <Icon name={navigate.left} />
53
+ <p>
54
+ {#if previous[fields.text]}
55
+ {previous[fields.text]}
56
+ {/if}
57
+ </p>
58
+ {/if}
59
+ </span>
60
+ <span class="flex items-center justify-center">
61
+ <block class="flex items-center">
62
+ {#each items as item, index}
63
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
64
+ <pg
65
+ class:numbers
66
+ class:dot={!numbers}
67
+ class:is-selected={value == item}
68
+ on:click={() => handleClick(item)}
69
+ tabindex="0"
70
+ class="cursor-pointer"
71
+ >
72
+ {#if numbers}
73
+ {index + 1}
74
+ {/if}
75
+ </pg>
76
+ {/each}
77
+ </block>
78
+ </span>
79
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
80
+ <span
81
+ class="flex items-center cursor-pointer justify-end"
82
+ on:click={() => handleClick(next)}
83
+ tabIndex={next ? 0 : -1}
84
+ >
85
+ {#if next}
86
+ <p class="text-right">
87
+ {#if next[fields.text]}
88
+ {next[fields.text]}
89
+ {/if}
90
+ </p>
91
+ <Icon name={navigate.right} />
92
+ {/if}
93
+ </span>
94
+ </nav-pages>
@@ -0,0 +1,73 @@
1
+ <script>
2
+ import { swipeable } from './actions/swipeable'
3
+ import { navigable } from './actions/navigable'
4
+ import { fly, fade } from 'svelte/transition'
5
+ import { cubicInOut } from 'svelte/easing'
6
+
7
+ let className = 'three-col'
8
+ export { className as class }
9
+ export let items
10
+ export let small = true
11
+ export let duration = 400
12
+ export let easing = cubicInOut
13
+ export let value
14
+
15
+ let previous = -1
16
+ let activeIndex = 0
17
+ let direction = 1
18
+ let width
19
+
20
+ function handleNext() {
21
+ if (activeIndex < items.length - 1) value = items[activeIndex + 1]
22
+ }
23
+
24
+ function handlePrevious() {
25
+ if (activeIndex > 0) value = items[activeIndex - 1]
26
+ }
27
+
28
+ function activeIndexFromPage(value) {
29
+ const index = items.findIndex((item) => item === value)
30
+ return index > -1 ? index : 0
31
+ }
32
+
33
+ $: activeIndex = activeIndexFromPage(value)
34
+ $: {
35
+ direction = Math.sign(activeIndex - previous)
36
+ previous = activeIndex
37
+ }
38
+ </script>
39
+
40
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
41
+ <container
42
+ use:swipeable={{ enabled: small }}
43
+ on:swipeLeft={handleNext}
44
+ on:swipeRight={handlePrevious}
45
+ use:navigable={{ enabled: small }}
46
+ on:previous={handlePrevious}
47
+ on:next={handleNext}
48
+ tabindex={0}
49
+ class="overflow-hidden {className}"
50
+ bind:clientWidth={width}
51
+ >
52
+ {#each items as item, index}
53
+ {@const segmentClass = 'col-' + (index + 1)}
54
+ {@const props = item.props ?? {}}
55
+ {#if small && index === activeIndex}
56
+ <segment
57
+ class="absolute w-full h-full {segmentClass}"
58
+ out:fade={{
59
+ x: -1 * direction * width,
60
+ duration,
61
+ easing
62
+ }}
63
+ in:fly={{ x: direction * width, duration, easing }}
64
+ >
65
+ <svelte:component this={item.component} {...props} />
66
+ </segment>
67
+ {:else if !small}
68
+ <segment class={segmentClass}>
69
+ <svelte:component this={item.component} {...props} />
70
+ </segment>
71
+ {/if}
72
+ {/each}
73
+ </container>
@@ -0,0 +1,8 @@
1
+ <script>
2
+ let className = ''
3
+ export { className as class }
4
+ </script>
5
+
6
+ <scroll class="flex flex-col h-full overflow-scroll {className}">
7
+ <slot />
8
+ </scroll>
@@ -0,0 +1,19 @@
1
+ <script>
2
+ import Scrollable from './Scrollable.svelte'
3
+
4
+ export let search = ''
5
+ </script>
6
+
7
+ <div class="flex flex-col overflow-hidden h-full w-full searchable">
8
+ <div class="flex flex-col flex-shrink-0 flex-grow px-4 py-2 quick-search">
9
+ <input
10
+ type="search"
11
+ bind:value={search}
12
+ class="rounded-full px-3 leading-loose"
13
+ placeholder="search"
14
+ />
15
+ </div>
16
+ <Scrollable>
17
+ <slot />
18
+ </Scrollable>
19
+ </div>
@@ -0,0 +1,5 @@
1
+ <aside class="flex flex-col sidebar">
2
+ <slot name="header" />
3
+ <slot />
4
+ <slot name="footer" />
5
+ </aside>
@@ -0,0 +1,17 @@
1
+ <script>
2
+ import { slide, fade } from 'svelte/transition'
3
+
4
+ let className = 'menu'
5
+ export { className as class }
6
+ export let top
7
+ export let offset = -top
8
+ </script>
9
+
10
+ <scroll
11
+ in:slide={{ duration: 50, y: offset }}
12
+ out:fade={{ duration: 50 }}
13
+ style:top="{top}px"
14
+ class="flex flex-col absolute w-full z-10 overflow-scroll {className}"
15
+ >
16
+ <slot />
17
+ </scroll>
@@ -0,0 +1,48 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { defaultFields } from './constants'
4
+ import { Text } from './items'
5
+ import { navigable } from './actions'
6
+ import { getComponent } from './list'
7
+
8
+ const dispatch = createEventDispatcher()
9
+
10
+ let className = ''
11
+ export { className as class }
12
+ export let items = []
13
+ export let fields = {}
14
+ export let using = {}
15
+ export let value
16
+ let index = -1
17
+
18
+ function moveNext() {
19
+ index = index < items.length - 1 ? index + 1 : 0
20
+ dispatch('select', items[index])
21
+ }
22
+ function moveBack() {
23
+ index = index > 0 ? index - 1 : items.length - 1
24
+ dispatch('select', items[index])
25
+ }
26
+
27
+ $: using = { default: Text, ...using }
28
+ $: fields = { ...defaultFields, ...fields }
29
+ $: value = index >= 0 ? items[index] : null
30
+ $: component = getComponent(value, fields)
31
+ </script>
32
+
33
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
34
+ <spin-list class="flex flex-row w-full {className}" tabindex={0}>
35
+ <input
36
+ type="text"
37
+ class="flex flex-grow"
38
+ bind:value
39
+ readonly
40
+ use:navigable
41
+ on:next={moveNext}
42
+ on:previous={moveBack}
43
+ />
44
+ <!-- <svelte:component this={component} bind:content={value} {fields} on:change /> -->
45
+ <square class="h-full position-absolute right-0">
46
+ <icon class="i-carbon-chevron-sort" />
47
+ </square>
48
+ </spin-list>
@@ -0,0 +1,109 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { pannable } from './actions'
4
+
5
+ const dispatch = createEventDispatcher()
6
+
7
+ export let vertical = false
8
+ export let limits = [50, 30]
9
+ export let pos = 70
10
+ export let styles = ['', '']
11
+
12
+ let refs = {}
13
+
14
+ function clamp(min, max, value) {
15
+ return min > value ? min : max < value ? max : value
16
+ }
17
+
18
+ function handlePanMove(event) {
19
+ const { top, bottom, left, right } = refs.wrapper.getBoundingClientRect()
20
+ const extents = horizontal ? [left, right] : [top, bottom]
21
+ let px = clamp(
22
+ extents[0],
23
+ extents[1],
24
+ horizontal ? event.detail.x : event.detail.y
25
+ )
26
+ pos = clamp(
27
+ limits[0],
28
+ 100 - limits[1],
29
+ (100 * (px - extents[0])) / (extents[1] - extents[0])
30
+ )
31
+ dispatch('change', { pos })
32
+ }
33
+
34
+ $: horizontal = !vertical
35
+ $: side = horizontal ? 'left' : 'top'
36
+ $: dimension = horizontal ? 'width' : 'height'
37
+ $: direction = vertical ? 'vertical' : 'horizontal'
38
+ </script>
39
+
40
+ <div class="wrapper {direction}" bind:this={refs.wrapper}>
41
+ <div class={styles[0]} style="{dimension}: {pos}%;">
42
+ <slot name="a" />
43
+ </div>
44
+ <div class={styles[1]} style="{dimension}: {100 - pos}%;">
45
+ <slot name="b" />
46
+ </div>
47
+ <span
48
+ class="wall"
49
+ use:pannable
50
+ on:panmove={handlePanMove}
51
+ style="{side}: calc({pos}% - 8px)"
52
+ />
53
+ </div>
54
+
55
+ <style>
56
+ .wrapper {
57
+ position: relative;
58
+ display: flex;
59
+ height: 100%;
60
+ width: 100%;
61
+ }
62
+ .wrapper div {
63
+ flex: 1 1 auto;
64
+ height: 100%;
65
+ }
66
+
67
+ .horizontal {
68
+ flex-direction: row;
69
+ }
70
+ .vertical {
71
+ flex-direction: column;
72
+ }
73
+ .wall {
74
+ position: absolute;
75
+ z-index: 10;
76
+ }
77
+ .wall::after {
78
+ content: '';
79
+ position: absolute;
80
+ background-color: var(--bg-800);
81
+ }
82
+ .wall:hover {
83
+ cursor: ew-resize;
84
+ }
85
+ .horizontal .wall {
86
+ padding: 0 8px;
87
+ width: 0;
88
+ height: 100%;
89
+ cursor: ew-resize;
90
+ }
91
+ .horizontal .wall::after {
92
+ left: 8px;
93
+ top: 0;
94
+ width: 1px;
95
+ height: 100%;
96
+ }
97
+ .vertical .wall {
98
+ padding: 8px 0;
99
+ width: 100%;
100
+ height: 0;
101
+ cursor: ns-resize;
102
+ }
103
+ .vertical .wall::after {
104
+ top: 8px;
105
+ left: 0;
106
+ width: 100%;
107
+ height: 1px;
108
+ }
109
+ </style>
@@ -0,0 +1,44 @@
1
+ <script>
2
+ import Splitter from './Splitter.svelte'
3
+
4
+ export let vertical = false
5
+ export let min = 30
6
+ export let max = 70
7
+ export let pos = 30
8
+
9
+ $: sizes = [pos, 100 - pos]
10
+
11
+ $: direction = { direction: vertical ? 'flex-col' : 'flex-row' }
12
+ $: sizeA = {
13
+ width: vertical ? 100 : sizes[0],
14
+ height: vertical ? sizes[0] : 100
15
+ }
16
+ $: sizeB = {
17
+ width: vertical ? 100 : sizes[1],
18
+ height: vertical ? sizes[1] : 100
19
+ }
20
+
21
+ function onSplitterChange(e) {
22
+ pos = e.detail.pos - e.detail.offset
23
+ }
24
+ </script>
25
+
26
+ <div class="relative flex w-full h-full" style:--direction={direction}>
27
+ <section style:--sizeA={sizeA} class="flex flex-grow flex-shrink select-none">
28
+ <slot name="a" />
29
+ </section>
30
+ <section style:--sizeB={sizeB} class="flex flex-grow flex-shrink select-none">
31
+ <slot name="b" />
32
+ </section>
33
+ <Splitter {vertical} {min} {max} {pos} on:change={onSplitterChange} />
34
+ </div>
35
+
36
+ <style>
37
+ div {
38
+ flex-direction: var(--direction);
39
+ }
40
+ section {
41
+ width: calc(var(--width) * 1%);
42
+ height: calc(var(--height) * 1%);
43
+ }
44
+ </style>
@@ -0,0 +1,95 @@
1
+ <script>
2
+ // ensure that parent has position: relative.
3
+ // on reaching limit remove mouse events.
4
+ import { createEventDispatcher } from 'svelte'
5
+ import { pannable } from './actions'
6
+
7
+ const dispatch = createEventDispatcher()
8
+
9
+ export let vertical = false
10
+ export let index
11
+ export let min = 0
12
+ export let max = 100
13
+ export let pos = 50
14
+ export let offset = 0
15
+
16
+ let wall
17
+
18
+ function clamp(min, max, value) {
19
+ return min > value ? min : max < value ? max : value
20
+ }
21
+
22
+ function fixLimits() {
23
+ if (min > max) [min, max] = [max, min]
24
+ max = clamp(0, 100, max)
25
+ min = clamp(0, 100, min)
26
+ pos = clamp(min, max, pos)
27
+ }
28
+
29
+ function handlePanMove(event) {
30
+ const { top, bottom, left, right } =
31
+ wall.parentElement.getBoundingClientRect()
32
+ const extents = horizontal ? [left, right] : [top, bottom]
33
+ let px = clamp(
34
+ extents[0],
35
+ extents[1],
36
+ horizontal ? event.detail.x : event.detail.y
37
+ )
38
+
39
+ pos = clamp(min, max, (100 * (px - extents[0])) / (extents[1] - extents[0]))
40
+
41
+ dispatch('change', { pos, index, offset })
42
+ }
43
+ $: fixLimits()
44
+ $: horizontal = !vertical
45
+ $: side = horizontal ? 'left' : 'top'
46
+ </script>
47
+
48
+ <span
49
+ bind:this={wall}
50
+ class="wall"
51
+ class:vertical
52
+ class:horizontal
53
+ use:pannable
54
+ on:panmove={handlePanMove}
55
+ style="{side}: calc({pos}% - 8px)"
56
+ />
57
+
58
+ <style>
59
+ .wall {
60
+ position: absolute;
61
+ z-index: 10;
62
+ }
63
+ .wall::after {
64
+ content: '';
65
+ position: absolute;
66
+ background-color: #000;
67
+ }
68
+ .wall:hover {
69
+ cursor: ew-resize;
70
+ }
71
+ .horizontal.wall {
72
+ padding: 0 8px;
73
+ width: 0;
74
+ height: 100%;
75
+ cursor: ew-resize;
76
+ }
77
+ .horizontal.wall::after {
78
+ left: 8px;
79
+ top: 0;
80
+ width: 1px;
81
+ height: 100%;
82
+ }
83
+ .vertical.wall {
84
+ padding: 8px 0;
85
+ width: 100%;
86
+ height: 0;
87
+ cursor: ns-resize;
88
+ }
89
+ .vertical.wall::after {
90
+ top: 8px;
91
+ left: 0;
92
+ width: 100%;
93
+ height: 1px;
94
+ }
95
+ </style>
@@ -0,0 +1,27 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+
4
+ const dispatch = createEventDispatcher()
5
+
6
+ export let icon = null
7
+ export let label
8
+ export let active = false
9
+ export let allowClose = false
10
+ </script>
11
+
12
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
13
+ <tab class="flex flex-row items-center" class:active on:click>
14
+ {#if icon}
15
+ <icon class={icon} aria-label={icon} />
16
+ {/if}
17
+ {#if label}
18
+ <p class="flex flex-shrink-0 flex-grow justify-center">{label}</p>
19
+ {/if}
20
+ {#if allowClose}
21
+ <icon
22
+ class="remove small"
23
+ aria-label="remove"
24
+ on:click={() => dispatch('remove')}
25
+ />
26
+ {/if}
27
+ </tab>
@@ -0,0 +1,34 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { defaultFields } from './constants'
4
+ import TabItem from './TabItem.svelte'
5
+
6
+ const dispatch = createEventDispatcher()
7
+
8
+ export let items = []
9
+ export let fields = {}
10
+ export let allowClose = false
11
+ export let value = items[0]
12
+
13
+ function activate(item) {
14
+ value = item
15
+ dispatch('switch', item)
16
+ }
17
+ function closeTab(index) {
18
+ items[index].isClosed = true
19
+ }
20
+ $: fields = { ...defaultFields, ...fields }
21
+ $: filtered = items.filter((item) => !item.isClosed)
22
+ </script>
23
+
24
+ {#each filtered as item, index}
25
+ {@const label = item[fields.text]}
26
+ <TabItem
27
+ icon={item[fields.icon]}
28
+ {label}
29
+ {allowClose}
30
+ active={value == item}
31
+ on:click={() => activate(item)}
32
+ on:close={() => closeTab(index)}
33
+ />
34
+ {/each}
@@ -0,0 +1,31 @@
1
+ <script>
2
+ import TabItems from './TabItems.svelte'
3
+ import TabItem from './TabItem.svelte'
4
+
5
+ let className = ''
6
+ export { className as class }
7
+ export let items = []
8
+ export let fields = {}
9
+ export let title = null
10
+ export let allowAdd = false
11
+ export let allowClose = false
12
+ export let value = items[0]
13
+
14
+ function addTab() {
15
+ items = [...items, {}]
16
+ value = items[items.length - 1]
17
+ }
18
+ </script>
19
+
20
+ <tab-view class="flex flex-col w-full flex-grow {className}">
21
+ <tabs class="flex flex-row flex-shrink-0 w-full select-none cursor-pointer">
22
+ {#if title}
23
+ <p>{title}</p>
24
+ {/if}
25
+ <TabItems {items} {fields} {allowClose} bind:value on:close />
26
+ {#if allowAdd}
27
+ <TabItem label="+" on:click={addTab} />
28
+ {/if}
29
+ </tabs>
30
+ <content class="flex flex-col flex-grow"><slot /></content>
31
+ </tab-view>
@@ -0,0 +1,19 @@
1
+ <script>
2
+ import NestedList from './NestedList.svelte'
3
+ import { Text } from './items'
4
+ import { defaultFields } from './constants'
5
+
6
+ export let items = []
7
+ export let fields = {}
8
+ export let using = { default: Text }
9
+ export let root = 'root'
10
+ export let rtl = false
11
+
12
+ $: fields = { ...defaultFields, ...fields }
13
+ $: items =
14
+ items.length == 1
15
+ ? items
16
+ : [{ [fields.text]: root, [fields.children]: items }]
17
+ </script>
18
+
19
+ <NestedList {items} {fields} {using} {rtl} />
@@ -0,0 +1,24 @@
1
+ const KEYCODE_ESC = 27
2
+
3
+ export function dismissable(node) {
4
+ const handleClick = (event) => {
5
+ if (node && !node.contains(event.target) && !event.defaultPrevented) {
6
+ node.dispatchEvent(new CustomEvent('dismiss', node))
7
+ }
8
+ }
9
+ const keyup = (event) => {
10
+ if (event.keyCode === KEYCODE_ESC) {
11
+ node.dispatchEvent(new CustomEvent('dismiss', node))
12
+ }
13
+ }
14
+
15
+ document.addEventListener('click', handleClick, true)
16
+ document.addEventListener('keyup', keyup, true)
17
+
18
+ return {
19
+ destroy() {
20
+ document.removeEventListener('click', handleClick, true)
21
+ document.removeEventListener('keyup', keyup, true)
22
+ }
23
+ }
24
+ }