@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Jerry Thomas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # Core Components
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@rokkit/core",
3
+ "version": "1.0.0-next.11",
4
+ "description": "Core components, actions and stores for svelte apps.",
5
+ "author": "Jerry Thomas <me@jerrythomas.name>",
6
+ "license": "MIT",
7
+ "main": "index.js",
8
+ "svelte": "src/index.js",
9
+ "module": "src/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "type": "module",
12
+ "prettier": "@jerrythomas/prettier-config",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "devDependencies": {
17
+ "@jerrythomas/eslint-config-svelte": "^1.0.2",
18
+ "@jerrythomas/prettier-config": "^1.0.0",
19
+ "@sveltejs/vite-plugin-svelte": "^2.0.2",
20
+ "@testing-library/svelte": "^3.2.2",
21
+ "@vitest/coverage-istanbul": "^0.28.5",
22
+ "@vitest/ui": "~0.12.10",
23
+ "eslint": "^7.32.0",
24
+ "jsdom": "^19.0.0",
25
+ "svelte": "^3.55.1",
26
+ "typescript": "^4.9.5",
27
+ "validators": "latest",
28
+ "vite": "^4.1.1",
29
+ "vitest": "~0.19.1",
30
+ "shared-config": "1.0.0"
31
+ },
32
+ "files": [
33
+ "src/**/*.js",
34
+ "src/**/*.svelte",
35
+ "!src/mocks",
36
+ "!src/**/*.spec.js"
37
+ ],
38
+ "exports": {
39
+ "./src": "./src",
40
+ "./package.json": "./package.json",
41
+ "./stores": "./src/stores/index.js",
42
+ "./actions": "./src/actions/index.js",
43
+ "./constants": "./src/constants.js",
44
+ ".": {
45
+ "types": "./dist/index.d.ts",
46
+ "import": "./src/index.js"
47
+ }
48
+ },
49
+ "scripts": {
50
+ "lint": "prettier --check --plugin-search-dir=. . && eslint .",
51
+ "format": "prettier --write --plugin-search-dir=. .",
52
+ "test:ct": "playwright test -c playwright.config.js",
53
+ "test:ci": "vitest run",
54
+ "test:ui": "vitest --ui",
55
+ "test": "vitest",
56
+ "coverage": "vitest run --coverage",
57
+ "upgrade": "pnpm upgrade"
58
+ }
59
+ }
@@ -0,0 +1,80 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { defaultFields } from './constants'
4
+ import { Text, Summary } from './items'
5
+ import List from './List.svelte'
6
+ import { navigator } from './actions/navigator'
7
+
8
+ const dispatch = createEventDispatcher()
9
+ let className = ''
10
+ export { className as class }
11
+ export let items = []
12
+ export let fields = {}
13
+ export let using = {}
14
+ export let autoClose = false
15
+ export let value = null
16
+ let cursor = []
17
+
18
+ $: fields = { ...defaultFields, ...fields }
19
+ $: using = { default: Text, ...using }
20
+
21
+ function handle(event) {
22
+ // console.log(event.type, event.detail)
23
+ value = event.detail.node
24
+ cursor = event.detail.path
25
+ if (['collapse', 'expand'].includes(event.type)) {
26
+ if (autoClose) {
27
+ items.map((x) => {
28
+ if (x !== value && x[fields.isOpen]) {
29
+ x[fields.isOpen] = false
30
+ }
31
+ })
32
+ }
33
+ items = items
34
+ }
35
+ dispatch(event.type, event.detail)
36
+ }
37
+ </script>
38
+
39
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
40
+ <accordion
41
+ class="flex flex-col w-full select-none {className}"
42
+ tabindex="0"
43
+ use:navigator={{
44
+ items,
45
+ fields,
46
+ enabled: true,
47
+ indices: cursor
48
+ }}
49
+ on:select={handle}
50
+ on:move={handle}
51
+ on:expand={handle}
52
+ on:collapse={handle}
53
+ >
54
+ {#each items as item, index}
55
+ {@const hasItems =
56
+ item[fields.children] && item[fields.children].length > 0}
57
+ {@const itemFields = { ...fields, ...(fields.fields ?? fields) }}
58
+
59
+ <div
60
+ id={'id-' + index}
61
+ class="flex flex-col"
62
+ class:is-expanded={item[fields.isOpen]}
63
+ class:is-selected={item === value}
64
+ data-path={index}
65
+ >
66
+ <Summary {fields} {using} bind:content={item} />
67
+ {#if hasItems && item[fields.isOpen]}
68
+ <List
69
+ bind:items={item[fields.children]}
70
+ bind:value
71
+ fields={itemFields}
72
+ {using}
73
+ on:select
74
+ hierarchy={[index]}
75
+ tabindex="-1"
76
+ />
77
+ {/if}
78
+ </div>
79
+ {/each}
80
+ </accordion>
@@ -0,0 +1,39 @@
1
+ <script>
2
+ import { fade } from 'svelte/transition'
3
+ import { flip } from 'svelte/animate'
4
+ import { alerts } from './stores'
5
+ import { dismissable } from './actions'
6
+
7
+ function dismissAll() {
8
+ unreadAlerts.map((alert) => (alert.dismissed = true))
9
+ alerts.set([...$alerts])
10
+ }
11
+
12
+ function dismiss(alert) {
13
+ alert.dismissed = true
14
+ alerts.set([...$alerts])
15
+ }
16
+
17
+ $: unreadAlerts = $alerts.filter((x) => x.dismissed)
18
+ </script>
19
+
20
+ {#if unreadAlerts.length > 0}
21
+ <alert-list
22
+ class="flex flex-col gap-2 absolute z-10"
23
+ use:dismissable
24
+ on:dismiss={dismissAll}
25
+ >
26
+ {#each unreadAlerts as alert (alert.id)}
27
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
28
+ <alert
29
+ class={alert.type}
30
+ on:click|stopPropagation={dismiss(alert)}
31
+ animate:flip
32
+ in:fade
33
+ out:fade
34
+ >
35
+ {alert.message}
36
+ </alert>
37
+ {/each}
38
+ </alert-list>
39
+ {/if}
@@ -0,0 +1,79 @@
1
+ <script>
2
+ import { createEventDispatcher } from 'svelte'
3
+ import { dismissable } from './actions'
4
+ import { defaultFields, defaultStateIcons } from './constants.js'
5
+
6
+ import Icon from './Icon.svelte'
7
+ import Text from './items/Text.svelte'
8
+ import List from './List.svelte'
9
+
10
+ const dispatch = createEventDispatcher()
11
+
12
+ let className = ''
13
+ export { className as class }
14
+ export let items = []
15
+ export let fields = defaultFields
16
+ export let using = { default: Text }
17
+ export let value = null
18
+ export let title
19
+ export let icon
20
+ export let small = false
21
+
22
+ $: using = { default: Text, ...using }
23
+ $: fields = { ...defaultFields, ...fields }
24
+
25
+ let offsetTop = 0
26
+ let open = false
27
+ let icons = defaultStateIcons.selector
28
+
29
+ function handleSelect(event) {
30
+ open = false
31
+ dispatch('change', event.detail)
32
+ }
33
+ </script>
34
+
35
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
36
+ <drop-down
37
+ class="flex w-full relative cursor-pointer select-none dropdown {className}"
38
+ class:open
39
+ tabindex="0"
40
+ aria-haspopup="true"
41
+ aria-controls="menu"
42
+ use:dismissable
43
+ on:blur={() => (open = false)}
44
+ on:dismiss={() => (open = false)}
45
+ >
46
+ <button
47
+ on:click|stopPropagation={() => (open = !open)}
48
+ class="flex"
49
+ bind:clientHeight={offsetTop}
50
+ tabindex="-1"
51
+ >
52
+ {#if icon}
53
+ <Icon name={icon} />
54
+ {/if}
55
+ {#if !small && title}
56
+ <p>{title}</p>
57
+ {/if}
58
+ {#if open}
59
+ <icon class={icons.opened} />
60
+ {:else}
61
+ <icon class={icons.closed} />
62
+ {/if}
63
+ </button>
64
+ {#if open}
65
+ <div
66
+ class="flex flex-col absolute z-10 h-fit w-full menu"
67
+ style:top="{offsetTop}px"
68
+ >
69
+ <List
70
+ {items}
71
+ {fields}
72
+ {using}
73
+ bind:value
74
+ on:select={handleSelect}
75
+ tabindex="-1"
76
+ />
77
+ </div>
78
+ {/if}
79
+ </drop-down>
@@ -0,0 +1,67 @@
1
+ <script>
2
+ import { defaultFields } from './constants.js'
3
+ import List from './List.svelte'
4
+ import Slider from './Slider.svelte'
5
+ import { Text } from './items'
6
+
7
+ export let data
8
+ export let value = null
9
+ export let fields = defaultFields
10
+ export let using = { default: Text }
11
+
12
+ $: using = { default: Text, ...using }
13
+ $: fields = { ...defaultFields, ...fields }
14
+
15
+ let opened = false
16
+ let search
17
+ let searchBox
18
+
19
+ // on search change filter list
20
+ function handleSelect(event) {
21
+ console.log('selected', event)
22
+ value = event.detail
23
+ search = event.detail[fields.text]
24
+ }
25
+ </script>
26
+
27
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
28
+ <div
29
+ class="flex flex-col outline-none w-full h-12 relative dropdown"
30
+ tabindex={0}
31
+ >
32
+ <div class="flex flex-shrink-0 h-12 items-center pl-4 selected-item">
33
+ <span class="flex flex-grow">
34
+ <input
35
+ type="text"
36
+ class="p-0 border-none bg-transparent w-full"
37
+ bind:value={search}
38
+ bind:this={searchBox}
39
+ on:focus={() => {
40
+ opened = true
41
+ searchBox.select()
42
+ }}
43
+ on:blur={() => {
44
+ opened = false
45
+ }}
46
+ />
47
+ </span>
48
+ {#if opened}
49
+ <icon class="selector-opened" />
50
+ {:else}
51
+ <icon class="selector-closed" />
52
+ {/if}
53
+ </div>
54
+
55
+ {#if opened}
56
+ <Slider>
57
+ <List
58
+ bind:items={data}
59
+ bind:value
60
+ {fields}
61
+ {using}
62
+ on:select={handleSelect}
63
+ on:change
64
+ />
65
+ </Slider>
66
+ {/if}
67
+ </div>
@@ -0,0 +1,15 @@
1
+ <script>
2
+ let className = ''
3
+ export { className as class }
4
+
5
+ export let name = ''
6
+ let h
7
+ </script>
8
+
9
+ <square-icon
10
+ class="flex flex-col flex-shrink-0 h-full items-center justify-center {className}"
11
+ bind:clientHeight={h}
12
+ style:width="{h}px"
13
+ >
14
+ <icon class={name} aria-hidden="true" />
15
+ </square-icon>
@@ -0,0 +1,48 @@
1
+ <script>
2
+ import ListActions from './ListActions.svelte'
3
+ import ListItems from './List.svelte'
4
+
5
+ let className = 'list'
6
+ export { className as class }
7
+ export let items = []
8
+ export let fields = {}
9
+ export let using = {}
10
+ export let value = null
11
+ export let searchable = false
12
+ export let editable = false
13
+
14
+ let search
15
+ let filtered
16
+
17
+ function addItem() {
18
+ items = [...items, {}]
19
+ value = items[items.length - 1]
20
+ }
21
+ function deleteSelection() {
22
+ if (value) value.isDeleted = true
23
+ }
24
+ function clearSelection() {
25
+ value = null
26
+ }
27
+
28
+ $: filtered =
29
+ searchable && search && search.length
30
+ ? items.filter((item) => item[fields.text].includes(search))
31
+ : items
32
+ </script>
33
+
34
+ <list class="flex flex-col w-full {className}">
35
+ {#if searchable || editable}
36
+ <ListActions
37
+ bind:search
38
+ {searchable}
39
+ {editable}
40
+ on:delete={deleteSelection}
41
+ on:clear={clearSelection}
42
+ on:add={addItem}
43
+ />
44
+ {/if}
45
+ <scroll class="flex flex-col h-full overflow-scroll">
46
+ <ListItems bind:items={filtered} {fields} {using} bind:value on:select />
47
+ </scroll>
48
+ </list>
@@ -0,0 +1,65 @@
1
+ <script>
2
+ import { navigator } from './actions'
3
+ import { createEventDispatcher } from 'svelte'
4
+ import { defaultFields } from './constants'
5
+ import { Text } from './items'
6
+
7
+ const dispatch = createEventDispatcher()
8
+
9
+ let className = 'list'
10
+ export { className as class }
11
+ export let items = []
12
+ export let fields = {}
13
+ export let using = {}
14
+ export let value = null
15
+ export let tabindex = 0
16
+ export let hierarchy = []
17
+ let cursor = []
18
+
19
+ function handleNav(event) {
20
+ value = event.detail.node
21
+ cursor = event.detail.path
22
+
23
+ dispatch('select', { item: value, indices: cursor })
24
+ }
25
+
26
+ $: fields = { ...defaultFields, ...fields }
27
+ $: using = { default: Text, ...using }
28
+ $: filtered = items.filter((item) => !item[fields.isDeleted])
29
+ </script>
30
+
31
+ <list
32
+ class="flex flex-col w-full flex-shrink-0 select-none {className}"
33
+ role="listbox"
34
+ use:navigator={{
35
+ items,
36
+ fields,
37
+ enabled: hierarchy.length == 0,
38
+ indices: cursor
39
+ }}
40
+ on:move={handleNav}
41
+ on:select={handleNav}
42
+ {tabindex}
43
+ >
44
+ <slot />
45
+ {#each filtered as item, index}
46
+ {@const component = item[fields.component]
47
+ ? using[item[fields.component]] || using.default
48
+ : using.default}
49
+ {@const path = [...hierarchy, index].join(',')}
50
+ <item
51
+ class="flex flex-shrink-0 flex-grow-0 items-center cursor-pointer w-full gap-2 select-none item"
52
+ role="option"
53
+ aria-selected={value === item}
54
+ class:is-selected={value === item}
55
+ data-path={path}
56
+ >
57
+ <svelte:component
58
+ this={component}
59
+ bind:content={item}
60
+ {fields}
61
+ on:change
62
+ />
63
+ </item>
64
+ {/each}
65
+ </list>
@@ -0,0 +1,35 @@
1
+ <script>
2
+ // import Icon from '../layout/Icon.svelte'
3
+ import { createEventDispatcher } from 'svelte'
4
+ import { defaultStateIcons } from './constants'
5
+
6
+ const dispatch = createEventDispatcher()
7
+
8
+ export let search
9
+ export let searchable = true
10
+ export let editable = false
11
+
12
+ const actionIcons = defaultStateIcons.action
13
+ </script>
14
+
15
+ <toolbar class="flex flex-row w-full items-center">
16
+ {#if searchable}
17
+ <search class="flex flex-row flex-grow">
18
+ <icon class={actionIcons.search} />
19
+ <input
20
+ type="search"
21
+ bind:value={search}
22
+ class="rounded-r-full px-3 leading-loose"
23
+ placeholder="search"
24
+ />
25
+ </search>
26
+ {/if}
27
+ {#if editable}
28
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
29
+ <icon class={actionIcons.clear} on:click={() => dispatch('clear')} />
30
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
31
+ <icon class={actionIcons.remove} on:click={() => dispatch('remove')} />
32
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
33
+ <icon class={actionIcons.add} on:click={() => dispatch('add')} />
34
+ {/if}
35
+ </toolbar>
File without changes
@@ -0,0 +1,87 @@
1
+ <script>
2
+ import { Node, Text } from './items'
3
+ import { defaultFields } from './constants'
4
+ import { createEventDispatcher } from 'svelte'
5
+ import { navigator } from './actions/navigator'
6
+
7
+ const dispatch = createEventDispatcher()
8
+
9
+ export let items = []
10
+ export let fields = defaultFields
11
+ export let using = {}
12
+ export let types = []
13
+ export let value = null
14
+ export let linesVisible = true
15
+ export let rtl = false
16
+ export let hierarchy = []
17
+
18
+ let indices = []
19
+
20
+ function handle(event) {
21
+ value = event.detail.node
22
+ indices = event.detail.path
23
+ if (['collapse', 'expand'].includes(event.type)) {
24
+ items = items
25
+ }
26
+ dispatch(event.type, value)
27
+ }
28
+ // function handleExpand(event) {
29
+ // value = event.detail.node
30
+ // indices = event.detail.path
31
+ // items = items // tell svelte data has changed
32
+ // dispatch('expand', value)
33
+ // }
34
+ // function handleCollapse(event) {
35
+ // // console.log(event)
36
+ // value = event.detail.node
37
+ // indices = event.detail.path
38
+ // items = items // tell svelte data has changed
39
+ // dispatch('collapse', value)
40
+ // }
41
+ $: using = { default: Text, ...using }
42
+ $: fields = { ...defaultFields, ...fields }
43
+ $: nodeTypes = items.map((_, index) =>
44
+ index === items.length - 1 ? 'last' : 'middle'
45
+ )
46
+ </script>
47
+
48
+ <nested-list
49
+ class="flex flex-col w-full"
50
+ role="listbox"
51
+ class:rtl
52
+ tabindex={hierarchy.length == 0 ? 0 : -1}
53
+ use:navigator={{ items, fields, indices, enabled: hierarchy.length == 0 }}
54
+ on:select={handle}
55
+ on:move={handle}
56
+ on:expand={handle}
57
+ on:collapse={handle}
58
+ >
59
+ {#each items as content, index}
60
+ {@const type = nodeTypes[index] === 'middle' ? 'line' : 'empty'}
61
+ {@const hasChildren = fields.children in content}
62
+ {@const connectors = types.slice(0, -1)}
63
+ {@const path = [...hierarchy, index]}
64
+
65
+ <Node
66
+ bind:content
67
+ {fields}
68
+ {using}
69
+ types={[...connectors, nodeTypes[index]]}
70
+ {linesVisible}
71
+ {rtl}
72
+ {path}
73
+ selected={value === content}
74
+ />
75
+ {#if hasChildren && content[fields.isOpen]}
76
+ <svelte:self
77
+ items={content[fields.children]}
78
+ bind:value
79
+ {fields}
80
+ {using}
81
+ types={[...connectors, type, nodeTypes[index]]}
82
+ {linesVisible}
83
+ hierarchy={path}
84
+ />
85
+ {/if}
86
+ {/each}
87
+ </nested-list>
@@ -0,0 +1,4 @@
1
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
2
+ <overlay class="absolute h-screen w-screen left-0 top-0" on:click>
3
+ <slot />
4
+ </overlay>