@phosart/common 0.4.55 → 0.4.57

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/Image.svelte CHANGED
@@ -15,6 +15,7 @@
15
15
  loading?: boolean;
16
16
  transformSrc?: (src: string) => string;
17
17
  nsfw?: boolean;
18
+ lqipFit?: 'cover' | 'contain';
18
19
  }
19
20
 
20
21
  let {
@@ -26,7 +27,8 @@
26
27
  controls = false,
27
28
  nolqip = false,
28
29
  loading = $bindable(true),
29
- transformSrc: userTransformSrc
30
+ transformSrc: userTransformSrc,
31
+ lqipFit = 'contain'
30
32
  }: Props = $props();
31
33
 
32
34
  const config = useLibraryConfig();
@@ -43,7 +45,9 @@
43
45
  let src = $derived(highRes ? onlyHighRes(picture) : no4K(picture));
44
46
 
45
47
  let background = $derived(
46
- !nolqip && src.lqip && showBackground ? `url(${src.lqip.src}) no-repeat center/contain` : 'none'
48
+ !nolqip && src.lqip && showBackground
49
+ ? `url(${src.lqip.src}) no-repeat center/${lqipFit}`
50
+ : 'none'
47
51
  );
48
52
 
49
53
  $effect(() => {
@@ -9,6 +9,7 @@ interface Props {
9
9
  loading?: boolean;
10
10
  transformSrc?: (src: string) => string;
11
11
  nsfw?: boolean;
12
+ lqipFit?: 'cover' | 'contain';
12
13
  }
13
14
  declare const Image: import("svelte").Component<Props, {}, "loading">;
14
15
  type Image = ReturnType<typeof Image>;
@@ -1 +1 @@
1
- {"version":3,"file":"Image.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Image.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,KAAK,IAAI,UAAU,EAAQ,MAAM,eAAe,CAAC;AAO3E,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAyHF,QAAA,MAAM,KAAK,kDAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"Image.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Image.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAe,KAAK,KAAK,IAAI,UAAU,EAAQ,MAAM,eAAe,CAAC;AAO3E,UAAU,KAAK;IACd,OAAO,EAAE,UAAU,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B;AA4HF,QAAA,MAAM,KAAK,kDAAwC,CAAC;AACpD,KAAK,KAAK,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AACtC,eAAe,KAAK,CAAC"}
@@ -27,19 +27,23 @@
27
27
  parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
28
28
  );
29
29
 
30
+ let altWidth: number = $state(0);
30
31
  let containerWidth: number = $state(0);
31
32
  let containerHeight: number = $state(0);
32
33
  let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
34
+ let containerWidthLessAlt = $derived(containerWidth - altWidth);
33
35
  let isComic = $derived(piece.alts_display === 'comic_panels');
34
36
  let bounded: HTMLDivElement | null = $state(null);
37
+ let selectedAlt: number | null = $state(null);
35
38
 
36
39
  function scale(image: Picture) {
37
40
  const scaleByHeight =
38
41
  !isComic &&
39
- containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
42
+ containerWidthLessAlt / containerHeightLessInfo >
43
+ image.full.fallback.w / image.full.fallback.h;
40
44
  const scalingFactor: number = scaleByHeight
41
45
  ? containerHeightLessInfo / image.full.fallback.h
42
- : containerWidth / image.full.fallback.w;
46
+ : containerWidthLessAlt / image.full.fallback.w;
43
47
 
44
48
  return scalingFactor;
45
49
  }
@@ -148,35 +152,101 @@
148
152
  bind:this={bounded}
149
153
  >
150
154
  <div class="flex flex-col">
151
- <div
152
- class="bounded-div"
153
- onkeypress={(e) => {
154
- e.stopPropagation();
155
- scrollDown();
156
- }}
157
- onclick={(e) => {
158
- e.stopPropagation();
159
- }}
160
- role="button"
161
- tabindex={-1}
162
- style="width: {w}px; height: {h}px;{isComic ? ' border-radius: 12px 12px 0 0;' : ''}"
163
- >
164
- <ImageView {piece} {nameInHeader}>
165
- {#snippet display(image, onloaded)}
166
- <div class="image-container">
167
- <Image
168
- video={image.video?.full}
169
- controls
170
- picture={image.image.full}
171
- alt={image.alt}
172
- nsfw={image.nsfw}
173
- {onloaded}
174
- />
175
- </div>
176
- {/snippet}
177
- </ImageView>
155
+ <div class="flex">
156
+ <div
157
+ class="bounded-div"
158
+ onkeypress={(e) => {
159
+ e.stopPropagation();
160
+ scrollDown();
161
+ }}
162
+ onclick={(e) => {
163
+ e.stopPropagation();
164
+ }}
165
+ role="button"
166
+ tabindex={-1}
167
+ style="
168
+ z-index: 2; width: {w}px; height: {h}px;{isComic
169
+ ? ' border-radius: 12px 12px 0 0;'
170
+ : piece.alts && piece.alts.length > 0
171
+ ? ' border-radius: 12px 0 0 12px;'
172
+ : ''}"
173
+ >
174
+ <ImageView bind:selectedAlt {piece} {nameInHeader}>
175
+ {#snippet display(image, onloaded)}
176
+ <div class="image-container">
177
+ <Image
178
+ video={image.video?.full}
179
+ controls
180
+ picture={image.image.full}
181
+ alt={image.alt}
182
+ nsfw={image.nsfw}
183
+ {onloaded}
184
+ />
185
+ </div>
186
+ {/snippet}
187
+ </ImageView>
188
+ </div>
189
+ {#if piece.alts && piece.alts.length > 0}
190
+ <div
191
+ bind:clientWidth={altWidth}
192
+ style="background-color: transparent; width:75px; position: relative"
193
+ >
194
+ {#each piece.alts as alt, i ('alt-' + JSON.stringify(alt))}
195
+ {@const isOriginal = i === selectedAlt}
196
+ {@const selPiece = isOriginal ? piece : alt}
197
+ {@const top = (i * h) / piece.alts.length}
198
+ {@const WRatio = 75 / selPiece.image.full.fallback.w}
199
+ {@const myH = selPiece.image.full.fallback.h * WRatio}
200
+
201
+ <div>
202
+ <button
203
+ onclick={(e) => {
204
+ e.stopPropagation();
205
+ selectedAlt = isOriginal ? null : i;
206
+ }}
207
+ tabindex={-1}
208
+ class="altcarry"
209
+ style="top: {top}px; width:75px; height: {h /
210
+ piece.alts.length}px; overflow:hidden; position: absolute; z-index: 1;"
211
+ >
212
+ <div
213
+ style="width: 75px; height: {Math.max(
214
+ myH,
215
+ h / piece.alts.length
216
+ )}px; min-height: {myH}px; position:absolute; top:0;right:0;"
217
+ >
218
+ <div class="image-container objr" style="top: 0; object-fit: cover">
219
+ <Image
220
+ video={selPiece.video?.full}
221
+ controls
222
+ picture={selPiece.image.full}
223
+ alt={selPiece.alt}
224
+ loading={false}
225
+ nolqip
226
+ />
227
+ </div>
228
+ </div>
229
+ </button>
230
+ <div
231
+ class="alt-tooltip"
232
+ style="z-index: 3; height: 2rem; text-overflow: elipsis; top: calc({top}px + 1rem); color: white; padding: 0.5rem; position: absolute; left: -9rem; width: 8rem; display: flex; justify-content: end; align-items: center;"
233
+ >
234
+ <div
235
+ style="background-color: #0009; border-radius: 0.25rem; padding: 0 0.25rem"
236
+ >
237
+ {#if isOriginal}
238
+ Back to Original Piece
239
+ {:else}
240
+ Alt: {alt.name}
241
+ {/if}
242
+ </div>
243
+ </div>
244
+ </div>
245
+ {/each}
246
+ </div>
247
+ {/if}
178
248
  </div>
179
- {#if isComic && piece.alts}
249
+ {#if isComic && piece.alts && piece.alts.length > 0}
180
250
  {#each piece.alts as alt, i (JSON.stringify(alt))}
181
251
  <div
182
252
  class="bounded-div"
@@ -287,4 +357,30 @@
287
357
  left: 0;
288
358
  right: 0;
289
359
  }
360
+
361
+ .objr :global(img) {
362
+ object-position: right center;
363
+ object-fit: cover;
364
+ }
365
+
366
+ .altcarry {
367
+ right: 1rem;
368
+ transition:
369
+ right 0.2s ease-in-out,
370
+ clip-path 0.2s ease-in-out;
371
+ clip-path: inset(0 0 0 16px);
372
+ }
373
+ .altcarry:hover {
374
+ right: 0rem;
375
+ clip-path: inset(0 0 0 0);
376
+ }
377
+
378
+ .altcarry:hover ~ .alt-tooltip {
379
+ opacity: 1;
380
+ }
381
+ .alt-tooltip {
382
+ opacity: 0;
383
+ user-select: none;
384
+ transition: opacity 0.2s ease-in-out;
385
+ }
290
386
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ImageSection.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageSection.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,+CAA+C,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,gBAAgB,CAAC;AAMvD,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AAyKF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ImageSection.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageSection.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,+CAA+C,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,gBAAgB,CAAC;AAMvD,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACnB;AAsNF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -14,17 +14,16 @@
14
14
  display: Snippet<
15
15
  [image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
16
16
  >;
17
+ selectedAlt?: number | null;
17
18
  }
18
19
 
19
- let { piece, nameInHeader, display }: Props = $props();
20
+ let { piece, nameInHeader, display, selectedAlt = $bindable(null) }: Props = $props();
20
21
 
21
22
  let config = useLibraryConfig();
22
23
 
23
24
  let loading = $state(true);
24
25
  let showingDescription = $state(false);
25
26
 
26
- let selectedAlt: number | null = $state(null);
27
-
28
27
  let image = $derived(typeof selectedAlt === 'number' ? piece.alts![selectedAlt] : piece);
29
28
 
30
29
  run(() => {
@@ -7,8 +7,9 @@ interface Props {
7
7
  image: ArtPiece | NonNullable<ArtPiece['alts']>[number],
8
8
  onloaded: () => void
9
9
  ]>;
10
+ selectedAlt?: number | null;
10
11
  }
11
- declare const ImageView: import("svelte").Component<Props, {}, "">;
12
+ declare const ImageView: import("svelte").Component<Props, {}, "selectedAlt">;
12
13
  type ImageView = ReturnType<typeof ImageView>;
13
14
  export default ImageView;
14
15
  //# sourceMappingURL=ImageView.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageView.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageView.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CACf;QAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,QAAQ,EAAE,MAAM,IAAI;KAAC,CAC/E,CAAC;CACF;AA6CF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"ImageView.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageView.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGrC,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CACf;QAAC,KAAK,EAAE,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,QAAQ,EAAE,MAAM,IAAI;KAAC,CAC/E,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA2CF,QAAA,MAAM,SAAS,sDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phosart/common",
3
- "version": "0.4.55",
3
+ "version": "0.4.57",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",
@@ -15,6 +15,7 @@
15
15
  loading?: boolean;
16
16
  transformSrc?: (src: string) => string;
17
17
  nsfw?: boolean;
18
+ lqipFit?: 'cover' | 'contain';
18
19
  }
19
20
 
20
21
  let {
@@ -26,7 +27,8 @@
26
27
  controls = false,
27
28
  nolqip = false,
28
29
  loading = $bindable(true),
29
- transformSrc: userTransformSrc
30
+ transformSrc: userTransformSrc,
31
+ lqipFit = 'contain'
30
32
  }: Props = $props();
31
33
 
32
34
  const config = useLibraryConfig();
@@ -43,7 +45,9 @@
43
45
  let src = $derived(highRes ? onlyHighRes(picture) : no4K(picture));
44
46
 
45
47
  let background = $derived(
46
- !nolqip && src.lqip && showBackground ? `url(${src.lqip.src}) no-repeat center/contain` : 'none'
48
+ !nolqip && src.lqip && showBackground
49
+ ? `url(${src.lqip.src}) no-repeat center/${lqipFit}`
50
+ : 'none'
47
51
  );
48
52
 
49
53
  $effect(() => {
@@ -27,19 +27,23 @@
27
27
  parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
28
28
  );
29
29
 
30
+ let altWidth: number = $state(0);
30
31
  let containerWidth: number = $state(0);
31
32
  let containerHeight: number = $state(0);
32
33
  let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
34
+ let containerWidthLessAlt = $derived(containerWidth - altWidth);
33
35
  let isComic = $derived(piece.alts_display === 'comic_panels');
34
36
  let bounded: HTMLDivElement | null = $state(null);
37
+ let selectedAlt: number | null = $state(null);
35
38
 
36
39
  function scale(image: Picture) {
37
40
  const scaleByHeight =
38
41
  !isComic &&
39
- containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
42
+ containerWidthLessAlt / containerHeightLessInfo >
43
+ image.full.fallback.w / image.full.fallback.h;
40
44
  const scalingFactor: number = scaleByHeight
41
45
  ? containerHeightLessInfo / image.full.fallback.h
42
- : containerWidth / image.full.fallback.w;
46
+ : containerWidthLessAlt / image.full.fallback.w;
43
47
 
44
48
  return scalingFactor;
45
49
  }
@@ -148,35 +152,101 @@
148
152
  bind:this={bounded}
149
153
  >
150
154
  <div class="flex flex-col">
151
- <div
152
- class="bounded-div"
153
- onkeypress={(e) => {
154
- e.stopPropagation();
155
- scrollDown();
156
- }}
157
- onclick={(e) => {
158
- e.stopPropagation();
159
- }}
160
- role="button"
161
- tabindex={-1}
162
- style="width: {w}px; height: {h}px;{isComic ? ' border-radius: 12px 12px 0 0;' : ''}"
163
- >
164
- <ImageView {piece} {nameInHeader}>
165
- {#snippet display(image, onloaded)}
166
- <div class="image-container">
167
- <Image
168
- video={image.video?.full}
169
- controls
170
- picture={image.image.full}
171
- alt={image.alt}
172
- nsfw={image.nsfw}
173
- {onloaded}
174
- />
175
- </div>
176
- {/snippet}
177
- </ImageView>
155
+ <div class="flex">
156
+ <div
157
+ class="bounded-div"
158
+ onkeypress={(e) => {
159
+ e.stopPropagation();
160
+ scrollDown();
161
+ }}
162
+ onclick={(e) => {
163
+ e.stopPropagation();
164
+ }}
165
+ role="button"
166
+ tabindex={-1}
167
+ style="
168
+ z-index: 2; width: {w}px; height: {h}px;{isComic
169
+ ? ' border-radius: 12px 12px 0 0;'
170
+ : piece.alts && piece.alts.length > 0
171
+ ? ' border-radius: 12px 0 0 12px;'
172
+ : ''}"
173
+ >
174
+ <ImageView bind:selectedAlt {piece} {nameInHeader}>
175
+ {#snippet display(image, onloaded)}
176
+ <div class="image-container">
177
+ <Image
178
+ video={image.video?.full}
179
+ controls
180
+ picture={image.image.full}
181
+ alt={image.alt}
182
+ nsfw={image.nsfw}
183
+ {onloaded}
184
+ />
185
+ </div>
186
+ {/snippet}
187
+ </ImageView>
188
+ </div>
189
+ {#if piece.alts && piece.alts.length > 0}
190
+ <div
191
+ bind:clientWidth={altWidth}
192
+ style="background-color: transparent; width:75px; position: relative"
193
+ >
194
+ {#each piece.alts as alt, i ('alt-' + JSON.stringify(alt))}
195
+ {@const isOriginal = i === selectedAlt}
196
+ {@const selPiece = isOriginal ? piece : alt}
197
+ {@const top = (i * h) / piece.alts.length}
198
+ {@const WRatio = 75 / selPiece.image.full.fallback.w}
199
+ {@const myH = selPiece.image.full.fallback.h * WRatio}
200
+
201
+ <div>
202
+ <button
203
+ onclick={(e) => {
204
+ e.stopPropagation();
205
+ selectedAlt = isOriginal ? null : i;
206
+ }}
207
+ tabindex={-1}
208
+ class="altcarry"
209
+ style="top: {top}px; width:75px; height: {h /
210
+ piece.alts.length}px; overflow:hidden; position: absolute; z-index: 1;"
211
+ >
212
+ <div
213
+ style="width: 75px; height: {Math.max(
214
+ myH,
215
+ h / piece.alts.length
216
+ )}px; min-height: {myH}px; position:absolute; top:0;right:0;"
217
+ >
218
+ <div class="image-container objr" style="top: 0; object-fit: cover">
219
+ <Image
220
+ video={selPiece.video?.full}
221
+ controls
222
+ picture={selPiece.image.full}
223
+ alt={selPiece.alt}
224
+ loading={false}
225
+ nolqip
226
+ />
227
+ </div>
228
+ </div>
229
+ </button>
230
+ <div
231
+ class="alt-tooltip"
232
+ style="z-index: 3; height: 2rem; text-overflow: elipsis; top: calc({top}px + 1rem); color: white; padding: 0.5rem; position: absolute; left: -9rem; width: 8rem; display: flex; justify-content: end; align-items: center;"
233
+ >
234
+ <div
235
+ style="background-color: #0009; border-radius: 0.25rem; padding: 0 0.25rem"
236
+ >
237
+ {#if isOriginal}
238
+ Back to Original Piece
239
+ {:else}
240
+ Alt: {alt.name}
241
+ {/if}
242
+ </div>
243
+ </div>
244
+ </div>
245
+ {/each}
246
+ </div>
247
+ {/if}
178
248
  </div>
179
- {#if isComic && piece.alts}
249
+ {#if isComic && piece.alts && piece.alts.length > 0}
180
250
  {#each piece.alts as alt, i (JSON.stringify(alt))}
181
251
  <div
182
252
  class="bounded-div"
@@ -287,4 +357,30 @@
287
357
  left: 0;
288
358
  right: 0;
289
359
  }
360
+
361
+ .objr :global(img) {
362
+ object-position: right center;
363
+ object-fit: cover;
364
+ }
365
+
366
+ .altcarry {
367
+ right: 1rem;
368
+ transition:
369
+ right 0.2s ease-in-out,
370
+ clip-path 0.2s ease-in-out;
371
+ clip-path: inset(0 0 0 16px);
372
+ }
373
+ .altcarry:hover {
374
+ right: 0rem;
375
+ clip-path: inset(0 0 0 0);
376
+ }
377
+
378
+ .altcarry:hover ~ .alt-tooltip {
379
+ opacity: 1;
380
+ }
381
+ .alt-tooltip {
382
+ opacity: 0;
383
+ user-select: none;
384
+ transition: opacity 0.2s ease-in-out;
385
+ }
290
386
  </style>
@@ -14,17 +14,16 @@
14
14
  display: Snippet<
15
15
  [image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
16
16
  >;
17
+ selectedAlt?: number | null;
17
18
  }
18
19
 
19
- let { piece, nameInHeader, display }: Props = $props();
20
+ let { piece, nameInHeader, display, selectedAlt = $bindable(null) }: Props = $props();
20
21
 
21
22
  let config = useLibraryConfig();
22
23
 
23
24
  let loading = $state(true);
24
25
  let showingDescription = $state(false);
25
26
 
26
- let selectedAlt: number | null = $state(null);
27
-
28
27
  let image = $derived(typeof selectedAlt === 'number' ? piece.alts![selectedAlt] : piece);
29
28
 
30
29
  run(() => {