@anton-gustafsson/snapshot-core 0.4.3 → 0.4.4

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.
@@ -4,8 +4,10 @@ export interface NavItem<T = unknown> {
4
4
  /** The snapshot id — a plain domain id. Anything else the consumer needs belongs in `data`. */
5
5
  id: string;
6
6
  label: string;
7
- /** 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. */
7
+ /** Markup `<svg>`, `<img>`, or any elementrendered as raw HTML in the placeholder frame shown before a card's first capture. Must start with `<`: a bare glyph or emoji is ignored (it read as an icon but rendered as a stray character at whatever the frame's font happened to be); use `placeholderText` for words. */
8
8
  icon?: string;
9
+ /** Per-item override of the component-level `placeholderText` — a caption in the frame shown before this card's first capture (e.g. "not visited yet"). Plain text only; pass `''` to show none where the component sets one. */
10
+ placeholderText?: string;
9
11
  description?: string;
10
12
  /** Arbitrary consumer payload — echoed back verbatim on `nav-select` / `nav-edit`, so no lookup-by-id is needed in the handler. */
11
13
  data?: T;
@@ -40,6 +42,14 @@ export declare class SnapshotNavList extends LitElement {
40
42
  editButtonPosition: SnapshotNavListEditButtonPosition;
41
43
  /** Edit button glyph. Same convention as `NavItem.icon`: a plain-text glyph (e.g. an emoji), or markup — a string starting with `<` renders as raw HTML/SVG, so a consumer can pass its own icon (e.g. `<svg>...</svg>`). */
42
44
  editIcon: string;
45
+ /**
46
+ * Caption shown in the frame of a card with no capture yet (e.g. "no snapshot
47
+ * yet"), for every card at once; `NavItem.placeholderText` overrides it per
48
+ * row. Plain text, never markup — it's rendered as text, not HTML. Empty by
49
+ * default, which keeps the icon-and-hatch placeholder; set it and the frame
50
+ * goes see-through with a dashed edge instead.
51
+ */
52
+ placeholderText: string;
43
53
  private thumbs;
44
54
  private loadingIds;
45
55
  /**
@@ -69,6 +79,8 @@ export declare class SnapshotNavList extends LitElement {
69
79
  private edit;
70
80
  private isEditable;
71
81
  private renderEditButton;
82
+ /** The frame before a card's first capture: an icon, a caption, or both. */
83
+ private renderPlaceholder;
72
84
  render(): import("lit-html").TemplateResult<1>;
73
85
  }
74
86
  /** Detail type of both `nav-select` and `nav-edit` — the clicked item itself. */
@@ -11,6 +11,22 @@ import { snapshotService as defaultSnapshotService } from './snapshot-service';
11
11
  function isMarkupIcon(icon) {
12
12
  return icon.trimStart().startsWith('<');
13
13
  }
14
+ const warnedTextIcons = new Set();
15
+ /**
16
+ * `NavItem.icon` is markup-only, so a plain-text glyph renders as nothing —
17
+ * silently, without this, since the old behavior was to paint it as text.
18
+ * Warns once per distinct value: `items` re-renders on every thumbnail that
19
+ * lands, and a whole list of glyph icons would otherwise flood the console.
20
+ */
21
+ function markupIconOrWarn(icon) {
22
+ if (isMarkupIcon(icon))
23
+ return icon;
24
+ if (!warnedTextIcons.has(icon)) {
25
+ warnedTextIcons.add(icon);
26
+ console.warn(`<snapshot-nav-list>: NavItem.icon takes markup (e.g. '<svg>...</svg>'), not text — ignoring ${JSON.stringify(icon)}. Use placeholderText for a caption.`);
27
+ }
28
+ return undefined;
29
+ }
14
30
  const DEFAULT_EDIT_ICON = '✎';
15
31
  /**
16
32
  * A grid of preview cards: a contained (never-cropped) screenshot above real
@@ -31,6 +47,14 @@ export class SnapshotNavList extends LitElement {
31
47
  this.editButtonPosition = 'overlay';
32
48
  /** Edit button glyph. Same convention as `NavItem.icon`: a plain-text glyph (e.g. an emoji), or markup — a string starting with `<` renders as raw HTML/SVG, so a consumer can pass its own icon (e.g. `<svg>...</svg>`). */
33
49
  this.editIcon = DEFAULT_EDIT_ICON;
50
+ /**
51
+ * Caption shown in the frame of a card with no capture yet (e.g. "no snapshot
52
+ * yet"), for every card at once; `NavItem.placeholderText` overrides it per
53
+ * row. Plain text, never markup — it's rendered as text, not HTML. Empty by
54
+ * default, which keeps the icon-and-hatch placeholder; set it and the frame
55
+ * goes see-through with a dashed edge instead.
56
+ */
57
+ this.placeholderText = '';
34
58
  this.thumbs = new Map();
35
59
  this.loadingIds = new Set();
36
60
  /**
@@ -120,28 +144,75 @@ export class SnapshotNavList extends LitElement {
120
144
  }
121
145
  /* unexposed frame: fine diagonal hatch instead of a generic gradient blob */
122
146
  .thumb-placeholder {
147
+ box-sizing: border-box;
123
148
  display: flex;
149
+ flex-direction: column;
124
150
  align-items: center;
125
151
  justify-content: center;
126
- background-color: color-mix(in srgb, currentColor 7%, transparent);
127
- background-image: repeating-linear-gradient(
128
- 135deg,
129
- color-mix(in srgb, currentColor 16%, transparent) 0px,
130
- color-mix(in srgb, currentColor 16%, transparent) 1.5px,
131
- transparent 1.5px,
132
- transparent 7px
152
+ gap: 0.4rem;
153
+ padding: 0.45rem 0;
154
+ background-color: var(--snapshot-nav-list-placeholder-bg, color-mix(in srgb, currentColor 7%, transparent));
155
+ /* var-driven so a plain 'none' turns the hatch off on its own — a card
156
+ showing real fallback art wants the frame plain behind it. */
157
+ background-image: var(
158
+ --snapshot-nav-list-placeholder-hatch,
159
+ repeating-linear-gradient(
160
+ 135deg,
161
+ color-mix(in srgb, currentColor 16%, transparent) 0px,
162
+ color-mix(in srgb, currentColor 16%, transparent) 1.5px,
163
+ transparent 1.5px,
164
+ transparent 7px
165
+ )
133
166
  );
134
167
  }
168
+ /* A caption carries the "nothing here yet" meaning on its own, so the hatch
169
+ (and the tint under it) would only fight it — the frame goes see-through,
170
+ marked out by a dashed edge instead. */
171
+ .thumb-placeholder.has-text {
172
+ background-color: var(--snapshot-nav-list-placeholder-bg, transparent);
173
+ background-image: none;
174
+ border: 1px dashed var(--snapshot-nav-list-placeholder-border, color-mix(in srgb, currentColor 22%, transparent));
175
+ border-radius: inherit;
176
+ }
177
+ .placeholder-text {
178
+ font-family: var(--snapshot-nav-list-placeholder-font, ui-monospace, SFMono-Regular, Menlo, monospace);
179
+ font-size: var(--snapshot-nav-list-placeholder-font-size, 0.75rem);
180
+ letter-spacing: var(--snapshot-nav-list-placeholder-letter-spacing, 0.02em);
181
+ color: var(--snapshot-nav-list-placeholder-color, color-mix(in srgb, currentColor 55%, transparent));
182
+ max-width: 100%;
183
+ padding: 0 0.5rem;
184
+ overflow: hidden;
185
+ text-overflow: ellipsis;
186
+ white-space: nowrap;
187
+ text-align: center;
188
+ }
189
+ /* Spans the frame (minus the caption, if any) rather than shrinking to the
190
+ glyph: a percentage width/height on a fallback <img> then has a real box
191
+ to resolve against, while a default-sized icon still sits centered. */
135
192
  .icon-lg {
136
- font-size: 1.6rem;
137
- opacity: 0.4;
193
+ flex: 1 1 auto;
194
+ min-height: 0;
195
+ width: 100%;
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: center;
199
+ opacity: var(--snapshot-nav-list-placeholder-icon-opacity, 0.4);
200
+ }
201
+ /* Sized off one var so a card using a real fallback image (rather than a
202
+ small glyph-sized icon) can grow it to fill the frame from outside the
203
+ shadow root. */
204
+ .icon-lg svg,
205
+ .icon-lg img {
206
+ width: var(--snapshot-nav-list-placeholder-icon-size, 1.6rem);
207
+ height: var(--snapshot-nav-list-placeholder-icon-size, 1.6rem);
208
+ display: block;
138
209
  }
139
210
  .icon-lg svg {
140
- width: 1.6rem;
141
- height: 1.6rem;
142
- display: block;
143
211
  fill: currentColor;
144
212
  }
213
+ .icon-lg img {
214
+ object-fit: var(--snapshot-nav-list-placeholder-icon-fit, contain);
215
+ }
145
216
  .thumb-loading {
146
217
  display: flex;
147
218
  align-items: center;
@@ -392,6 +463,15 @@ export class SnapshotNavList extends LitElement {
392
463
  ${isMarkupIcon(icon) ? unsafeHTML(icon) : icon}
393
464
  </button>`;
394
465
  }
466
+ /** The frame before a card's first capture: an icon, a caption, or both. */
467
+ renderPlaceholder(item) {
468
+ const text = item.placeholderText ?? this.placeholderText;
469
+ const icon = item.icon ? markupIconOrWarn(item.icon) : undefined;
470
+ return html `<div class="thumb thumb-placeholder ${text ? 'has-text' : ''}" part="thumb" aria-hidden="true">
471
+ ${icon ? html `<span class="icon-lg" part="placeholder-icon">${unsafeHTML(icon)}</span>` : ''}
472
+ ${text ? html `<span class="placeholder-text" part="placeholder-text">${text}</span>` : ''}
473
+ </div>`;
474
+ }
395
475
  render() {
396
476
  const editInMeta = this.editButtonPosition === 'meta';
397
477
  return html `
@@ -411,11 +491,7 @@ export class SnapshotNavList extends LitElement {
411
491
  ? html `<div class="thumb thumb-loading" part="thumb" aria-hidden="true">
412
492
  <span class="spinner" part="spinner"></span>
413
493
  </div>`
414
- : html `<div class="thumb thumb-placeholder" part="thumb" aria-hidden="true">
415
- <span class="icon-lg"
416
- >${item.icon ? (isMarkupIcon(item.icon) ? unsafeHTML(item.icon) : item.icon) : ''}</span
417
- >
418
- </div>`}
494
+ : this.renderPlaceholder(item)}
419
495
  ${this.isEditable(item) && !editInMeta ? this.renderEditButton(item) : ''}
420
496
  </div>
421
497
  <div class="meta" part="meta">
@@ -452,6 +528,9 @@ __decorate([
452
528
  __decorate([
453
529
  property({ attribute: 'edit-icon' })
454
530
  ], SnapshotNavList.prototype, "editIcon", void 0);
531
+ __decorate([
532
+ property({ attribute: 'placeholder-text' })
533
+ ], SnapshotNavList.prototype, "placeholderText", void 0);
455
534
  __decorate([
456
535
  state()
457
536
  ], 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.4.3",
3
+ "version": "0.4.4",
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": {