@rokkit/core 1.0.0-next.13 → 1.0.0-next.130

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 (91) hide show
  1. package/dist/calendar.d.ts +10 -0
  2. package/dist/colors/index.d.ts +47 -0
  3. package/dist/connector.d.ts +8 -0
  4. package/dist/constants.d.ts +88 -0
  5. package/dist/events.d.ts +12 -0
  6. package/dist/field-mapper.d.ts +60 -0
  7. package/dist/index.d.ts +13 -0
  8. package/dist/key-event-map.d.ts +18 -0
  9. package/dist/mapped-items.d.ts +14 -0
  10. package/dist/mapping.d.ts +14 -0
  11. package/dist/nested.d.ts +9 -0
  12. package/dist/string.d.ts +59 -0
  13. package/dist/theme.d.ts +96 -0
  14. package/dist/ticks.d.ts +10 -0
  15. package/dist/types.d.ts +300 -0
  16. package/dist/utils.d.ts +107 -0
  17. package/dist/vite/icon-collections.d.ts +32 -0
  18. package/dist/vite/index.d.ts +1 -0
  19. package/package.json +24 -39
  20. package/src/calendar.js +44 -0
  21. package/src/colors/extra.json +16 -0
  22. package/src/colors/syntax.json +42 -0
  23. package/src/colors/tailwind.json +275 -0
  24. package/src/connector.js +34 -0
  25. package/src/constants.js +181 -107
  26. package/src/events.js +32 -0
  27. package/src/field-mapper.js +147 -0
  28. package/src/index.js +19 -27
  29. package/src/key-event-map.js +36 -0
  30. package/src/mapped-items.js +22 -0
  31. package/src/mapping.js +21 -0
  32. package/src/nested.js +28 -0
  33. package/src/string.js +97 -0
  34. package/src/ticks.js +26 -0
  35. package/src/types.js +160 -0
  36. package/src/utils.js +250 -0
  37. package/src/vite/icon-collections.js +73 -0
  38. package/src/vite/index.js +1 -0
  39. package/LICENSE +0 -21
  40. package/README.md +0 -1
  41. package/src/Accordion.svelte +0 -80
  42. package/src/Alerts.svelte +0 -39
  43. package/src/DropDown.svelte +0 -82
  44. package/src/DropSearch.svelte +0 -67
  45. package/src/EditableTabs.svelte +0 -31
  46. package/src/Icon.svelte +0 -15
  47. package/src/List-Discard.svelte +0 -48
  48. package/src/List.svelte +0 -65
  49. package/src/ListActions.svelte +0 -35
  50. package/src/NavTabs.svelte +0 -0
  51. package/src/NestedList.svelte +0 -77
  52. package/src/Overlay.svelte +0 -4
  53. package/src/PageNavigator.svelte +0 -94
  54. package/src/ResponsiveGrid.svelte +0 -73
  55. package/src/Scrollable.svelte +0 -8
  56. package/src/Searchable.svelte +0 -19
  57. package/src/Sidebar.svelte +0 -5
  58. package/src/Slider.svelte +0 -17
  59. package/src/SpinList.svelte +0 -48
  60. package/src/SplitPane.svelte +0 -109
  61. package/src/SplitView.svelte +0 -44
  62. package/src/Splitter.svelte +0 -95
  63. package/src/TabItem.svelte +0 -27
  64. package/src/TabItems.svelte +0 -34
  65. package/src/Tabs.svelte +0 -49
  66. package/src/Tree.svelte +0 -45
  67. package/src/actions/dismissable.js +0 -24
  68. package/src/actions/fillable.js +0 -114
  69. package/src/actions/hierarchy.js +0 -189
  70. package/src/actions/index.js +0 -7
  71. package/src/actions/navigable.js +0 -43
  72. package/src/actions/navigator.js +0 -179
  73. package/src/actions/pannable.js +0 -50
  74. package/src/actions/swipeable.js +0 -56
  75. package/src/actions/themeable.js +0 -23
  76. package/src/items/Collapsible.svelte +0 -51
  77. package/src/items/Connector.svelte +0 -26
  78. package/src/items/Link.svelte +0 -18
  79. package/src/items/Node.svelte +0 -52
  80. package/src/items/Pill.svelte +0 -19
  81. package/src/items/Separator.svelte +0 -1
  82. package/src/items/Summary.svelte +0 -27
  83. package/src/items/Text.svelte +0 -21
  84. package/src/items/index.js +0 -8
  85. package/src/list.js +0 -14
  86. package/src/mocks/Custom.svelte +0 -7
  87. package/src/mocks/index.js +0 -10
  88. package/src/stores/alerts.js +0 -3
  89. package/src/stores/index.js +0 -6
  90. package/src/stores/persist.js +0 -63
  91. package/src/stores/theme.js +0 -34
