@anton-gustafsson/snapshot-core 0.0.2 → 0.0.3
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/dist/snapshot-nav-list.d.ts +6 -1
- package/dist/snapshot-nav-list.js +85 -27
- package/package.json +1 -1
|
@@ -3,8 +3,10 @@ import type { SnapshotService } from './snapshot-service';
|
|
|
3
3
|
export interface NavItem {
|
|
4
4
|
id: string;
|
|
5
5
|
label: string;
|
|
6
|
+
/** A plain-text glyph (e.g. an emoji), or markup — a string starting with `<` renders as raw HTML/SVG instead of text, so a consumer can pass its own icon (e.g. `<svg>...</svg>`). Only the placeholder frame shown before a card's first capture. */
|
|
6
7
|
icon?: string;
|
|
7
8
|
route?: string;
|
|
9
|
+
description?: string;
|
|
8
10
|
}
|
|
9
11
|
export type SnapshotNavListVariant = 'list' | 'icon-only';
|
|
10
12
|
/**
|
|
@@ -23,10 +25,12 @@ export declare class SnapshotNavList extends LitElement {
|
|
|
23
25
|
overlayOpacity: number;
|
|
24
26
|
/** backdrop blur behind the title, in px */
|
|
25
27
|
overlayBlur: number;
|
|
26
|
-
/** icon-only only: 'bottom' is the caption strip (default), 'center'
|
|
28
|
+
/** icon-only only: 'bottom' is the caption strip (default), 'center' centers a larger title. */
|
|
27
29
|
labelPosition: 'bottom' | 'center';
|
|
28
30
|
/** Defaults to the shared singleton — set your own instance (e.g. a namespaced or custom-storage SnapshotService) per <snapshot-nav-list> if needed. */
|
|
29
31
|
snapshotService: SnapshotService;
|
|
32
|
+
/** Shows a top-right edit button per card. Off by default — clicking it fires `nav-edit` instead of `nav-select`; the host decides what "edit" means (e.g. open its own dialog component). */
|
|
33
|
+
editable: boolean;
|
|
30
34
|
private thumbs;
|
|
31
35
|
private loadingIds;
|
|
32
36
|
/** In-flight dedup guard, separate from `loadingIds` (which is only for spinner display) so a repeat `loadThumb` call for an id already being fetched is a no-op. */
|
|
@@ -40,6 +44,7 @@ export declare class SnapshotNavList extends LitElement {
|
|
|
40
44
|
private loadThumb;
|
|
41
45
|
private get overlayStyle();
|
|
42
46
|
private select;
|
|
47
|
+
private edit;
|
|
43
48
|
render(): import("lit-html").TemplateResult<1>;
|
|
44
49
|
}
|
|
45
50
|
declare global {
|
|
@@ -7,7 +7,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { LitElement, html, css } from 'lit';
|
|
8
8
|
import { property, state } from 'lit/decorators.js';
|
|
9
9
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
10
|
+
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
10
11
|
import { snapshotService as defaultSnapshotService } from './snapshot-service';
|
|
12
|
+
function isMarkupIcon(icon) {
|
|
13
|
+
return icon.trimStart().startsWith('<');
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Visual identity: a contact sheet. Every tile is a "frame" — numbered like a strip
|
|
13
17
|
* of negatives — because that's literally what a snapshot thumbnail is. All colors
|
|
@@ -25,10 +29,12 @@ export class SnapshotNavList extends LitElement {
|
|
|
25
29
|
this.overlayOpacity = 0.35;
|
|
26
30
|
/** backdrop blur behind the title, in px */
|
|
27
31
|
this.overlayBlur = 0;
|
|
28
|
-
/** icon-only only: 'bottom' is the caption strip (default), 'center'
|
|
32
|
+
/** icon-only only: 'bottom' is the caption strip (default), 'center' centers a larger title. */
|
|
29
33
|
this.labelPosition = 'bottom';
|
|
30
34
|
/** Defaults to the shared singleton — set your own instance (e.g. a namespaced or custom-storage SnapshotService) per <snapshot-nav-list> if needed. */
|
|
31
35
|
this.snapshotService = defaultSnapshotService;
|
|
36
|
+
/** Shows a top-right edit button per card. Off by default — clicking it fires `nav-edit` instead of `nav-select`; the host decides what "edit" means (e.g. open its own dialog component). */
|
|
37
|
+
this.editable = false;
|
|
32
38
|
this.thumbs = new Map();
|
|
33
39
|
this.loadingIds = new Set();
|
|
34
40
|
/** In-flight dedup guard, separate from `loadingIds` (which is only for spinner display) so a repeat `loadThumb` call for an id already being fetched is a no-op. */
|
|
@@ -47,10 +53,8 @@ export class SnapshotNavList extends LitElement {
|
|
|
47
53
|
display: flex;
|
|
48
54
|
flex-direction: column;
|
|
49
55
|
gap: var(--snapshot-nav-list-gap, 0.3rem);
|
|
50
|
-
counter-reset: frame;
|
|
51
56
|
}
|
|
52
57
|
li {
|
|
53
|
-
counter-increment: frame;
|
|
54
58
|
display: flex;
|
|
55
59
|
align-items: center;
|
|
56
60
|
gap: 0.7rem;
|
|
@@ -136,6 +140,12 @@ export class SnapshotNavList extends LitElement {
|
|
|
136
140
|
font-size: 1.6rem;
|
|
137
141
|
opacity: 0.4;
|
|
138
142
|
}
|
|
143
|
+
.icon-lg svg {
|
|
144
|
+
width: 1.6rem;
|
|
145
|
+
height: 1.6rem;
|
|
146
|
+
display: block;
|
|
147
|
+
fill: currentColor;
|
|
148
|
+
}
|
|
139
149
|
.thumb-loading {
|
|
140
150
|
display: flex;
|
|
141
151
|
align-items: center;
|
|
@@ -183,15 +193,6 @@ export class SnapshotNavList extends LitElement {
|
|
|
183
193
|
gap: 0.15rem;
|
|
184
194
|
min-width: 0;
|
|
185
195
|
}
|
|
186
|
-
.frame-number {
|
|
187
|
-
font-family: var(--snapshot-nav-list-mono-font, ui-monospace, 'SF Mono', Menlo, monospace);
|
|
188
|
-
font-size: 0.65rem;
|
|
189
|
-
letter-spacing: 0.04em;
|
|
190
|
-
color: color-mix(in srgb, currentColor 55%, transparent);
|
|
191
|
-
}
|
|
192
|
-
.frame-number::before {
|
|
193
|
-
content: 'F' counter(frame, decimal-leading-zero);
|
|
194
|
-
}
|
|
195
196
|
.label {
|
|
196
197
|
overflow: hidden;
|
|
197
198
|
text-overflow: ellipsis;
|
|
@@ -199,6 +200,47 @@ export class SnapshotNavList extends LitElement {
|
|
|
199
200
|
color: inherit;
|
|
200
201
|
font-weight: 500;
|
|
201
202
|
}
|
|
203
|
+
.description {
|
|
204
|
+
overflow: hidden;
|
|
205
|
+
text-overflow: ellipsis;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
color: color-mix(in srgb, currentColor 60%, transparent);
|
|
208
|
+
font-size: 0.75rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.edit-button {
|
|
212
|
+
position: absolute;
|
|
213
|
+
top: 4px;
|
|
214
|
+
right: 4px;
|
|
215
|
+
z-index: 3;
|
|
216
|
+
display: flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
justify-content: center;
|
|
219
|
+
width: 24px;
|
|
220
|
+
height: 24px;
|
|
221
|
+
padding: 0;
|
|
222
|
+
border: none;
|
|
223
|
+
border-radius: 6px;
|
|
224
|
+
background: color-mix(in srgb, #000 55%, transparent);
|
|
225
|
+
color: #fff;
|
|
226
|
+
font-size: 0.85rem;
|
|
227
|
+
line-height: 1;
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
opacity: 0;
|
|
230
|
+
transition: opacity 0.15s ease;
|
|
231
|
+
}
|
|
232
|
+
li:hover .edit-button,
|
|
233
|
+
li:focus-within .edit-button {
|
|
234
|
+
opacity: 1;
|
|
235
|
+
}
|
|
236
|
+
.edit-button:hover {
|
|
237
|
+
background: color-mix(in srgb, #000 75%, transparent);
|
|
238
|
+
}
|
|
239
|
+
.edit-button:focus-visible {
|
|
240
|
+
opacity: 1;
|
|
241
|
+
outline: 2px solid var(--frame-accent);
|
|
242
|
+
outline-offset: 1px;
|
|
243
|
+
}
|
|
202
244
|
|
|
203
245
|
/* list: a compact thumb reads better in a narrow sidebar than the grid's 160x100 */
|
|
204
246
|
:host([variant='list']) .thumb-wrap {
|
|
@@ -227,33 +269,25 @@ export class SnapshotNavList extends LitElement {
|
|
|
227
269
|
:host([variant='icon-only']) .meta {
|
|
228
270
|
position: absolute;
|
|
229
271
|
inset: auto 0 0 0;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
justify-content: space-between;
|
|
233
|
-
gap: 0.4rem;
|
|
272
|
+
align-items: flex-start;
|
|
273
|
+
gap: 0.15rem;
|
|
234
274
|
padding: 0.4rem 0.5rem;
|
|
235
275
|
color: var(--overlay-text, #fff);
|
|
236
276
|
}
|
|
237
277
|
:host([variant='icon-only']) .label {
|
|
238
278
|
white-space: normal;
|
|
239
279
|
}
|
|
240
|
-
:host([variant='icon-only']) .
|
|
241
|
-
color: color-mix(in srgb,
|
|
242
|
-
flex-shrink: 0;
|
|
280
|
+
:host([variant='icon-only']) .description {
|
|
281
|
+
color: color-mix(in srgb, var(--overlay-text, #fff) 75%, transparent);
|
|
243
282
|
}
|
|
244
283
|
|
|
245
|
-
/* label-position="center":
|
|
284
|
+
/* label-position="center": title big and centered */
|
|
246
285
|
:host([variant='icon-only'][label-position='center']) .meta {
|
|
247
286
|
inset: 0;
|
|
248
287
|
align-items: center;
|
|
249
288
|
justify-content: center;
|
|
250
289
|
padding: 0.6rem;
|
|
251
290
|
}
|
|
252
|
-
:host([variant='icon-only'][label-position='center']) .frame-number {
|
|
253
|
-
position: absolute;
|
|
254
|
-
top: 0.6rem;
|
|
255
|
-
left: 0.6rem;
|
|
256
|
-
}
|
|
257
291
|
:host([variant='icon-only'][label-position='center']) .label {
|
|
258
292
|
font-size: 1.15rem;
|
|
259
293
|
font-weight: 600;
|
|
@@ -349,6 +383,14 @@ export class SnapshotNavList extends LitElement {
|
|
|
349
383
|
composed: true,
|
|
350
384
|
}));
|
|
351
385
|
}
|
|
386
|
+
edit(e, item) {
|
|
387
|
+
e.stopPropagation();
|
|
388
|
+
this.dispatchEvent(new CustomEvent('nav-edit', {
|
|
389
|
+
detail: { id: item.id, route: item.route },
|
|
390
|
+
bubbles: true,
|
|
391
|
+
composed: true,
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
352
394
|
render() {
|
|
353
395
|
const overlayStyle = this.overlayStyle;
|
|
354
396
|
return html `
|
|
@@ -369,13 +411,26 @@ export class SnapshotNavList extends LitElement {
|
|
|
369
411
|
<span class="spinner" part="spinner"></span>
|
|
370
412
|
</div>`
|
|
371
413
|
: html `<div class="thumb thumb-placeholder" part="thumb" aria-hidden="true">
|
|
372
|
-
<span class="icon-lg"
|
|
414
|
+
<span class="icon-lg"
|
|
415
|
+
>${item.icon ? (isMarkupIcon(item.icon) ? unsafeHTML(item.icon) : item.icon) : ''}</span
|
|
416
|
+
>
|
|
373
417
|
</div>`}
|
|
374
418
|
<div class="image-overlay" part="overlay" style=${styleMap(overlayStyle)}></div>
|
|
419
|
+
${this.editable
|
|
420
|
+
? html `<button
|
|
421
|
+
type="button"
|
|
422
|
+
class="edit-button"
|
|
423
|
+
part="edit-button"
|
|
424
|
+
aria-label="Edit ${item.label}"
|
|
425
|
+
@click=${(e) => this.edit(e, item)}
|
|
426
|
+
>
|
|
427
|
+
✎
|
|
428
|
+
</button>`
|
|
429
|
+
: ''}
|
|
375
430
|
</div>
|
|
376
431
|
<div class="meta" part="meta">
|
|
377
|
-
<span class="frame-number" part="frame-number" aria-hidden="true"></span>
|
|
378
432
|
<span class="label" part="label">${item.label}</span>
|
|
433
|
+
${item.description ? html `<span class="description" part="description">${item.description}</span>` : ''}
|
|
379
434
|
</div>
|
|
380
435
|
</li>
|
|
381
436
|
`)}
|
|
@@ -404,6 +459,9 @@ __decorate([
|
|
|
404
459
|
__decorate([
|
|
405
460
|
property({ attribute: false })
|
|
406
461
|
], SnapshotNavList.prototype, "snapshotService", void 0);
|
|
462
|
+
__decorate([
|
|
463
|
+
property({ type: Boolean })
|
|
464
|
+
], SnapshotNavList.prototype, "editable", void 0);
|
|
407
465
|
__decorate([
|
|
408
466
|
state()
|
|
409
467
|
], SnapshotNavList.prototype, "thumbs", void 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anton-gustafsson/snapshot-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A pluggable snapshot service that turns any DOM element into a stored, shareable image, plus an optional <snapshot-nav-list> web component to display them.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|