@e280/quay 0.0.0-1

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 (206) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +117 -0
  3. package/package.json +57 -0
  4. package/s/cellar/cask.ts +18 -0
  5. package/s/cellar/cellar.test.ts +100 -0
  6. package/s/cellar/cellar.ts +53 -0
  7. package/s/cellar/forklift.ts +9 -0
  8. package/s/cellar/index.ts +7 -0
  9. package/s/cellar/memory-forklift.ts +29 -0
  10. package/s/cellar/opfs-forklift.ts +49 -0
  11. package/s/demo/main.bundle.ts +39 -0
  12. package/s/demo/main.css +126 -0
  13. package/s/dom/components/breadcrumb/component.ts +29 -0
  14. package/s/dom/components/browser/component.ts +75 -0
  15. package/s/dom/components/browser/style.css.ts +118 -0
  16. package/s/dom/components/dropzone/component.ts +33 -0
  17. package/s/dom/components/dropzone/style.css.ts +54 -0
  18. package/s/dom/components/filter/component.ts +44 -0
  19. package/s/dom/components/outliner/component.ts +16 -0
  20. package/s/dom/components/searchbar/component.ts +29 -0
  21. package/s/dom/components/sort/component.ts +48 -0
  22. package/s/dom/components.ts +15 -0
  23. package/s/dom/context.ts +5 -0
  24. package/s/dom/theme.css.ts +11 -0
  25. package/s/dom/utils/find-group-name.ts +12 -0
  26. package/s/dom/utils/find-local-group.ts +9 -0
  27. package/s/dom/views/tree/component.ts +64 -0
  28. package/s/dom/views/tree/style.css.ts +64 -0
  29. package/s/genius/folio.md +29 -0
  30. package/s/genius/parts/hy-tree.ts +142 -0
  31. package/s/genius/quay.md +13 -0
  32. package/s/genius/turbo.md +21 -0
  33. package/s/genius/twig.ts +34 -0
  34. package/s/index.html.ts +89 -0
  35. package/s/index.ts +14 -0
  36. package/s/logic/aspects/codex/codex.ts +46 -0
  37. package/s/logic/aspects/codex/example.ts +65 -0
  38. package/s/logic/aspects/codex/parts/clade.ts +28 -0
  39. package/s/logic/aspects/codex/parts/codex-item.ts +94 -0
  40. package/s/logic/aspects/codex/parts/hierarchy.ts +82 -0
  41. package/s/logic/aspects/codex/parts/taxonomy.ts +18 -0
  42. package/s/logic/aspects/codex/parts/types.ts +23 -0
  43. package/s/logic/aspects/codex/utils/generate-id.ts +7 -0
  44. package/s/logic/aspects/dropzone.ts +77 -0
  45. package/s/logic/aspects/tree-trail.ts +33 -0
  46. package/s/logic/brain.ts +22 -0
  47. package/s/logic/group.ts +104 -0
  48. package/s/logic/permissions.ts +34 -0
  49. package/s/logic/presets/media/group.ts +94 -0
  50. package/s/logic/presets/media/schema.ts +22 -0
  51. package/s/logic/presets/opfs/schema.ts +21 -0
  52. package/s/logic/presets/plain/group.ts +44 -0
  53. package/s/logic/presets/plain/schema.ts +15 -0
  54. package/s/logic/types.ts +33 -0
  55. package/s/tests.test.ts +8 -0
  56. package/x/cellar/cask.d.ts +8 -0
  57. package/x/cellar/cask.js +19 -0
  58. package/x/cellar/cask.js.map +1 -0
  59. package/x/cellar/cellar.d.ts +22 -0
  60. package/x/cellar/cellar.js +47 -0
  61. package/x/cellar/cellar.js.map +1 -0
  62. package/x/cellar/cellar.test.d.ts +11 -0
  63. package/x/cellar/cellar.test.js +79 -0
  64. package/x/cellar/cellar.test.js.map +1 -0
  65. package/x/cellar/forklift.d.ts +7 -0
  66. package/x/cellar/forklift.js +2 -0
  67. package/x/cellar/forklift.js.map +1 -0
  68. package/x/cellar/index.d.ts +5 -0
  69. package/x/cellar/index.js +6 -0
  70. package/x/cellar/index.js.map +1 -0
  71. package/x/cellar/memory-forklift.d.ts +9 -0
  72. package/x/cellar/memory-forklift.js +21 -0
  73. package/x/cellar/memory-forklift.js.map +1 -0
  74. package/x/cellar/opfs-forklift.d.ts +11 -0
  75. package/x/cellar/opfs-forklift.js +44 -0
  76. package/x/cellar/opfs-forklift.js.map +1 -0
  77. package/x/demo/main.bundle.d.ts +1 -0
  78. package/x/demo/main.bundle.js +30 -0
  79. package/x/demo/main.bundle.js.map +1 -0
  80. package/x/demo/main.bundle.min.js +471 -0
  81. package/x/demo/main.css +126 -0
  82. package/x/dom/components/breadcrumb/component.d.ts +1 -0
  83. package/x/dom/components/breadcrumb/component.js +21 -0
  84. package/x/dom/components/breadcrumb/component.js.map +1 -0
  85. package/x/dom/components/browser/component.d.ts +1 -0
  86. package/x/dom/components/browser/component.js +65 -0
  87. package/x/dom/components/browser/component.js.map +1 -0
  88. package/x/dom/components/browser/style.css.d.ts +2 -0
  89. package/x/dom/components/browser/style.css.js +117 -0
  90. package/x/dom/components/browser/style.css.js.map +1 -0
  91. package/x/dom/components/dropzone/component.d.ts +1 -0
  92. package/x/dom/components/dropzone/component.js +27 -0
  93. package/x/dom/components/dropzone/component.js.map +1 -0
  94. package/x/dom/components/dropzone/style.css.d.ts +2 -0
  95. package/x/dom/components/dropzone/style.css.js +53 -0
  96. package/x/dom/components/dropzone/style.css.js.map +1 -0
  97. package/x/dom/components/filter/component.d.ts +1 -0
  98. package/x/dom/components/filter/component.js +40 -0
  99. package/x/dom/components/filter/component.js.map +1 -0
  100. package/x/dom/components/outliner/component.d.ts +1 -0
  101. package/x/dom/components/outliner/component.js +13 -0
  102. package/x/dom/components/outliner/component.js.map +1 -0
  103. package/x/dom/components/searchbar/component.d.ts +1 -0
  104. package/x/dom/components/searchbar/component.js +24 -0
  105. package/x/dom/components/searchbar/component.js.map +1 -0
  106. package/x/dom/components/sort/component.d.ts +1 -0
  107. package/x/dom/components/sort/component.js +44 -0
  108. package/x/dom/components/sort/component.js.map +1 -0
  109. package/x/dom/components.d.ts +18 -0
  110. package/x/dom/components.js +13 -0
  111. package/x/dom/components.js.map +1 -0
  112. package/x/dom/context.d.ts +2 -0
  113. package/x/dom/context.js +3 -0
  114. package/x/dom/context.js.map +1 -0
  115. package/x/dom/theme.css.d.ts +2 -0
  116. package/x/dom/theme.css.js +11 -0
  117. package/x/dom/theme.css.js.map +1 -0
  118. package/x/dom/utils/find-group-name.d.ts +1 -0
  119. package/x/dom/utils/find-group-name.js +9 -0
  120. package/x/dom/utils/find-group-name.js.map +1 -0
  121. package/x/dom/utils/find-local-group.d.ts +1 -0
  122. package/x/dom/utils/find-local-group.js +7 -0
  123. package/x/dom/utils/find-local-group.js.map +1 -0
  124. package/x/dom/views/tree/component.d.ts +7 -0
  125. package/x/dom/views/tree/component.js +52 -0
  126. package/x/dom/views/tree/component.js.map +1 -0
  127. package/x/dom/views/tree/style.css.d.ts +2 -0
  128. package/x/dom/views/tree/style.css.js +63 -0
  129. package/x/dom/views/tree/style.css.js.map +1 -0
  130. package/x/genius/folio.md +29 -0
  131. package/x/genius/parts/hy-tree.d.ts +44 -0
  132. package/x/genius/parts/hy-tree.js +116 -0
  133. package/x/genius/parts/hy-tree.js.map +1 -0
  134. package/x/genius/quay.md +13 -0
  135. package/x/genius/turbo.md +21 -0
  136. package/x/genius/twig.d.ts +34 -0
  137. package/x/genius/twig.js +2 -0
  138. package/x/genius/twig.js.map +1 -0
  139. package/x/importmap.json +47 -0
  140. package/x/index.d.ts +11 -0
  141. package/x/index.html +170 -0
  142. package/x/index.html.d.ts +3 -0
  143. package/x/index.html.js +87 -0
  144. package/x/index.html.js.map +1 -0
  145. package/x/index.js +9 -0
  146. package/x/index.js.map +1 -0
  147. package/x/logic/aspects/codex/codex.d.ts +17 -0
  148. package/x/logic/aspects/codex/codex.js +39 -0
  149. package/x/logic/aspects/codex/codex.js.map +1 -0
  150. package/x/logic/aspects/codex/example.d.ts +13 -0
  151. package/x/logic/aspects/codex/example.js +42 -0
  152. package/x/logic/aspects/codex/example.js.map +1 -0
  153. package/x/logic/aspects/codex/parts/clade.d.ts +16 -0
  154. package/x/logic/aspects/codex/parts/clade.js +23 -0
  155. package/x/logic/aspects/codex/parts/clade.js.map +1 -0
  156. package/x/logic/aspects/codex/parts/codex-item.d.ts +33 -0
  157. package/x/logic/aspects/codex/parts/codex-item.js +74 -0
  158. package/x/logic/aspects/codex/parts/codex-item.js.map +1 -0
  159. package/x/logic/aspects/codex/parts/hierarchy.d.ts +23 -0
  160. package/x/logic/aspects/codex/parts/hierarchy.js +69 -0
  161. package/x/logic/aspects/codex/parts/hierarchy.js.map +1 -0
  162. package/x/logic/aspects/codex/parts/taxonomy.d.ts +7 -0
  163. package/x/logic/aspects/codex/parts/taxonomy.js +13 -0
  164. package/x/logic/aspects/codex/parts/taxonomy.js.map +1 -0
  165. package/x/logic/aspects/codex/parts/types.d.ts +22 -0
  166. package/x/logic/aspects/codex/parts/types.js +2 -0
  167. package/x/logic/aspects/codex/parts/types.js.map +1 -0
  168. package/x/logic/aspects/codex/utils/generate-id.d.ts +1 -0
  169. package/x/logic/aspects/codex/utils/generate-id.js +5 -0
  170. package/x/logic/aspects/codex/utils/generate-id.js.map +1 -0
  171. package/x/logic/aspects/dropzone.d.ts +16 -0
  172. package/x/logic/aspects/dropzone.js +60 -0
  173. package/x/logic/aspects/dropzone.js.map +1 -0
  174. package/x/logic/aspects/tree-trail.d.ts +8 -0
  175. package/x/logic/aspects/tree-trail.js +24 -0
  176. package/x/logic/aspects/tree-trail.js.map +1 -0
  177. package/x/logic/brain.d.ts +11 -0
  178. package/x/logic/brain.js +17 -0
  179. package/x/logic/brain.js.map +1 -0
  180. package/x/logic/group.d.ts +51 -0
  181. package/x/logic/group.js +79 -0
  182. package/x/logic/group.js.map +1 -0
  183. package/x/logic/permissions.d.ts +18 -0
  184. package/x/logic/permissions.js +26 -0
  185. package/x/logic/permissions.js.map +1 -0
  186. package/x/logic/presets/media/group.d.ts +7 -0
  187. package/x/logic/presets/media/group.js +75 -0
  188. package/x/logic/presets/media/group.js.map +1 -0
  189. package/x/logic/presets/media/schema.d.ts +18 -0
  190. package/x/logic/presets/media/schema.js +2 -0
  191. package/x/logic/presets/media/schema.js.map +1 -0
  192. package/x/logic/presets/opfs/schema.d.ts +18 -0
  193. package/x/logic/presets/opfs/schema.js +2 -0
  194. package/x/logic/presets/opfs/schema.js.map +1 -0
  195. package/x/logic/presets/plain/group.d.ts +7 -0
  196. package/x/logic/presets/plain/group.js +33 -0
  197. package/x/logic/presets/plain/group.js.map +1 -0
  198. package/x/logic/presets/plain/schema.d.ts +12 -0
  199. package/x/logic/presets/plain/schema.js +2 -0
  200. package/x/logic/presets/plain/schema.js.map +1 -0
  201. package/x/logic/types.d.ts +20 -0
  202. package/x/logic/types.js +11 -0
  203. package/x/logic/types.js.map +1 -0
  204. package/x/tests.test.d.ts +1 -0
  205. package/x/tests.test.js +6 -0
  206. package/x/tests.test.js.map +1 -0
