@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.
@@ -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' pins the frame number to the corner and centers a larger title. */
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' pins the frame number to the corner and centers a larger title. */
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
- flex-direction: row;
231
- align-items: baseline;
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']) .frame-number {
241
- color: color-mix(in srgb, currentColor 70%, transparent);
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": frame number pinned to the corner, title big and centered */
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">${item.icon ?? ''}</span>
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.2",
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": {