@anton-gustafsson/snapshot-core 0.0.5 → 0.0.6

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.
@@ -8,7 +8,7 @@ export interface NavItem {
8
8
  route?: string;
9
9
  description?: string;
10
10
  }
11
- export type SnapshotNavListVariant = 'list' | 'icon-only';
11
+ export type SnapshotNavListVariant = 'list' | 'icon-only' | 'card';
12
12
  /**
13
13
  * Visual identity: a contact sheet. Every tile is a "frame" — numbered like a strip
14
14
  * of negatives — because that's literally what a snapshot thumbnail is. All colors
@@ -21,8 +21,10 @@ export declare class SnapshotNavList extends LitElement {
21
21
  variant: SnapshotNavListVariant;
22
22
  /** icon-only tile overlay: tint behind the title so it stays legible over any image. Transparent by default — opt into a scrim explicitly. */
23
23
  overlayTint: 'dark' | 'light' | 'none';
24
- /** overlay tint strength, 0-1 */
25
- overlayOpacity: number;
24
+ /** caption background tint strength, 0-1 */
25
+ textOverlayOpacity: number;
26
+ /** image scrim tint strength, 0-1 — 0 keeps the image clear (blur only) */
27
+ imageOverlayOpacity: number;
26
28
  /** backdrop blur behind the title, in px */
27
29
  overlayBlur: number;
28
30
  /** icon-only only: 'bottom' is the caption strip (default), 'center' centers a larger title. */
@@ -42,7 +44,10 @@ export declare class SnapshotNavList extends LitElement {
42
44
  willUpdate(changed: Map<string, unknown>): void;
43
45
  updated(changed: Map<string, unknown>): void;
44
46
  private loadThumb;
45
- private get overlayStyle();
47
+ /** image scrim: blur + its own (usually 0) tint strength — independent of the caption's. */
48
+ private get imageOverlayStyle();
49
+ /** caption background: tint (to pop the text) + the same blur. */
50
+ private get metaStyle();
46
51
  private select;
47
52
  private edit;
48
53
  render(): import("lit-html").TemplateResult<1>;
@@ -25,8 +25,10 @@ export class SnapshotNavList extends LitElement {
25
25
  this.variant = 'icon-only';
26
26
  /** icon-only tile overlay: tint behind the title so it stays legible over any image. Transparent by default — opt into a scrim explicitly. */
27
27
  this.overlayTint = 'none';
28
- /** overlay tint strength, 0-1 */
29
- this.overlayOpacity = 0.35;
28
+ /** caption background tint strength, 0-1 */
29
+ this.textOverlayOpacity = 0.35;
30
+ /** image scrim tint strength, 0-1 — 0 keeps the image clear (blur only) */
31
+ this.imageOverlayOpacity = 0;
30
32
  /** backdrop blur behind the title, in px */
31
33
  this.overlayBlur = 0;
32
34
  /** icon-only only: 'bottom' is the caption strip (default), 'center' centers a larger title. */
@@ -171,7 +173,7 @@ export class SnapshotNavList extends LitElement {
171
173
  }
172
174
  }
173
175
 
174
- /* tint/blur controls apply only to the image, via this scrim layered on top of it */
176
+ /* independent from .meta's tint image-overlay-opacity defaults to 0 so the image stays clear (blur only) */
175
177
  .image-overlay {
176
178
  position: absolute;
177
179
  inset: 0;
@@ -298,6 +300,72 @@ export class SnapshotNavList extends LitElement {
298
300
  font-weight: 600;
299
301
  text-align: center;
300
302
  }
303
+
304
+ /* card: a contained (never cropped) preview above real body text below it —
305
+ text never sits on top of the image, so unlike icon-only it needs no
306
+ overlay tint to stay legible. Modeled on a typical "preview card"
307
+ pattern: framed shot, title + description underneath, shadow on hover. */
308
+ :host([variant='card']) ul {
309
+ display: grid;
310
+ grid-template-columns: repeat(auto-fill, minmax(var(--snapshot-nav-list-card-min-width, 220px), 1fr));
311
+ gap: var(--snapshot-nav-list-card-gap, 1.25rem);
312
+ }
313
+ :host([variant='card']) li {
314
+ flex-direction: column;
315
+ align-items: stretch;
316
+ gap: 0;
317
+ padding: var(--snapshot-nav-list-card-padding, 0.5rem);
318
+ background: var(--snapshot-nav-list-card-bg, transparent);
319
+ border: 1px solid color-mix(in srgb, currentColor 12%, transparent);
320
+ box-shadow: none;
321
+ transition:
322
+ box-shadow var(--snapshot-nav-list-card-transition-dur, 0.15s) ease,
323
+ border-color var(--snapshot-nav-list-card-transition-dur, 0.15s) ease;
324
+ }
325
+ :host([variant='card']) li:hover,
326
+ :host([variant='card']) li:focus-visible {
327
+ background: var(--snapshot-nav-list-card-bg, transparent);
328
+ box-shadow: var(--snapshot-nav-list-card-shadow, 0 2px 8px color-mix(in srgb, currentColor 18%, transparent));
329
+ border-color: color-mix(in srgb, currentColor 22%, transparent);
330
+ }
331
+ :host([variant='card']) li:focus-visible {
332
+ outline: none;
333
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-accent) 55%, transparent);
334
+ }
335
+ :host([variant='card']) li:focus-visible .thumb-wrap {
336
+ outline: none;
337
+ }
338
+ :host([variant='card']) .thumb-wrap {
339
+ width: 100%;
340
+ height: auto;
341
+ aspect-ratio: 2 / 1;
342
+ border-radius: var(--snapshot-nav-list-radius-sm, 7px);
343
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent);
344
+ background: color-mix(in srgb, currentColor 4%, transparent);
345
+ display: grid;
346
+ place-items: center;
347
+ }
348
+ :host([variant='card']) .thumb-wrap::before,
349
+ :host([variant='card']) .thumb-wrap::after {
350
+ display: none;
351
+ }
352
+ :host([variant='card']) img.thumb {
353
+ /* contain, not cover — the whole dashboard stays readable, nothing cropped */
354
+ object-fit: contain;
355
+ }
356
+ :host([variant='card']) .meta {
357
+ padding: 0.75rem 0.5rem 0.5rem;
358
+ gap: 0.25rem;
359
+ }
360
+ :host([variant='card']) .label {
361
+ white-space: normal;
362
+ font-size: 1rem;
363
+ font-weight: 600;
364
+ }
365
+ :host([variant='card']) .description {
366
+ white-space: normal;
367
+ font-size: 0.8125rem;
368
+ }
301
369
  `; }
302
370
  subscribeToService() {
303
371
  this.unsubscribe?.();
@@ -371,13 +439,24 @@ export class SnapshotNavList extends LitElement {
371
439
  this.requestUpdate();
372
440
  }
373
441
  }
374
- get overlayStyle() {
442
+ /** image scrim: blur + its own (usually 0) tint strength — independent of the caption's. */
443
+ get imageOverlayStyle() {
444
+ const blur = `${this.overlayBlur}px`;
445
+ if (this.overlayTint === 'none' || this.imageOverlayOpacity === 0) {
446
+ return { '--overlay-blur': blur };
447
+ }
448
+ const tintColor = this.overlayTint === 'light' ? '#fff' : '#000';
449
+ const bg = `color-mix(in srgb, ${tintColor} ${Math.round(this.imageOverlayOpacity * 100)}%, transparent)`;
450
+ return { '--overlay-bg': bg, '--overlay-blur': blur };
451
+ }
452
+ /** caption background: tint (to pop the text) + the same blur. */
453
+ get metaStyle() {
375
454
  const blur = `${this.overlayBlur}px`;
376
455
  if (this.overlayTint === 'none') {
377
456
  return { '--overlay-bg': 'transparent', '--overlay-text': 'inherit', '--overlay-blur': blur };
378
457
  }
379
458
  const tintColor = this.overlayTint === 'light' ? '#fff' : '#000';
380
- const bg = `color-mix(in srgb, ${tintColor} ${Math.round(this.overlayOpacity * 100)}%, transparent)`;
459
+ const bg = `color-mix(in srgb, ${tintColor} ${Math.round(this.textOverlayOpacity * 100)}%, transparent)`;
381
460
  const text = this.overlayTint === 'light' ? '#111' : '#fff';
382
461
  return { '--overlay-bg': bg, '--overlay-text': text, '--overlay-blur': blur };
383
462
  }
@@ -397,7 +476,8 @@ export class SnapshotNavList extends LitElement {
397
476
  }));
398
477
  }
399
478
  render() {
400
- const overlayStyle = this.overlayStyle;
479
+ const imageOverlayStyle = this.imageOverlayStyle;
480
+ const metaStyle = this.metaStyle;
401
481
  return html `