@@ -0,0 +1,75 @@
1
+ import {html, shadowComponent} from "@benev/slate"
2
+
3
+ import styleCss from "./style.css.js"
4
+ import themeCss from "../../theme.css.js"
5
+
6
+ import {findLocalGroup} from "../../utils/find-local-group.js"
7
+ import {CodexItem} from "../../../logic/aspects/codex/parts/codex-item.js"
8
+
9
+ export const QuayBrowser = shadowComponent(use => {
10
+ use.styles(themeCss, styleCss)
11
+
12
+ const group = findLocalGroup(use.element)
13
+ const {trail, config} = group
14
+ const [viewMode, setViewMode] = use.state<'details' | 'tiles'>('tiles')
15
+
16
+ use.mount(() => {
17
+ const dispose = group.on.upload.sub(() => use.rerender())
18
+ return () => dispose()
19
+ })
20
+
21
+ const getItems = () => {
22
+ return group.sort(trail.currentFolder).filter(item => group.matches(item)) as CodexItem[]
23
+ }
24
+
25
+ const renderTiles = () => html`
26
+ <div class="tiles">
27
+ ${getItems().map(item => html`
28
+ <div class="item" @click="${(e: Event) => trail.setTrail(e, item)}">
29
+ <div class="media-icon">
30
+ ${config.renderPreview(item) ?? config.renderIcon(item, false)}
31
+ </div>
32
+ <div class="label" title="${config.renderLabel(item)}">
33
+ ${config.renderLabel(item)}
34
+ </div>
35
+ </div>
36
+ `)}
37
+ </div>
38
+ `
39
+
40
+ const renderDetails = () => html`
41
+ <div class="details">
42
+ ${getItems().map(item => html`
43
+ <div @click=${(e: Event) => trail.setTrail(e, item)} class="card">
44
+ ${config.renderIcon(item, false)}
45
+ <div class=meta>
46
+ <div class=name>${config.renderLabel(item)}</div>
47
+ <div class=type>${item.kind}</div>
48
+ </div>
49
+ </div>
50
+ `)}
51
+ </div>
52
+ `
53
+ // // TODO icons
54
+ // <sl-icon name=${item.taxon.icon}></sl-icon>
55
+
56
+ return html`
57
+ <div class="toolbar">
58
+ <sl-button-group label="View mode">
59
+ <sl-tooltip content="Tiles">
60
+ <sl-button size="small" outline data-active=${viewMode === 'tiles'} @click=${() => setViewMode('tiles')}>
61
+ <sl-icon name="grid-3x3-gap"></sl-icon>
62
+ </sl-button>
63
+ </sl-tooltip>
64
+ <sl-tooltip content="Details">
65
+ <sl-button size="small" outline data-active=${viewMode === 'details'} @click=${() => setViewMode('details')}>
66
+ <sl-icon name="list"></sl-icon>
67
+ </sl-button>
68
+ </sl-tooltip>
69
+ </sl-button-group>
70
+ </div>
71
+
72
+ ${viewMode === 'details' ? renderDetails() : renderTiles()}
73
+ `
74
+ })
75
+
@@ -0,0 +1,118 @@
1
+
2
+ import {css} from "@benev/slate"
3
+ export default css`
4
+
5
+ .toolbar {
6
+ display: flex;
7
+ justify-content: flex-end;
8
+ padding: 0.5rem;
9
+
10
+ sl-button {
11
+ background: #0F0F11;
12
+ border-radius: 5px;
13
+
14
+ &[data-active=true] {
15
+ background: var(--sl-color-neutral-0);
16
+ }
17
+
18
+ &::part(base) {
19
+ display: flex;
20
+ align-items: center;
21
+ height: 30px;
22
+ }
23
+
24
+ &::part(label) {
25
+ display: flex;
26
+ width: 40px;
27
+ }
28
+ }
29
+ }
30
+
31
+ .tiles,
32
+ .details {
33
+ padding: 0.5rem;
34
+ }
35
+
36
+ .tiles {
37
+ display: flex;
38
+ flex-wrap: wrap;
39
+ row-gap: 1em;
40
+ width: 100%;
41
+
42
+ .item {
43
+ flex: 1;
44
+ max-width: 150px;
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: center;
48
+
49
+ .media-icon {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ width: 100px;
54
+ height: 60px;
55
+ border: 2px solid #0D0D0E;
56
+ border-radius: 5px;
57
+ background: #0F0F11;
58
+ pointer-events: none;
59
+
60
+ img {
61
+ width: 100%;
62
+ height: 100%;
63
+ }
64
+ }
65
+
66
+ .label {
67
+ margin-top: 0.5em;
68
+ max-width: 150px;
69
+ font-size: 0.85rem;
70
+ text-align: center;
71
+ white-space: nowrap;
72
+ overflow: hidden;
73
+ text-overflow: ellipsis;
74
+ color: var(--sl-color-neutral-700);
75
+ }
76
+ }
77
+ }
78
+
79
+ .details {
80
+ .card {
81
+ display: flex;
82
+ gap: 1rem;
83
+ padding: 0.8rem;
84
+ border: 2px solid #0D0D0E;
85
+ background: #0F0F11;
86
+ border-radius: 6px;
87
+ margin-bottom: 0.5rem;
88
+ align-items: center;
89
+
90
+ .meta {
91
+ .name {
92
+ font-weight: 500;
93
+ color: var(--sl-color-neutral-800);
94
+ }
95
+ .type {
96
+ font-size: 0.75rem;
97
+ color: var(--sl-color-neutral-500);
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ sl-icon {
104
+ font-size: 2rem;
105
+ color: var(--sl-color-neutral-300);
106
+ }
107
+
108
+ sl-tooltip {
109
+ --sl-tooltip-arrow-size: 0;
110
+
111
+ &::part(body) {
112
+ background: #0D0D0E;
113
+ color: var(--sl-color-neutral-700);
114
+ }
115
+ }
116
+
117
+ `
118
+
@@ -0,0 +1,33 @@
1
+
2
+ import {html, shadowComponent} from "@benev/slate"
3
+
4
+ import styleCss from "./style.css.js"
5
+ import themeCss from "../../theme.css.js"
6
+
7
+ import {findLocalGroup} from "../../utils/find-local-group.js"
8
+
9
+ export const QuayDropzone = shadowComponent(use => {
10
+ use.styles(themeCss, styleCss)
11
+
12
+ const group = findLocalGroup(use.element)
13
+ const {dropzone, config: {root}} = group
14
+
15
+ return html`
16
+ <div class="dropzone" ?data-hovering=${dropzone.hovering}>
17
+ <input
18
+ type="file"
19
+ class="dz-input"
20
+ @change=${(e: DragEvent) => dropzone.change(e, root)}
21
+ @drop=${(e: DragEvent) => dropzone.drop(e, root)}
22
+ @dragover=${(e: DragEvent) => dropzone.dragover(e, root)}
23
+ @dragenter=${(e: DragEvent) => dropzone.dragenter(e, root)}
24
+ @dragleave=${dropzone.dragleave}
25
+ >
26
+ <div class=dz-info>
27
+ <sl-icon name="upload"></sl-icon>
28
+ Drop files here
29
+ </div>
30
+ </div>
31
+ `
32
+ })
33
+
@@ -0,0 +1,54 @@
1
+
2
+ import {css} from "@benev/slate"
3
+ export default css`
4
+
5
+ .dropzone {
6
+ width: 100%;
7
+ height: 100%;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ position: relative;
12
+ border: 2px dashed var(--sl-color-neutral-300);
13
+ border-radius: 6px;
14
+ transition: background 0.2s;
15
+ }
16
+
17
+ .dz-input {
18
+ width: 100%;
19
+ height: 100%;
20
+ z-index: 999;
21
+ font-size: 14px;
22
+ cursor: pointer;
23
+ opacity: 0;
24
+
25
+ &::file-selector-button {
26
+ display: none;
27
+ }
28
+ }
29
+
30
+ .dropzone:hover {
31
+ background: var(--sl-color-gray-50);
32
+ }
33
+
34
+ .dropzone[data-hovering] {
35
+ background: var(--sl-color-gray-50);
36
+ border-color: var(--sl-color-primary-500);
37
+
38
+ & .dz-info {
39
+ color: var(--sl-color-primary-300);
40
+ }
41
+ }
42
+
43
+ .dz-info {
44
+ position: absolute;
45
+ display: flex;
46
+ flex-direction: column;
47
+ align-items: center;
48
+ justify-content: center;
49
+ gap: 0.5em;
50
+ color: var(--sl-color-neutral-700);
51
+ }
52
+
53
+ `
54
+
@@ -0,0 +1,44 @@
1
+ import {html, shadowComponent, css} from "@benev/slate"
2
+ import themeCss from "../../theme.css.js"
3
+ import {findLocalGroup} from "../../utils/find-local-group.js"
4
+ import type SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js"
5
+
6
+ export const QuayFilter = shadowComponent(use => {
7
+ const group = findLocalGroup(use.element)
8
+
9
+ use.styles(themeCss, css`
10
+ sl-menu {
11
+ margin-top: 0.3em;
12
+
13
+ .item {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 0.5em;
17
+ }
18
+ }
19
+ `)
20
+
21
+ const onSelectFilter = (e: CustomEvent<{item: SlMenuItem}>) => {
22
+ const value = e.detail.item.value
23
+ group.selectedFilter.value = value
24
+ }
25
+
26
+ return html`
27
+ <div class="panel">
28
+ <sl-dropdown>
29
+ <sl-button size="small" slot="trigger" caret>
30
+ <sl-icon name="funnel"></sl-icon>
31
+ </sl-button>
32
+ <sl-menu @sl-select=${onSelectFilter}>
33
+ ${[...group.config.filters?.keys()].map(filter => html`
34
+ <sl-menu-item value=${filter}>
35
+ <div class=item>
36
+ <sl-icon name="check" slot="prefix" style="visibility: ${filter === group.selectedFilter.value ? 'visible' : 'hidden'}"></sl-icon>
37
+ <span>${filter}</span>
38
+ </div>
39
+ </sl-menu-item>
40
+ `)}
41
+ </sl-menu>
42
+ </sl-dropdown>
43
+ </div>`
44
+ })
@@ -0,0 +1,16 @@
1
+
2
+ import {html, shadowComponent} from "@benev/slate"
3
+ import themeCss from "../../theme.css.js"
4
+ import {Tree} from "../../views/tree/component.js"
5
+ import {findLocalGroup} from "../../utils/find-local-group.js"
6
+
7
+ export const QuayOutliner = shadowComponent(use => {
8
+ use.styles(themeCss)
9
+ const group = findLocalGroup(use.element)
10
+
11
+ return html`
12
+ <div class="panel">
13
+ ${Tree([group])}
14
+ </div>`
15
+ })
16
+
@@ -0,0 +1,29 @@
1
+
2
+ import {html, shadowComponent, css} from '@benev/slate'
3
+ import themeCss from '../../theme.css.js'
4
+
5
+ import {findLocalGroup} from '../../utils/find-local-group.js'
6
+
7
+ const styleCss = css`
8
+ sl-input::part(prefix) {
9
+ padding-left: 0.5em;
10
+ }
11
+ `
12
+
13
+ export const QuaySearchbar = shadowComponent(use => {
14
+ use.styles(themeCss, styleCss)
15
+ const group = findLocalGroup(use.element)
16
+
17
+ return html`
18
+ <sl-input
19
+ size='small'
20
+ pill
21
+ placeholder='Search'
22
+ clearable
23
+ @sl-input=${(e: CustomEvent) => group.searchText.value = (e.target as HTMLInputElement).value}
24
+ >
25
+ <sl-icon name='search' slot='prefix'></sl-icon>
26
+ </sl-input>
27
+ `
28
+ })
29
+
@@ -0,0 +1,48 @@
1
+ import {html, shadowComponent, css} from "@benev/slate"
2
+ import themeCss from "../../theme.css.js"
3
+ import {findLocalGroup} from "../../utils/find-local-group.js"
4
+ import type SlMenuItem from "@shoelace-style/shoelace/dist/components/menu-item/menu-item.js"
5
+
6
+ export const QuaySort = shadowComponent(use => {
7
+ const group = findLocalGroup(use.element)
8
+
9
+ use.styles(themeCss, css`
10
+ sl-button sl-icon {
11
+ font-size: 14px;
12
+ }
13
+
14
+ sl-menu {
15
+ margin-top: 0.3em;
16
+
17
+ .item {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 0.5em;
21
+ }
22
+ }
23
+ `)
24
+
25
+ const onSelectSort = (e: CustomEvent<{item: SlMenuItem}>) => {
26
+ const value = e.detail.item.value
27
+ group.selectedSort.value = value
28
+ }
29
+
30
+ return html`
31
+ <div class="panel">
32
+ <sl-dropdown>
33
+ <sl-button size="small" slot="trigger" caret>
34
+ <sl-icon name="sort-down-alt"></sl-icon>
35
+ </sl-button>
36
+ <sl-menu @sl-select=${onSelectSort}>
37
+ ${[...group.config.sorts?.keys()].map(sort => html`
38
+ <sl-menu-item value=${sort}>
39
+ <div class=item>
40
+ <sl-icon name="check" slot="prefix" style="visibility: ${sort === group.selectedSort.value ? 'visible' : 'hidden'}"></sl-icon>
41
+ <span>${sort}</span>
42
+ </div>
43
+ </sl-menu-item>
44
+ `)}
45
+ </sl-menu>
46
+ </sl-dropdown>
47
+ </div>`
48
+ })
@@ -0,0 +1,15 @@
1
+ import {QuaySort} from "./components/sort/component.js"
2
+ import {QuayFilter} from "./components/filter/component.js"
3
+ import {QuayBrowser} from "./components/browser/component.js"
4
+ import {QuayDropzone} from "./components/dropzone/component.js"
5
+ import {QuayOutliner} from "./components/outliner/component.js"
6
+ import {QuaySearchbar} from "./components/searchbar/component.js"
7
+ import {QuayBreadcrumb} from "./components/breadcrumb/component.js"
8
+ export {QuayOutliner, QuaySearchbar, QuayDropzone, QuayBrowser, QuayBreadcrumb, QuayFilter, QuaySort}
9
+
10
+ export const components = {QuayOutliner, QuaySearchbar, QuayDropzone, QuayBrowser, QuayBreadcrumb, QuayFilter, QuaySort}
11
+
12
+ export function setShoelaceDarkTheme() {
13
+ document.documentElement.className = "sl-theme-dark"
14
+ }
15
+
@@ -0,0 +1,5 @@
1
+
2
+ import {Brain} from "../logic/brain.js"
3
+
4
+ export const brain = new Brain()
5
+
@@ -0,0 +1,11 @@
1
+ import {css} from "@benev/slate"
2
+ export default css`@layer theme, view; @layer theme {
3
+
4
+ * {
5
+ margin: 0;
6
+ padding: 0;
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ }`
11
+
@@ -0,0 +1,12 @@
1
+ export function findGroupName(el: HTMLElement) {
2
+ for (
3
+ let current: HTMLElement | null = el;
4
+ current;
5
+ current = current.parentElement
6
+ ) {
7
+ const group = current.getAttribute("group")
8
+ if (group)
9
+ return group
10
+ }
11
+ return "default"
12
+ }
@@ -0,0 +1,9 @@
1
+
2
+ import {brain} from "../context.js"
3
+ import {findGroupName} from "./find-group-name.js"
4
+
5
+ export function findLocalGroup(element: HTMLElement) {
6
+ const name = findGroupName(element)
7
+ return brain.getGroup(name)
8
+ }
9
+
@@ -0,0 +1,64 @@
1
+
2
+ import {html, shadowView, TemplateResult} from "@benev/slate"
3
+
4
+ import styleCss from "./style.css.js"
5
+ import themeCss from "../../theme.css.js"
6
+
7
+ import {Group} from "../../../logic/group.js"
8
+ import {CodexItem} from "../../../logic/aspects/codex/parts/codex-item.js"
9
+
10
+ export const Tree = shadowView(use => (group: Group) => {
11
+ use.styles(themeCss, styleCss)
12
+
13
+ const {config} = group
14
+ const {dropzone, trail, config: {root}} = group
15
+
16
+ use.mount(() => {
17
+ const dispose = group.on.upload.sub(() => use.rerender())
18
+ return () => dispose()
19
+ })
20
+
21
+ const renderFolderItem = (item: CodexItem) => html`
22
+ <input
23
+ @dragenter=${(e: DragEvent) => dropzone.dragenter(e, item)}
24
+ @dragleave=${dropzone.dragleave}
25
+ @dragover=${(e: DragEvent) => dropzone.dragover(e, item)}
26
+ @drop=${(e: DragEvent) => dropzone.drop(e, item)}
27
+ @click=${(e: Event) => e.preventDefault()}
28
+ id="file-import"
29
+ class="file-import folder-hover"
30
+ >
31
+ <span class="folder" slot="expand-icon">${config.renderIcon(item, false)}</span>
32
+ <span class="folder" slot="collapse-icon">${config.renderIcon(item, true)}</span>
33
+ ${config.renderLabel(item)}
34
+ `
35
+
36
+ const renderItem = (item: CodexItem) => html`
37
+ ${config.renderIcon(item, false)}
38
+ ${config.renderLabel(item)}
39
+ `
40
+
41
+ const render = (item: CodexItem): TemplateResult => {
42
+ return html`
43
+ <sl-tree-item
44
+ draggable="true"
45
+ @dragstart=${(e: DragEvent) => dropzone.dragstart(e, item)}
46
+ @dragend=${dropzone.dragend}
47
+ @click=${(e: Event) => trail.setTrail(e, item)}
48
+ data-type=${item.kind}
49
+ ?data-hover=${dropzone.hovering?.id === item.id}
50
+ >
51
+ ${item.kind === "folder"
52
+ ? renderFolderItem(item)
53
+ : renderItem(item)}
54
+ ${group.sort(item.children).map((item) => group.matches(item) ? render(item) : null)}
55
+ </sl-tree-item>
56
+ `
57
+ }
58
+
59
+ return html`
60
+ <sl-tree selection=leaf>
61
+ ${render(root)}
62
+ </sl-tree>`
63
+ })
64
+
@@ -0,0 +1,64 @@
1
+
2
+ import {css} from "@benev/slate"
3
+ export default css`
4
+
5
+ sl-tree-item::part(expand-button) {
6
+ rotate: none;
7
+ }
8
+
9
+ sl-tree-item::part(base) {
10
+ min-height: 30px;
11
+ justify-content: center;
12
+ }
13
+
14
+ sl-icon:is(.item) {
15
+ padding: 0.5em;
16
+ }
17
+
18
+ sl-tree-item:not([data-type=folder])::part(expand-button) {
19
+ display: none;
20
+ }
21
+
22
+ .file-import {
23
+ position: absolute;
24
+ opacity: 0;
25
+ left: 0;
26
+ width: 100%;
27
+ }
28
+
29
+ .folder-hover {
30
+ position: absolute;
31
+ width: 100%;
32
+ height: 100%;
33
+ left: 0;
34
+ bottom: 0;
35
+ }
36
+
37
+ sl-tree-item {
38
+ min-height: 30px;
39
+ transition: background 0.2s ease, border-radius 0.2s ease;
40
+
41
+ ::part(label) {
42
+ gap: 0.5em;
43
+ }
44
+
45
+ .folder {
46
+ display: flex;
47
+ justify-content: center;
48
+ align-items: center;
49
+ }
50
+
51
+ ::part(item--selected) {
52
+ background: rgba(0, 0, 0, 0.4);
53
+ min-height: 30px;
54
+ }
55
+
56
+ }
57
+
58
+ sl-tree-item[data-hover] {
59
+ border-radius: 5px;
60
+ background: rgba(0, 0, 0, 0.4);
61
+ }
62
+
63
+ `
64
+
@@ -0,0 +1,29 @@
1
+
2
+ # 🗂️ Folio
3
+
4
+ A virtual filesystem for the web.
5
+
6
+ Folio provides primitives for building intuitive file browsing experiences on the web.
7
+ - Agnostic about where data is actually stored
8
+ - For local, best to use [Turbo](./turbo.md) as an OPFS turbocharger
9
+
10
+ > 🤷 **Why not just use OPFS?**
11
+ > OPFS doesn't have cross-browser support for efficient move or rename operations.
12
+ > Folio is like a souped-up extended opfs.
13
+
14
+ Folio stores two kinds of data:
15
+ - **Blob storage**
16
+ Stores the binary payloads for files.
17
+ Usually, blob storage will target *opfs* — though localstorage, indexeddb, or cloud storage are options.
18
+ sha256 hashes are the identifiers, thus binary payloads are thus de-duplicated.
19
+ - **Index storage**
20
+ Stores metadata and directory hierarchy.
21
+ Usually, index storage will target *indexeddb* — though localstorage or cloud storage are options.
22
+
23
+ <br/>
24
+
25
+ ---
26
+
27
+ > ### Wip: implementation plan
28
+ > - Folio implements the `Twig` interface
29
+