@@ -1,179 +0,0 @@
1
- import {
2
- moveNext,
3
- movePrevious,
4
- isNested,
5
- hasChildren,
6
- pathFromIndices,
7
- indicesFromPath,
8
- getCurrentNode
9
- } from './hierarchy'
10
- /**
11
- * @typedef NavigatorOptions
12
- * @property {Array<*>} items - An array containing the data set to navigate
13
- * @property {boolean} [vertical=true] - Identifies whether navigation shoud be vertical or horizontal
14
- * @property {string} [idPrefix='id-'] - id prefix used for identifying individual node
15
- * @property {import('../constants').FieldMapping} fields - Field mapping to identify attributes to be used for state and identification of children
16
- */
17
-
18
- /**
19
- * Keyboard navigation for Lists and NestedLists. The data is either nested or not and is not
20
- * expected to switch from nested to simple list or vice-versa.
21
- *
22
- * @param {HTMLElement} node - The node on which the action is to be used on
23
- * @param {NavigatorOptions} options - Configuration options for the action
24
- * @returns
25
- */
26
- export function navigator(node, options) {
27
- const { fields, enabled = true, vertical = true, idPrefix = 'id-' } = options
28
- let items, path, currentNode
29
-
30
- if (!enabled) return { destroy: () => {} }
31
-
32
- const update = (options) => {
33
- items = options.items
34
- path = pathFromIndices(options.indices ?? [], items, fields)
35
- currentNode = getCurrentNode(path)
36
- }
37
-
38
- const next = () => {
39
- const previousNode = currentNode
40
- path = moveNext(path, items, fields)
41
- currentNode = getCurrentNode(path)
42
-
43
- if (previousNode !== currentNode) moveTo(node, path, currentNode, idPrefix)
44
- }
45
- const previous = () => {
46
- const previousNode = currentNode
47
- path = movePrevious(path)
48
- if (path.length > 0) {
49
- currentNode = getCurrentNode(path)
50
- if (previousNode !== currentNode)
51
- moveTo(node, path, currentNode, idPrefix)
52
- }
53
- }
54
- const select = () => {
55
- if (currentNode)
56
- node.dispatchEvent(
57
- new CustomEvent('select', {
58
- detail: { path: indicesFromPath(path), node: currentNode }
59
- })
60
- )
61
- }
62
- const collapse = () => {
63
- if (currentNode) {
64
- const collapse =
65
- hasChildren(currentNode, path[path.length - 1].fields) &&
66
- currentNode[path[path.length - 1].fields.isOpen]
67
- if (collapse) {
68
- currentNode[path[path.length - 1].fields.isOpen] = false
69
- node.dispatchEvent(
70
- new CustomEvent('collapse', {
71
- detail: { path: indicesFromPath(path), node: currentNode }
72
- })
73
- )
74
- } else if (path.length > 0) {
75
- path = path.slice(0, -1)
76
- currentNode = getCurrentNode(path)
77
- select()
78
- }
79
- }
80
- }
81
- const expand = () => {
82
- if (currentNode && hasChildren(currentNode, path[path.length - 1].fields)) {
83
- currentNode[path[path.length - 1].fields.isOpen] = true
84
- node.dispatchEvent(
85
- new CustomEvent('expand', {
86
- detail: { path: indicesFromPath(path), node: currentNode }
87
- })
88
- )
89
- }
90
- }
91
-
92
- update(options)
93
-
94
- const nested = isNested(items, fields)
95
- const movement = vertical
96
- ? { ArrowDown: next, ArrowUp: previous }
97
- : { ArrowRight: next, ArrowLeft: previous }
98
- const states = !nested
99
- ? {}
100
- : vertical
101
- ? { ArrowRight: expand, ArrowLeft: collapse }
102
- : { ArrowDown: expand, ArrowUp: collapse }
103
- const actions = { ...movement, Enter: select, ...states }
104
-
105
- const handleKeyDown = (event) => {
106
- if (actions[event.key]) {
107
- event.preventDefault()
108
- event.stopPropagation()
109
- actions[event.key]()
110
- }
111
- }
112
-
113
- const handleClick = (event) => {
114
- let target = findParentWithDataPath(event.target)
115
- let indices = !target
116
- ? []
117
- : target.dataset.path
118
- .split(',')
119
- .filter((item) => item !== '')
120
- .map((item) => +item)
121
-
122
- if (indices.length > 0 && event.target.tagName != 'DETAIL') {
123
- path = pathFromIndices(indices, items, fields)
124
- currentNode = getCurrentNode(path)
125
- if (hasChildren(currentNode, path[path.length - 1].fields)) {
126
- currentNode[path[path.length - 1].fields.isOpen] =
127
- !currentNode[path[path.length - 1].fields.isOpen]
128
- const event = currentNode[path[path.length - 1].fields.isOpen]
129
- ? 'expand'
130
- : 'collapse'
131
- node.dispatchEvent(
132
- new CustomEvent(event, {
133
- detail: { path: indices, node: currentNode }
134
- })
135
- )
136
- }
137
- node.dispatchEvent(
138
- new CustomEvent('select', {
139
- detail: { path: indices, node: currentNode }
140
- })
141
- )
142
- }
143
- }
144
-
145
- node.addEventListener('keydown', handleKeyDown)
146
- node.addEventListener('click', handleClick)
147
-
148
- return {
149
- update,
150
- destroy() {
151
- node.removeEventListener('keydown', handleKeyDown)
152
- node.removeEventListener('click', handleClick)
153
- }
154
- }
155
- }
156
-
157
- export function moveTo(node, path, currentNode, idPrefix) {
158
- const indices = indicesFromPath(path)
159
-
160
- let current = node.querySelector('#' + idPrefix + indices.join('-'))
161
- if (current) current.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
162
-
163
- node.dispatchEvent(
164
- new CustomEvent('move', {
165
- detail: { path: indices, node: currentNode }
166
- })
167
- )
168
- }
169
-
170
- export function findParentWithDataPath(element) {
171
- if (element.hasAttribute('data-path')) return element
172
- let parent = element.parentNode
173
-
174
- while (parent && !parent.hasAttribute('data-path')) {
175
- parent = parent.parentNode
176
- }
177
-
178
- return parent
179
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * Handle drag and move events
3
- *
4
- * @param {*} node
5
- * @returns
6
- */
7
- export function pannable(node) {
8
- let x
9
- let y
10
-
11
- function track(event, name, delta = {}) {
12
- x = event.clientX
13
- y = event.clientY
14
- event.stopPropagation()
15
- event.preventDefault()
16
- node.dispatchEvent(
17
- new CustomEvent(name, {
18
- detail: { x, y, ...delta }
19
- })
20
- )
21
- }
22
-
23
- function handleMousedown(event) {
24
- track(event, 'panstart')
25
- window.addEventListener('mousemove', handleMousemove)
26
- window.addEventListener('mouseup', handleMouseup)
27
- }
28
-
29
- function handleMousemove(event) {
30
- const dx = event.clientX - x
31
- const dy = event.clientY - y
32
-
33
- track(event, 'panmove', { dx, dy })
34
- }
35
-
36
- function handleMouseup(event) {
37
- track(event, 'panend')
38
-
39
- window.removeEventListener('mousemove', handleMousemove)
40
- window.removeEventListener('mouseup', handleMouseup)
41
- }
42
-
43
- node.addEventListener('mousedown', handleMousedown)
44
-
45
- return {
46
- destroy() {
47
- node.removeEventListener('mousedown', handleMousedown)
48
- }
49
- }
50
- }
@@ -1,56 +0,0 @@
1
- // const defaultOptions = { horizontal: true, vertical: false, threshold: 100 }
2
- export function swipeable(
3
- node,
4
- { horizontal = true, vertical = false, threshold = 100, enabled = true } = {}
5
- ) {
6
- if (!enabled) return { destroy() {} }
7
-
8
- let startX
9
- let startY
10
-
11
- function touchStart(event) {
12
- const touch = event.touches ? event.touches[0] : event
13
- startX = touch.clientX
14
- startY = touch.clientY
15
- }
16
-
17
- function touchEnd(event) {
18
- const touch = event.changedTouches ? event.changedTouches[0] : event
19
- const distX = touch.clientX - startX
20
- const distY = touch.clientY - startY
21
-
22
- if (horizontal) {
23
- if (Math.abs(distX) > Math.abs(distY) && Math.abs(distX) >= threshold) {
24
- if (distX > 0) {
25
- node.dispatchEvent(new CustomEvent('swipeRight'))
26
- } else {
27
- node.dispatchEvent(new CustomEvent('swipeLeft'))
28
- }
29
- }
30
- }
31
-
32
- if (vertical) {
33
- if (Math.abs(distY) > Math.abs(distX) && Math.abs(distY) >= threshold) {
34
- if (distY > 0) {
35
- node.dispatchEvent(new CustomEvent('swipeDown'))
36
- } else {
37
- node.dispatchEvent(new CustomEvent('swipeUp'))
38
- }
39
- }
40
- }
41
- }
42
-
43
- node.addEventListener('touchstart', touchStart)
44
- node.addEventListener('touchend', touchEnd)
45
- node.addEventListener('mousedown', touchStart)
46
- node.addEventListener('mouseup', touchEnd)
47
-
48
- return {
49
- destroy() {
50
- node.removeEventListener('touchstart', touchStart)
51
- node.removeEventListener('touchend', touchEnd)
52
- node.removeEventListener('mousedown', touchStart)
53
- node.removeEventListener('mouseup', touchEnd)
54
- }
55
- }
56
- }
@@ -1,23 +0,0 @@
1
- import { theme } from '../stores'
2
-
3
- /**
4
- * Sets theme level classes based on the theme store
5
- *
6
- * @param {HTMLElement} node
7
- */
8
- export function themable(node) {
9
- let previous = {}
10
-
11
- theme.subscribe((data) => {
12
- if (data.name && data.name !== previous.name) {
13
- node.classList.remove(previous.name)
14
- node.classList.add(data.name)
15
- }
16
- if (data.mode && data.mode !== previous.mode) {
17
- node.classList.remove(previous.mode)
18
- node.classList.add(data.mode)
19
- }
20
-
21
- previous = data
22
- })
23
- }
@@ -1,51 +0,0 @@
1
- <script>
2
- import { createEventDispatcher } from 'svelte'
3
- import { defaultFields } from '../constants'
4
-
5
- const dispatch = createEventDispatcher()
6
-
7
- export let content
8
- export let fields = {}
9
-
10
- $: fields = { ...defaultFields, ...fields }
11
- $: hasItems = content[fields.children] && content[fields.children].length > 0
12
-
13
- function toggle() {
14
- if (hasItems) {
15
- content.isOpen = !content.isOpen
16
- }
17
- dispatch('toggle', content)
18
- }
19
- </script>
20
-
21
- <!-- svelte-ignore a11y-click-events-have-key-events -->
22
- <collapsible
23
- class="flex flex-row flex-shrink-0 items-center w-full leading-loose cursor-pointer"
24
- class:expanded={content.isOpen}
25
- on:click={toggle}
26
- >
27
- {#if content[fields.image]}
28
- <img
29
- class="h-8 w-8 rounded-full"
30
- alt={content[fields.text]}
31
- src={content[fields.image]}
32
- />
33
- {/if}
34
- {#if content[fields.icon]}
35
- <icon class={content[fields.icon]} />
36
- {/if}
37
- {#if content[fields.url]}
38
- <a href={content[fields.url]} class="flex flex-grow">
39
- {content[fields.text]}
40
- </a>
41
- {:else}
42
- <p class="flex flex-grow">{content[fields.text]}</p>
43
- {/if}
44
- {#if hasItems}
45
- {#if content.isOpen}
46
- <icon class="sm accordion-opened" aria-label="expand" />
47
- {:else}
48
- <icon class="sm accordion-closed" aria-label="collapse" />
49
- {/if}
50
- {/if}
51
- </collapsible>
@@ -1,26 +0,0 @@
1
- <script>
2
- export let type
3
- export let rtl = false
4
- </script>
5
-
6
- <span class="grid grid-rows-2 grid-cols-2 h-full w-4">
7
- {#if type === 'last'}
8
- {#if rtl}
9
- <i class="border-b border-r" />
10
- {:else}
11
- <i class="border-r" />
12
- <i class="border-b" />
13
- {/if}
14
- {:else if type === 'middle'}
15
- {#if rtl}
16
- <i class="border-r grid grid-rows-2 row-span-2">
17
- <i class="border-b" />
18
- </i>
19
- {:else}
20
- <i class="border-r col-span-1 row-span-2" />
21
- <i class="border-b" />
22
- {/if}
23
- {:else if type === 'line'}
24
- <i class="border-r row-span-2" />
25
- {/if}
26
- </span>
@@ -1,18 +0,0 @@
1
- <script>
2
- import Text from './Text.svelte'
3
- import { defaultFields } from '../constants'
4
-
5
- export let content
6
- export let fields = defaultFields
7
-
8
- $: target = fields.target ? content[fields.target] : ''
9
- </script>
10
-
11
- <a
12
- class="flex flex-row flex-grow gap-2 items-center content"
13
- href={content[fields.url]}
14
- {target}
15
- tabindex="-1"
16
- >
17
- <Text {content} {fields} />
18
- </a>
@@ -1,52 +0,0 @@
1
- <script>
2
- import Connector from './Connector.svelte'
3
- import { defaultFields, defaultStateIcons } from '../constants'
4
- import { createEventDispatcher } from 'svelte'
5
-
6
- const dispatch = createEventDispatcher()
7
- export let content
8
- export let fields = defaultFields
9
- export let types = []
10
- export let stateIcons = defaultStateIcons.node
11
- export let linesVisible = true
12
- export let selected = false
13
- export let using = {}
14
- export let rtl = false
15
- export let path = []
16
-
17
- $: hasChildren = fields.children in content
18
- $: state =
19
- hasChildren && content[fields.isOpen]
20
- ? { icon: stateIcons.opened, label: 'collapse' }
21
- : { icon: stateIcons.closed, label: 'expand' }
22
- $: component = content[fields.component]
23
- ? using[content[fields.component]] || using.default
24
- : using.default
25
-
26
- // function toggle() {
27
- // if (hasChildren) content[fields.isOpen] = !content[fields.isOpen]
28
- // dispatch('select', content)
29
- // }
30
- </script>
31
-
32
- <!-- svelte-ignore a11y-click-events-have-key-events -->
33
- <node
34
- id={'id-' + path.join('-')}
35
- class="flex flex-row h-8 gap-2 leading-loose items-center cursor-pointer select-none"
36
- class:is-selected={selected}
37
- aria-selected={selected}
38
- role="option"
39
- data-path={path.join(',')}
40
- >
41
- {#each types.slice(1) as type}
42
- <Connector type={linesVisible ? type : 'empty'} />
43
- {/each}
44
-
45
- {#if hasChildren}
46
- <span class="flex flex-col w-4 h-full items-center justify-center">
47
- <icon class={state.icon} aria-label={state.label} tabindex="-1" />
48
- </span>
49
- {/if}
50
-
51
- <svelte:component this={component} bind:content />
52
- </node>
@@ -1,19 +0,0 @@
1
- <script>
2
- import { createEventDispatcher } from 'svelte'
3
- import { defaultStateIcons } from '../constants'
4
-
5
- const dispatch = createEventDispatcher()
6
-
7
- export let item
8
- export let fields
9
-
10
- function handleClick() {
11
- dispatch('remove', item)
12
- }
13
- </script>
14
-
15
- <pill class="flex flex-row items-center">
16
- {item[fields.text]}
17
- <!-- svelte-ignore a11y-click-events-have-key-events -->
18
- <icon class={defaultStateIcons.item.remove} on:click={handleClick} />
19
- </pill>
@@ -1 +0,0 @@
1
- <span class="my-4 flex flex-grow separator" />
@@ -1,27 +0,0 @@
1
- <script>
2
- import { defaultFields } from '../constants'
3
- import Text from './Text.svelte'
4
-
5
- export let content
6
- export let fields = {}
7
- export let using = {}
8
-
9
- $: fields = { ...defaultFields, ...fields }
10
- $: using = { default: Text, ...using }
11
- $: hasItems = content[fields.children] && content[fields.children].length > 0
12
- $: component = using[content[fields.component] ?? 'default']
13
- </script>
14
-
15
- <summary
16
- class="flex flex-row flex-shrink-0 items-center w-full cursor-pointer"
17
- tabindex="-1"
18
- >
19
- <svelte:component this={component} bind:content {fields} />
20
- {#if hasItems}
21
- {#if content[fields.isOpen]}
22
- <icon class="sm accordion-opened" aria-label="expand" />
23
- {:else}
24
- <icon class="sm accordion-closed" aria-label="collapse" />
25
- {/if}
26
- {/if}
27
- </summary>
@@ -1,21 +0,0 @@
1
- <script>
2
- import { defaultFields } from '../constants'
3
-
4
- export let content
5
- export let fields = defaultFields
6
-
7
- $: isObject = typeof content == 'object'
8
- $: text = isObject ? content[fields.text] : content
9
- </script>
10
-
11
- {#if isObject && content[fields.image]}
12
- <img
13
- class="h-8 w-8 rounded-full object-cover"
14
- alt={content[fields.text]}
15
- src={content[fields.image]}
16
- />
17
- {/if}
18
- {#if isObject && content[fields.icon]}
19
- <icon class={content[fields.icon]} />
20
- {/if}
21
- <p class="flex flex-grow">{text}</p>
@@ -1,8 +0,0 @@
1
- export { default as Text } from './Text.svelte'
2
- export { default as Link } from './Link.svelte'
3
- export { default as Node } from './Node.svelte'
4
- export { default as Pill } from './Pill.svelte'
5
- export { default as Collapsible } from './Collapsible.svelte'
6
- export { default as Connector } from './Connector.svelte'
7
- export { default as Separator } from './Separator.svelte'
8
- export { default as Summary } from './Summary.svelte'
package/src/list.js DELETED
@@ -1,14 +0,0 @@
1
- export function getComponent(item, fields) {
2
- if (item && typeof item === 'object') {
3
- return item[fields.component] || fields.default
4
- }
5
- return fields.default
6
- }
7
-
8
- // update position based on externally supplied value
9
- export function updateCursor(cursor, value, items) {
10
- if (cursor.length > 0 && value != items[cursor[0]]) {
11
- let index = items.findIndex((x) => x == value)
12
- cursor = index > -1 ? [index] : []
13
- }
14
- }
@@ -1,7 +0,0 @@
1
- <script>
2
- import { defaultFields } from '../../src/constants'
3
- export let content
4
- export let fields = defaultFields
5
- </script>
6
-
7
- <span>{content[fields.text]}</span>
@@ -1,10 +0,0 @@
1
- import { vi } from 'vitest'
2
-
3
- export function createEvent(x, y) {
4
- return {
5
- clientX: x,
6
- clientY: y,
7
- stopPropagation: vi.fn(),
8
- preventDefault: vi.fn()
9
- }
10
- }
@@ -1,3 +0,0 @@
1
- import { writable } from 'svelte/store'
2
-
3
- export const alerts = writable([])
@@ -1,6 +0,0 @@
1
- import { writable } from 'svelte/store'
2
- // export * from './alerts'
3
- export { theme } from './theme'
4
- export const alerts = writable([])
5
-
6
- // export const theme = writable({ name: 'rokkit', mode: 'dark' })
@@ -1,63 +0,0 @@
1
- export const PARSE_ERROR_MESSAGE =
2
- 'Unable to parse value from local storage for key: '
3
-
4
- if (typeof window === 'undefined') {
5
- global.localStorage = {
6
- getItem: () => '{}',
7
- setItem: () => {}
8
- }
9
- }
10
- export function persistable(key, store) {
11
- let value
12
- const storageEventListener = (event) => {
13
- if (event.key === key) {
14
- event.stopPropagation()
15
- event.preventDefault()
16
- try {
17
- const newValue = JSON.parse(event.newValue)
18
- set(newValue)
19
- } catch (e) {
20
- console.error(PARSE_ERROR_MESSAGE, key)
21
- }
22
- }
23
- }
24
-
25
- try {
26
- value = JSON.parse(localStorage.getItem(key))
27
- // console.log(value)
28
- store.set(value)
29
- } catch {
30
- console.error(PARSE_ERROR_MESSAGE, key)
31
- }
32
-
33
- const set = (newValue) => {
34
- if (value !== newValue) {
35
- value = newValue
36
- localStorage.setItem(key, JSON.stringify(value))
37
- store.set(value)
38
- }
39
- }
40
-
41
- const update = (fn) => {
42
- store.update((currentValue) => {
43
- const value = fn(currentValue)
44
- localStorage.setItem(key, JSON.stringify(value))
45
- return value
46
- })
47
- }
48
-
49
- if (typeof window !== 'undefined') {
50
- window.addEventListener('storage', storageEventListener)
51
- }
52
-
53
- return {
54
- subscribe: store.subscribe,
55
- set,
56
- update,
57
- destroy() {
58
- if (typeof window !== 'undefined') {
59
- window.removeEventListener('storage', storageEventListener)
60
- }
61
- }
62
- }
63
- }
@@ -1,34 +0,0 @@
1
- import { writable } from 'svelte/store'
2
- import { persistable } from './persist'
3
-
4
- const THEME_STORE_KEY = 'app-theme'
5
-
6
- /**
7
- * @typedef {Object} Theme
8
- * @property {string} name
9
- * @property {string} mode
10
- */
11
-
12
- /**
13
- * Custom store that takes an object with two properties, name and mode.
14
- * @returns {import('svelte/store').Writable<Theme>}
15
- */
16
- export function ThemeStore() {
17
- const store = writable({ name: 'rokkit', mode: 'dark' })
18
-
19
- const set = (value) => {
20
- const { name, mode } = value ?? {}
21
- if (typeof name === 'string' && typeof mode === 'string') {
22
- store.set(value)
23
- } else if (value) {
24
- console.error('Both "name" and "mode" must be strings', value)
25
- }
26
- }
27
-
28
- return {
29
- ...store,
30
- set
31
- }
32
- }
33
-
34
- export const theme = persistable(THEME_STORE_KEY, ThemeStore())