402
482
  <ul role="listbox">
403
483
  ${this.items.map((item) => html `
@@ -420,7 +500,7 @@ export class SnapshotNavList extends LitElement {
420
500
  >${item.icon ? (isMarkupIcon(item.icon) ? unsafeHTML(item.icon) : item.icon) : ''}</span
421
501
  >
422
502
  </div>`}
423
- <div class="image-overlay" part="overlay" style=${styleMap(overlayStyle)}></div>
503
+ <div class="image-overlay" part="overlay" style=${styleMap(imageOverlayStyle)}></div>
424
504
  ${this.editable
425
505
  ? html `<button
426
506
  type="button"
@@ -433,7 +513,7 @@ export class SnapshotNavList extends LitElement {
433
513
  </button>`
434
514
  : ''}
435
515
  </div>
436
- <div class="meta" part="meta" style=${styleMap(overlayStyle)}>
516
+ <div class="meta" part="meta" style=${styleMap(metaStyle)}>
437
517
  <span class="label" part="label">${item.label}</span>
438
518
  ${item.description ? html `<span class="description" part="description">${item.description}</span>` : ''}
439
519
  </div>
@@ -453,8 +533,11 @@ __decorate([
453
533
  property({ attribute: 'overlay-tint' })
454
534
  ], SnapshotNavList.prototype, "overlayTint", void 0);
455
535
  __decorate([
456
- property({ type: Number, attribute: 'overlay-opacity' })
457
- ], SnapshotNavList.prototype, "overlayOpacity", void 0);
536
+ property({ type: Number, attribute: 'text-overlay-opacity' })
537
+ ], SnapshotNavList.prototype, "textOverlayOpacity", void 0);
538
+ __decorate([
539
+ property({ type: Number, attribute: 'image-overlay-opacity' })
540
+ ], SnapshotNavList.prototype, "imageOverlayOpacity", void 0);
458
541
  __decorate([
459
542
  property({ type: Number, attribute: 'overlay-blur' })
460
543
  ], SnapshotNavList.prototype, "overlayBlur", void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anton-gustafsson/snapshot-core",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
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": {