@e280/quay 0.0.0-11 → 0.0.0-13
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/README.md +59 -0
- package/package.json +2 -1
- package/s/cellar/cask.ts +13 -7
- package/s/cellar/cellar.test.ts +20 -18
- package/s/cellar/cellar.ts +3 -3
- package/s/cellar/forklift.ts +2 -2
- package/s/cellar/memory-forklift.ts +4 -3
- package/s/cellar/opfs-forklift.ts +4 -6
- package/s/dom/components/browser/style.css.ts +30 -30
- package/s/dom/components/dropzone/style.css.ts +11 -12
- package/s/dom/components/filter/component.ts +2 -2
- package/s/dom/components/searchbar/component.ts +5 -2
- package/s/dom/components/sort/component.ts +3 -3
- package/s/dom/theme.css.ts +28 -0
- package/s/dom/views/tree/style.css.ts +9 -9
- package/s/logic/aspects/codex/parts/hierarchy.ts +1 -0
- package/s/logic/group.ts +5 -3
- package/s/logic/presets/media/library.test.ts +41 -14
- package/s/logic/presets/media/library.ts +25 -17
- package/x/cellar/cask.d.ts +4 -4
- package/x/cellar/cask.js +13 -9
- package/x/cellar/cask.js.map +1 -1
- package/x/cellar/cellar.d.ts +1 -1
- package/x/cellar/cellar.js +3 -3
- package/x/cellar/cellar.js.map +1 -1
- package/x/cellar/cellar.test.js +19 -18
- package/x/cellar/cellar.test.js.map +1 -1
- package/x/cellar/forklift.d.ts +2 -2
- package/x/cellar/memory-forklift.d.ts +2 -2
- package/x/cellar/memory-forklift.js +3 -2
- package/x/cellar/memory-forklift.js.map +1 -1
- package/x/cellar/opfs-forklift.d.ts +2 -2
- package/x/cellar/opfs-forklift.js +3 -5
- package/x/cellar/opfs-forklift.js.map +1 -1
- package/x/demo/main.bundle.min.js +104 -71
- package/x/demo/main.bundle.min.js.map +2 -2
- package/x/dom/components/browser/style.css.js +30 -29
- package/x/dom/components/browser/style.css.js.map +1 -1
- package/x/dom/components/dropzone/style.css.js +11 -11
- package/x/dom/components/filter/component.js +2 -2
- package/x/dom/components/searchbar/component.js +5 -1
- package/x/dom/components/searchbar/component.js.map +1 -1
- package/x/dom/components/sort/component.js +3 -3
- package/x/dom/theme.css.js +28 -0
- package/x/dom/theme.css.js.map +1 -1
- package/x/dom/views/tree/style.css.js +9 -9
- package/x/index.html +2 -2
- package/x/logic/aspects/codex/parts/hierarchy.js +1 -0
- package/x/logic/aspects/codex/parts/hierarchy.js.map +1 -1
- package/x/logic/group.d.ts +3 -3
- package/x/logic/group.js +3 -2
- package/x/logic/group.js.map +1 -1
- package/x/logic/presets/media/library.d.ts +4 -40
- package/x/logic/presets/media/library.js +21 -16
- package/x/logic/presets/media/library.js.map +1 -1
- package/x/logic/presets/media/library.test.d.ts +3 -2
- package/x/logic/presets/media/library.test.js +34 -14
- package/x/logic/presets/media/library.test.js.map +1 -1
package/README.md
CHANGED
|
@@ -143,3 +143,62 @@ The scope passed to `open()` separates media libraries.
|
|
|
143
143
|
const projectA = await MediaLibrary.open("project-a")
|
|
144
144
|
const projectB = await MediaLibrary.open("project-b")
|
|
145
145
|
```
|
|
146
|
+
|
|
147
|
+
<br/>
|
|
148
|
+
|
|
149
|
+
## Styling
|
|
150
|
+
|
|
151
|
+
Customize quay through its css vars
|
|
152
|
+
|
|
153
|
+
```css
|
|
154
|
+
:root {
|
|
155
|
+
--quay-surface: #111;
|
|
156
|
+
--quay-border: #222;
|
|
157
|
+
--quay-text: #333;
|
|
158
|
+
--quay-muted: #777;
|
|
159
|
+
--quay-accent: #0ea5e9;
|
|
160
|
+
--quay-radius: 6px;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
You can scope those variables to one area or one component.
|
|
165
|
+
|
|
166
|
+
```css
|
|
167
|
+
.media-panel {
|
|
168
|
+
--quay-surface: #18181b;
|
|
169
|
+
--quay-border: #27272a;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
quay-browser {
|
|
173
|
+
--quay-browser-thumb-width: 120px;
|
|
174
|
+
--quay-browser-thumb-height: 72px;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Public tokens:
|
|
179
|
+
|
|
180
|
+
```css
|
|
181
|
+
--quay-surface
|
|
182
|
+
--quay-surface-hover
|
|
183
|
+
--quay-surface-selected
|
|
184
|
+
--quay-border
|
|
185
|
+
--quay-text
|
|
186
|
+
--quay-muted
|
|
187
|
+
--quay-accent
|
|
188
|
+
--quay-radius
|
|
189
|
+
--quay-browser-thumb-width
|
|
190
|
+
--quay-browser-thumb-height
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Quay is built on Shoelace, and its internal theme uses Shoelace tokens as fallbacks.
|
|
194
|
+
For deeper control, set Shoelace tokens in your app or scoped around Quay,
|
|
195
|
+
watch out for conflicts if you already use shoelace components in your app.
|
|
196
|
+
|
|
197
|
+
```css
|
|
198
|
+
.media-panel {
|
|
199
|
+
--sl-color-primary-600: #575757;
|
|
200
|
+
--sl-input-focus-ring-color: rgb(119 119 119 / 35%);
|
|
201
|
+
--sl-border-radius-medium: 3px;
|
|
202
|
+
--sl-spacing-x-small: 0.5em;
|
|
203
|
+
}
|
|
204
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e280/quay",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-13",
|
|
4
4
|
"description": "File-browser and outliner UI for the web",
|
|
5
5
|
"author": "Przemysław Gałęzki",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"@e280/sly": "^0.4.0-next.5",
|
|
17
17
|
"@e280/strata": "^0.4.0-next.8",
|
|
18
18
|
"@e280/stz": "^0.0.0-22",
|
|
19
|
+
"@noble/hashes": "^2.2.0",
|
|
19
20
|
"lit": "^3.3.1"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
package/s/cellar/cask.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
|
|
2
2
|
import {Hex} from "@e280/stz"
|
|
3
|
+
import {blake3} from "@noble/hashes/blake3.js"
|
|
3
4
|
|
|
4
5
|
/** File with a hash label */
|
|
5
6
|
export class Cask {
|
|
6
|
-
static async hash(
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
static async hash(file: Blob) {
|
|
8
|
+
const hasher = blake3.create()
|
|
9
|
+
|
|
10
|
+
for await (const chunk of file.stream())
|
|
11
|
+
hasher.update(chunk)
|
|
12
|
+
|
|
13
|
+
const digest = hasher.digest()
|
|
14
|
+
return Hex.fromBytes(digest)
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
static async make(
|
|
12
|
-
const hash = await this.hash(
|
|
13
|
-
return new this(hash,
|
|
17
|
+
static async make(file: Blob) {
|
|
18
|
+
const hash = await this.hash(file)
|
|
19
|
+
return new this(hash, file)
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
constructor(public hash: string, public
|
|
22
|
+
constructor(public hash: string, public file: Blob) {}
|
|
17
23
|
}
|
|
18
24
|
|
package/s/cellar/cellar.test.ts
CHANGED
|
@@ -6,36 +6,38 @@ import {Cask} from "./cask.js"
|
|
|
6
6
|
import {Cellar} from "./cellar.js"
|
|
7
7
|
import {MemoryForklift} from "./memory-forklift.js"
|
|
8
8
|
|
|
9
|
+
const blob = (text: string) => new Blob([Txt.toBytes(text)])
|
|
10
|
+
|
|
9
11
|
export default Science.suite({
|
|
10
12
|
"save + load roundtrip": test(async() => {
|
|
11
13
|
const cellar = new Cellar()
|
|
12
14
|
const text = "hello world"
|
|
13
|
-
const
|
|
14
|
-
await cellar.save(
|
|
15
|
+
const file = blob(text)
|
|
16
|
+
await cellar.save(file)
|
|
15
17
|
|
|
16
|
-
const hash = await Cask.hash(
|
|
18
|
+
const hash = await Cask.hash(file)
|
|
17
19
|
expect(await cellar.has(hash)).is(true)
|
|
18
20
|
|
|
19
21
|
const loaded = await cellar.load(hash)
|
|
20
22
|
expect(loaded.hash).is(hash)
|
|
21
|
-
expect(
|
|
23
|
+
expect(await loaded.file.text()).is(text)
|
|
22
24
|
}),
|
|
23
25
|
|
|
24
26
|
"save is idempotent": test(async() => {
|
|
25
27
|
const cellar = new Cellar()
|
|
26
|
-
const
|
|
27
|
-
await cellar.save(
|
|
28
|
-
await cellar.save(
|
|
28
|
+
const file = blob("foo bar")
|
|
29
|
+
await cellar.save(file)
|
|
30
|
+
await cellar.save(file) // again
|
|
29
31
|
|
|
30
|
-
const hash = await Cask.hash(
|
|
32
|
+
const hash = await Cask.hash(file)
|
|
31
33
|
expect(await cellar.has(hash)).is(true)
|
|
32
34
|
}),
|
|
33
35
|
|
|
34
36
|
"throws on corruption": test(async() => {
|
|
35
37
|
const forklift = new MemoryForklift()
|
|
36
38
|
const cellar = new Cellar(forklift)
|
|
37
|
-
const good =
|
|
38
|
-
const bad =
|
|
39
|
+
const good = blob("valid data")
|
|
40
|
+
const bad = blob("corrupt data")
|
|
39
41
|
|
|
40
42
|
const hash = await Cask.hash(good)
|
|
41
43
|
await forklift.save(hash, bad)
|
|
@@ -45,10 +47,10 @@ export default Science.suite({
|
|
|
45
47
|
|
|
46
48
|
"delete removes file": test(async() => {
|
|
47
49
|
const cellar = new Cellar()
|
|
48
|
-
const
|
|
49
|
-
await cellar.save(
|
|
50
|
+
const file = blob("temporary data")
|
|
51
|
+
await cellar.save(file)
|
|
50
52
|
|
|
51
|
-
const hash = await Cask.hash(
|
|
53
|
+
const hash = await Cask.hash(file)
|
|
52
54
|
expect(await cellar.has(hash)).is(true)
|
|
53
55
|
|
|
54
56
|
await cellar.delete(hash)
|
|
@@ -67,7 +69,7 @@ export default Science.suite({
|
|
|
67
69
|
const texts = ["a", "b", "c"]
|
|
68
70
|
|
|
69
71
|
for (const t of texts)
|
|
70
|
-
await cellar.save(
|
|
72
|
+
await cellar.save(blob(t))
|
|
71
73
|
|
|
72
74
|
const hashes = []
|
|
73
75
|
for await (const hash of cellar.list())
|
|
@@ -75,16 +77,16 @@ export default Science.suite({
|
|
|
75
77
|
|
|
76
78
|
expect(hashes.length).is(3)
|
|
77
79
|
for (const t of texts) {
|
|
78
|
-
const h = await Cask.hash(
|
|
80
|
+
const h = await Cask.hash(blob(t))
|
|
79
81
|
expect(hashes.includes(h)).is(true)
|
|
80
82
|
}
|
|
81
83
|
}),
|
|
82
84
|
|
|
83
85
|
"clear removes all entries": test(async() => {
|
|
84
86
|
const cellar = new Cellar()
|
|
85
|
-
await cellar.save(
|
|
86
|
-
await cellar.save(
|
|
87
|
-
await cellar.save(
|
|
87
|
+
await cellar.save(blob("one"))
|
|
88
|
+
await cellar.save(blob("two"))
|
|
89
|
+
await cellar.save(blob("three"))
|
|
88
90
|
|
|
89
91
|
let count = 0
|
|
90
92
|
for await (const _ of cellar.list()) count++
|
package/s/cellar/cellar.ts
CHANGED
|
@@ -28,10 +28,10 @@ export class Cellar {
|
|
|
28
28
|
return this.forklift.has(hash)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async save(
|
|
32
|
-
const cask = await Cask.make(
|
|
31
|
+
async save(file: Blob) {
|
|
32
|
+
const cask = await Cask.make(file)
|
|
33
33
|
if (!await this.forklift.has(cask.hash))
|
|
34
|
-
await this.forklift.save(cask.hash, cask.
|
|
34
|
+
await this.forklift.save(cask.hash, cask.file)
|
|
35
35
|
return cask
|
|
36
36
|
}
|
|
37
37
|
|
package/s/cellar/forklift.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
export type Forklift = {
|
|
3
3
|
list(): AsyncIterable<string>
|
|
4
4
|
has(label: string): Promise<boolean>
|
|
5
|
-
save(label: string,
|
|
6
|
-
load(label: string): Promise<
|
|
5
|
+
save(label: string, file: Blob): Promise<void>
|
|
6
|
+
load(label: string): Promise<Blob>
|
|
7
7
|
delete(label: string): Promise<void>
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -14,12 +14,13 @@ export class MemoryForklift implements Forklift {
|
|
|
14
14
|
return this.#map.has(label)
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async save(label: string,
|
|
17
|
+
async save(label: string, file: Blob): Promise<void> {
|
|
18
|
+
const bytes = new Uint8Array(await file.arrayBuffer())
|
|
18
19
|
this.#map.set(label, bytes)
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
async load(label: string): Promise<
|
|
22
|
-
return this.#map.require(label)
|
|
22
|
+
async load(label: string): Promise<Blob> {
|
|
23
|
+
return new Blob([this.#map.require(label)])
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async delete(label: string) {
|
|
@@ -28,18 +28,16 @@ export class OpfsForklift implements Forklift {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async save(label: string,
|
|
31
|
+
async save(label: string, file: Blob): Promise<void> {
|
|
32
32
|
const fileHandle = await this.directory.getFileHandle(label, {create: true})
|
|
33
33
|
const writable = await fileHandle.createWritable()
|
|
34
|
-
await writable.write(
|
|
34
|
+
await writable.write(file)
|
|
35
35
|
await writable.close()
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async load(label: string): Promise<
|
|
38
|
+
async load(label: string): Promise<Blob> {
|
|
39
39
|
const fileHandle = await this.directory.getFileHandle(label)
|
|
40
|
-
|
|
41
|
-
const buffer = await file.arrayBuffer()
|
|
42
|
-
return new Uint8Array(buffer)
|
|
40
|
+
return await fileHandle.getFile()
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
async delete(label: string) {
|
|
@@ -5,38 +5,39 @@ export default css`
|
|
|
5
5
|
.toolbar {
|
|
6
6
|
display: flex;
|
|
7
7
|
justify-content: flex-end;
|
|
8
|
-
padding:
|
|
8
|
+
padding: var(--space-sm);
|
|
9
9
|
|
|
10
10
|
sl-button {
|
|
11
|
-
background:
|
|
12
|
-
border-radius:
|
|
11
|
+
background: var(--surface);
|
|
12
|
+
border-radius: var(--radius-small);
|
|
13
13
|
|
|
14
14
|
&[data-active=true] {
|
|
15
|
-
background: var(--
|
|
15
|
+
background: var(--surface-active);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
&::part(base) {
|
|
19
19
|
display: flex;
|
|
20
20
|
align-items: center;
|
|
21
|
-
height:
|
|
21
|
+
height: var(--control-height);
|
|
22
|
+
border-radius: var(--radius-small);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
&::part(label) {
|
|
25
26
|
display: flex;
|
|
26
|
-
width:
|
|
27
|
+
width: var(--control-width);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
.tiles,
|
|
32
33
|
.details {
|
|
33
|
-
padding:
|
|
34
|
+
padding: var(--space-sm);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
.tiles {
|
|
37
38
|
display: flex;
|
|
38
39
|
flex-wrap: wrap;
|
|
39
|
-
row-gap:
|
|
40
|
+
row-gap: var(--space-md);
|
|
40
41
|
width: 100%;
|
|
41
42
|
|
|
42
43
|
.item {
|
|
@@ -50,11 +51,11 @@ export default css`
|
|
|
50
51
|
display: flex;
|
|
51
52
|
align-items: center;
|
|
52
53
|
justify-content: center;
|
|
53
|
-
width: 100px;
|
|
54
|
-
height: 60px;
|
|
55
|
-
border:
|
|
56
|
-
border-radius:
|
|
57
|
-
background:
|
|
54
|
+
width: var(--quay-browser-thumb-width, 100px);
|
|
55
|
+
height: var(--quay-browser-thumb-height, 60px);
|
|
56
|
+
border: var(--border-width) solid var(--border);
|
|
57
|
+
border-radius: var(--radius-small);
|
|
58
|
+
background: var(--surface);
|
|
58
59
|
pointer-events: none;
|
|
59
60
|
|
|
60
61
|
img {
|
|
@@ -64,14 +65,14 @@ export default css`
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
.label {
|
|
67
|
-
margin-top:
|
|
68
|
+
margin-top: var(--space-xs);
|
|
68
69
|
max-width: 150px;
|
|
69
|
-
font-size:
|
|
70
|
+
font-size: var(--font-size-sm);
|
|
70
71
|
text-align: center;
|
|
71
72
|
white-space: nowrap;
|
|
72
73
|
overflow: hidden;
|
|
73
74
|
text-overflow: ellipsis;
|
|
74
|
-
color: var(--
|
|
75
|
+
color: var(--text-muted);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
}
|
|
@@ -79,40 +80,39 @@ export default css`
|
|
|
79
80
|
.details {
|
|
80
81
|
.card {
|
|
81
82
|
display: flex;
|
|
82
|
-
gap:
|
|
83
|
-
padding:
|
|
84
|
-
border:
|
|
85
|
-
background:
|
|
86
|
-
border-radius:
|
|
87
|
-
margin-bottom:
|
|
83
|
+
gap: var(--space-md);
|
|
84
|
+
padding: var(--space-sm);
|
|
85
|
+
border: var(--border-width) solid var(--border);
|
|
86
|
+
background: var(--surface);
|
|
87
|
+
border-radius: var(--radius);
|
|
88
|
+
margin-bottom: var(--space-sm);
|
|
88
89
|
align-items: center;
|
|
89
90
|
|
|
90
91
|
.meta {
|
|
91
92
|
.name {
|
|
92
93
|
font-weight: 500;
|
|
93
|
-
color: var(--
|
|
94
|
+
color: var(--text);
|
|
94
95
|
}
|
|
95
96
|
.type {
|
|
96
|
-
font-size:
|
|
97
|
-
color: var(--
|
|
97
|
+
font-size: var(--font-size-xs);
|
|
98
|
+
color: var(--text-subtle);
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
sl-icon {
|
|
104
|
-
font-size:
|
|
105
|
-
color: var(--
|
|
105
|
+
font-size: var(--icon-size);
|
|
106
|
+
color: var(--icon);
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
sl-tooltip {
|
|
109
110
|
--sl-tooltip-arrow-size: 0;
|
|
110
111
|
|
|
111
112
|
&::part(body) {
|
|
112
|
-
background:
|
|
113
|
-
color: var(--
|
|
113
|
+
background: var(--border);
|
|
114
|
+
color: var(--text-muted);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
`
|
|
118
|
-
|
|
@@ -9,16 +9,16 @@ export default css`
|
|
|
9
9
|
align-items: center;
|
|
10
10
|
justify-content: center;
|
|
11
11
|
position: relative;
|
|
12
|
-
border:
|
|
13
|
-
border-radius:
|
|
14
|
-
transition: background
|
|
12
|
+
border: var(--border-width) dashed var(--border-muted);
|
|
13
|
+
border-radius: var(--radius);
|
|
14
|
+
transition: background var(--transition), border-color var(--transition), color var(--transition);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.dz-input {
|
|
18
18
|
width: 100%;
|
|
19
19
|
height: 100%;
|
|
20
|
-
z-index:
|
|
21
|
-
font-size:
|
|
20
|
+
z-index: var(--z-overlay);
|
|
21
|
+
font-size: var(--font-size-sm);
|
|
22
22
|
cursor: pointer;
|
|
23
23
|
opacity: 0;
|
|
24
24
|
|
|
@@ -28,15 +28,15 @@ export default css`
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.dropzone:hover {
|
|
31
|
-
background: var(--
|
|
31
|
+
background: var(--surface-hover);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.dropzone[data-hovering] {
|
|
35
|
-
background: var(--
|
|
36
|
-
border-color: var(--
|
|
35
|
+
background: var(--surface-hover);
|
|
36
|
+
border-color: var(--primary);
|
|
37
37
|
|
|
38
38
|
& .dz-info {
|
|
39
|
-
color: var(--
|
|
39
|
+
color: var(--primary-soft);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -46,9 +46,8 @@ export default css`
|
|
|
46
46
|
flex-direction: column;
|
|
47
47
|
align-items: center;
|
|
48
48
|
justify-content: center;
|
|
49
|
-
gap:
|
|
50
|
-
color: var(--
|
|
49
|
+
gap: var(--space-sm);
|
|
50
|
+
color: var(--text-muted);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
`
|
|
54
|
-
|
|
@@ -12,12 +12,12 @@ export const QuayFilter = shadowElement(() => {
|
|
|
12
12
|
|
|
13
13
|
useStyles(themeCss, css`
|
|
14
14
|
sl-menu {
|
|
15
|
-
margin-top:
|
|
15
|
+
margin-top: var(--space-xs);
|
|
16
16
|
|
|
17
17
|
.item {
|
|
18
18
|
display: flex;
|
|
19
19
|
align-items: center;
|
|
20
|
-
gap:
|
|
20
|
+
gap: var(--space-sm);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
`)
|
|
@@ -7,8 +7,12 @@ import themeCss from '../../theme.css.js'
|
|
|
7
7
|
import {findLocalGroup} from '../../utils/find-local-group.js'
|
|
8
8
|
|
|
9
9
|
const styleCss = css`
|
|
10
|
+
sl-input::part(base) {
|
|
11
|
+
border-radius: var(--radius);
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
sl-input::part(prefix) {
|
|
11
|
-
padding-left:
|
|
15
|
+
padding-left: var(--space-sm);
|
|
12
16
|
}
|
|
13
17
|
`
|
|
14
18
|
|
|
@@ -29,4 +33,3 @@ export const QuaySearchbar = shadowElement(() => {
|
|
|
29
33
|
</sl-input>
|
|
30
34
|
`
|
|
31
35
|
})
|
|
32
|
-
|
|
@@ -12,16 +12,16 @@ export const QuaySort = shadowElement(() => {
|
|
|
12
12
|
|
|
13
13
|
useStyles(themeCss, css`
|
|
14
14
|
sl-button sl-icon {
|
|
15
|
-
font-size:
|
|
15
|
+
font-size: var(--font-size-sm);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
sl-menu {
|
|
19
|
-
margin-top:
|
|
19
|
+
margin-top: var(--space-xs);
|
|
20
20
|
|
|
21
21
|
.item {
|
|
22
22
|
display: flex;
|
|
23
23
|
align-items: center;
|
|
24
|
-
gap:
|
|
24
|
+
gap: var(--space-sm);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
`)
|
package/s/dom/theme.css.ts
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
import {css} from "lit"
|
|
3
3
|
export default css`@layer theme, view; @layer theme {
|
|
4
4
|
|
|
5
|
+
:host {
|
|
6
|
+
--surface: var(--quay-surface, #0f0f11);
|
|
7
|
+
--surface-active: var(--quay-surface-hover, var(--sl-color-neutral-0, #fff));
|
|
8
|
+
--surface-hover: var(--quay-surface-hover, var(--sl-color-gray-50, #f8f9fa));
|
|
9
|
+
--surface-selected: var(--quay-surface-selected, rgb(0 0 0 / 40%));
|
|
10
|
+
--border: var(--quay-border, #0d0d0e);
|
|
11
|
+
--border-muted: var(--quay-border, var(--sl-color-neutral-300, #d0d0d0));
|
|
12
|
+
--border-width: 1px;
|
|
13
|
+
--text: var(--quay-text, var(--sl-color-neutral-800, #333));
|
|
14
|
+
--text-muted: var(--quay-muted, var(--sl-color-neutral-700, #555));
|
|
15
|
+
--text-subtle: var(--quay-muted, var(--sl-color-neutral-500, #777));
|
|
16
|
+
--icon: var(--quay-muted, var(--sl-color-neutral-300, #d0d0d0));
|
|
17
|
+
--primary: var(--quay-accent, var(--sl-color-primary-500, #0ea5e9));
|
|
18
|
+
--primary-soft: color-mix(in srgb, var(--primary), white 65%);
|
|
19
|
+
--radius: var(--quay-radius, var(--sl-border-radius-medium, 6px));
|
|
20
|
+
--radius-small: var(--sl-border-radius-small, var(--radius));
|
|
21
|
+
--space-xs: var(--sl-spacing-2x-small, 0.3em);
|
|
22
|
+
--space-sm: var(--sl-spacing-x-small, 0.5rem);
|
|
23
|
+
--space-md: var(--sl-spacing-medium, 1rem);
|
|
24
|
+
--font-size-xs: var(--sl-font-size-x-small, 0.75rem);
|
|
25
|
+
--font-size-sm: var(--sl-font-size-small, 0.85rem);
|
|
26
|
+
--control-height: 30px;
|
|
27
|
+
--control-width: 40px;
|
|
28
|
+
--icon-size: 2rem;
|
|
29
|
+
--z-overlay: 999;
|
|
30
|
+
--transition: 0.2s ease;
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
* {
|
|
6
34
|
margin: 0;
|
|
7
35
|
padding: 0;
|
|
@@ -7,12 +7,12 @@ sl-tree-item::part(expand-button) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
sl-tree-item::part(base) {
|
|
10
|
-
min-height:
|
|
10
|
+
min-height: var(--control-height);
|
|
11
11
|
justify-content: center;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
sl-icon:is(.item) {
|
|
15
|
-
padding:
|
|
15
|
+
padding: var(--space-sm);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
sl-tree-item:not([data-type=folder])::part(expand-button) {
|
|
@@ -35,11 +35,11 @@ sl-tree-item:not([data-type=folder])::part(expand-button) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
sl-tree-item {
|
|
38
|
-
min-height:
|
|
39
|
-
transition: background
|
|
38
|
+
min-height: var(--control-height);
|
|
39
|
+
transition: background var(--transition), border-radius var(--transition);
|
|
40
40
|
|
|
41
41
|
::part(label) {
|
|
42
|
-
gap:
|
|
42
|
+
gap: var(--space-sm);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
.folder {
|
|
@@ -49,15 +49,15 @@ sl-tree-item {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
::part(item--selected) {
|
|
52
|
-
background:
|
|
53
|
-
min-height:
|
|
52
|
+
background: var(--surface-selected);
|
|
53
|
+
min-height: var(--control-height);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
sl-tree-item[data-hover] {
|
|
59
|
-
border-radius:
|
|
60
|
-
background:
|
|
59
|
+
border-radius: var(--radius-small);
|
|
60
|
+
background: var(--surface-selected);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
`
|
package/s/logic/group.ts
CHANGED
|
@@ -18,7 +18,7 @@ export class Group<Sc extends Schema = any> {
|
|
|
18
18
|
on = {
|
|
19
19
|
newFolder: sub<[{parent: CodexItem<Sc>}]>(),
|
|
20
20
|
move: sub<[{item: CodexItem<Sc>, target: CodexItem<Sc>}]>(),
|
|
21
|
-
delete: sub<[{
|
|
21
|
+
delete: sub<[{items: CodexItem<Sc>[]}]>(),
|
|
22
22
|
rename: sub<[{item: CodexItem<Sc>, newName: string}]>(),
|
|
23
23
|
upload: sub<[{files: File[], target: CodexItem<Sc>}]>(),
|
|
24
24
|
search: sub<[{terms: string[]}]>(),
|
|
@@ -73,9 +73,11 @@ export class Group<Sc extends Schema = any> {
|
|
|
73
73
|
if(!this.permissions(item).delete)
|
|
74
74
|
throw new Error("delete permission not granted")
|
|
75
75
|
|
|
76
|
+
const items = [...item.crawl()].map(([item]) => item)
|
|
77
|
+
|
|
76
78
|
item.destroy()
|
|
77
79
|
|
|
78
|
-
this.on.delete.pub({
|
|
80
|
+
return this.on.delete.pub({items})
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
rename(item: CodexItem<Sc>, newName: string) {
|
|
@@ -89,7 +91,7 @@ export class Group<Sc extends Schema = any> {
|
|
|
89
91
|
if(!this.permissions(folder).upload)
|
|
90
92
|
throw new Error("upload permission not granted")
|
|
91
93
|
|
|
92
|
-
this.on.upload.pub({files, target: folder})
|
|
94
|
+
return this.on.upload.pub({files, target: folder})
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
addFolder(parent: CodexItem<Sc>) {
|