@phosart/common 0.4.41 → 0.4.42
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/ModalGallery/ImageSection.svelte +102 -35
- package/dist/ModalGallery/ImageSection.svelte.d.ts.map +1 -1
- package/dist/ModalGallery/ImageView.svelte +6 -19
- package/dist/ModalGallery/ImageView.svelte.d.ts +5 -0
- package/dist/ModalGallery/ImageView.svelte.d.ts.map +1 -1
- package/dist/ModalGallery.svelte +23 -9
- package/dist/ModalGallery.svelte.d.ts.map +1 -1
- package/dist/server/theme/schema.d.ts +0 -3
- package/dist/server/theme/schema.d.ts.map +1 -1
- package/dist/server/theme/schema.js +1 -2
- package/package.json +1 -1
- package/src/lib/ModalGallery/ImageSection.svelte +102 -35
- package/src/lib/ModalGallery/ImageView.svelte +6 -19
- package/src/lib/ModalGallery.svelte +23 -9
- package/src/lib/server/theme/schema.ts +1 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
3
3
|
|
|
4
|
-
import type { ArtPiece } from '../util/art.js';
|
|
4
|
+
import type { ArtPiece, Picture } from '../util/art.js';
|
|
5
5
|
import ImageView from './ImageView.svelte';
|
|
6
6
|
import { useLibraryConfig } from '../util/phosart_config.svelte.js';
|
|
7
|
+
import Image from '../Image.svelte';
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
piece: ArtPiece;
|
|
@@ -22,22 +23,41 @@
|
|
|
22
23
|
onprev();
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
let infoHeight = $derived(
|
|
27
|
+
parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
|
|
28
|
+
);
|
|
29
|
+
|
|
25
30
|
let containerWidth: number = $state(0);
|
|
26
31
|
let containerHeight: number = $state(0);
|
|
27
|
-
let containerHeightLessInfo = $derived(Math.max(0, containerHeight -
|
|
32
|
+
let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
|
|
33
|
+
let isComic = $derived(piece.alts_display === 'comic_panels');
|
|
34
|
+
let bounded: HTMLDivElement | null = $state(null);
|
|
35
|
+
|
|
36
|
+
function scale(image: Picture) {
|
|
37
|
+
const scaleByHeight =
|
|
38
|
+
!isComic &&
|
|
39
|
+
containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
|
|
40
|
+
const scalingFactor: number = scaleByHeight
|
|
41
|
+
? containerHeightLessInfo / image.full.fallback.h
|
|
42
|
+
: containerWidth / image.full.fallback.w;
|
|
43
|
+
|
|
44
|
+
return scalingFactor;
|
|
45
|
+
}
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
: containerWidth / piece.image.full.fallback.w
|
|
37
|
-
);
|
|
47
|
+
function width(image: Picture) {
|
|
48
|
+
return image.full.fallback.w * scale(image);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function height(image: Picture) {
|
|
52
|
+
return image.full.fallback.h * scale(image);
|
|
53
|
+
}
|
|
38
54
|
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
function scrollDown() {
|
|
56
|
+
bounded?.scrollBy({ behavior: 'smooth', top: containerHeight });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let w = $derived(width(piece.image));
|
|
60
|
+
let h = $derived(height(piece.image) + 50);
|
|
41
61
|
|
|
42
62
|
let config = useLibraryConfig();
|
|
43
63
|
|
|
@@ -56,17 +76,65 @@
|
|
|
56
76
|
></div>
|
|
57
77
|
</div>
|
|
58
78
|
|
|
59
|
-
<div class="main-container">
|
|
60
|
-
<div
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
<div class="main-container" style={isComic ? 'overflow: visible' : ''}>
|
|
80
|
+
<div
|
|
81
|
+
class="bounding-div"
|
|
82
|
+
style={isComic ? 'overflow-y: scroll; align-items: flex-start; z-index: 100' : ''}
|
|
83
|
+
bind:clientHeight={containerHeight}
|
|
84
|
+
bind:clientWidth={containerWidth}
|
|
85
|
+
bind:this={bounded}
|
|
86
|
+
>
|
|
87
|
+
<div class="flex flex-col">
|
|
88
|
+
<div
|
|
89
|
+
class="bounded-div"
|
|
90
|
+
onclick={(e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
scrollDown();
|
|
93
|
+
}}
|
|
94
|
+
onkeypress={(e) => {
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
scrollDown();
|
|
97
|
+
}}
|
|
98
|
+
role="button"
|
|
99
|
+
tabindex={-1}
|
|
100
|
+
style="width: {w}px; height: {h}px"
|
|
101
|
+
>
|
|
102
|
+
<ImageView {piece} {nameInHeader}>
|
|
103
|
+
{#snippet display(image, onloaded)}
|
|
104
|
+
<div class="image-container">
|
|
105
|
+
<Image
|
|
106
|
+
video={image.video?.full}
|
|
107
|
+
controls
|
|
108
|
+
picture={image.image.full}
|
|
109
|
+
alt={image.alt}
|
|
110
|
+
{onloaded}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
{/snippet}
|
|
114
|
+
</ImageView>
|
|
115
|
+
</div>
|
|
116
|
+
{#if isComic && piece.alts}
|
|
117
|
+
{#each piece.alts as alt (JSON.stringify(alt))}
|
|
118
|
+
<div
|
|
119
|
+
class="bounded-div"
|
|
120
|
+
onclick={(e) => {
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
scrollDown();
|
|
123
|
+
}}
|
|
124
|
+
onkeypress={(e) => {
|
|
125
|
+
e.stopPropagation();
|
|
126
|
+
scrollDown();
|
|
127
|
+
}}
|
|
128
|
+
role="button"
|
|
129
|
+
tabindex={-1}
|
|
130
|
+
style="width: {width(alt.image)}px; height: {height(alt.image)}px"
|
|
131
|
+
>
|
|
132
|
+
<div class="image-container">
|
|
133
|
+
<Image video={alt.video?.full} controls picture={alt.image.full} alt={alt.alt} />
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
{/each}
|
|
137
|
+
{/if}
|
|
70
138
|
</div>
|
|
71
139
|
</div>
|
|
72
140
|
</div>
|
|
@@ -84,16 +152,7 @@
|
|
|
84
152
|
</div>
|
|
85
153
|
|
|
86
154
|
<style>
|
|
87
|
-
:global(:root) {
|
|
88
|
-
--info-height: 50px;
|
|
89
|
-
--carousel-height: 100px;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
155
|
@media only screen and (max-width: 800px) {
|
|
93
|
-
:global(:root) {
|
|
94
|
-
--info-height: 50px;
|
|
95
|
-
--carousel-height: 75px;
|
|
96
|
-
}
|
|
97
156
|
.nav-container.nav-container {
|
|
98
157
|
width: 0;
|
|
99
158
|
}
|
|
@@ -105,10 +164,10 @@
|
|
|
105
164
|
justify-content: center;
|
|
106
165
|
align-items: center;
|
|
107
166
|
overflow: visible;
|
|
108
|
-
max-height: calc(100% -
|
|
167
|
+
max-height: calc(100% - var(--carousel-height));
|
|
109
168
|
position: relative;
|
|
110
169
|
height: 100%;
|
|
111
|
-
padding:
|
|
170
|
+
padding: 1rem;
|
|
112
171
|
}
|
|
113
172
|
.nav-container {
|
|
114
173
|
color: white;
|
|
@@ -156,4 +215,12 @@
|
|
|
156
215
|
display: flex;
|
|
157
216
|
position: relative;
|
|
158
217
|
}
|
|
218
|
+
|
|
219
|
+
.image-container {
|
|
220
|
+
position: absolute;
|
|
221
|
+
top: var(--info-height);
|
|
222
|
+
bottom: 0;
|
|
223
|
+
left: 0;
|
|
224
|
+
right: 0;
|
|
225
|
+
}
|
|
159
226
|
</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,
|
|
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;AAiHF,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { run } from 'svelte/legacy';
|
|
3
3
|
|
|
4
|
-
import Image from '../Image.svelte';
|
|
5
|
-
|
|
6
4
|
import Spinner from './Spinner.svelte';
|
|
7
5
|
import Headline from '../Postcard/Headline.svelte';
|
|
8
6
|
import Description from '../Postcard/Description.svelte';
|
|
9
7
|
import type { ArtPiece } from '../util/art.js';
|
|
10
8
|
import { useLibraryConfig } from '../util/phosart_config.svelte.js';
|
|
9
|
+
import type { Snippet } from 'svelte';
|
|
11
10
|
|
|
12
11
|
interface Props {
|
|
13
12
|
piece: ArtPiece;
|
|
14
13
|
nameInHeader: boolean;
|
|
14
|
+
display: Snippet<
|
|
15
|
+
[image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
|
|
16
|
+
>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
let { piece, nameInHeader }: Props = $props();
|
|
19
|
+
let { piece, nameInHeader, display }: Props = $props();
|
|
18
20
|
|
|
19
21
|
let config = useLibraryConfig();
|
|
20
22
|
|
|
@@ -32,15 +34,7 @@
|
|
|
32
34
|
|
|
33
35
|
<Spinner {loading} />
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
<Image
|
|
37
|
-
video={image.video?.full}
|
|
38
|
-
controls
|
|
39
|
-
picture={image.image.full}
|
|
40
|
-
alt={image.alt}
|
|
41
|
-
onloaded={() => (loading = false)}
|
|
42
|
-
/>
|
|
43
|
-
</div>
|
|
37
|
+
{@render display(image, () => (loading = false))}
|
|
44
38
|
|
|
45
39
|
<div class="headline-container">
|
|
46
40
|
<Headline {piece} bind:showingDescription showName={!config.modal?.hideNames && nameInHeader} />
|
|
@@ -56,13 +50,6 @@
|
|
|
56
50
|
</div>
|
|
57
51
|
|
|
58
52
|
<style>
|
|
59
|
-
.image-container {
|
|
60
|
-
position: absolute;
|
|
61
|
-
top: var(--info-height);
|
|
62
|
-
bottom: 0;
|
|
63
|
-
left: 0;
|
|
64
|
-
right: 0;
|
|
65
|
-
}
|
|
66
53
|
.description-container {
|
|
67
54
|
position: absolute;
|
|
68
55
|
top: var(--info-height);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { ArtPiece } from '../util/art.ts';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
interface Props {
|
|
3
4
|
piece: ArtPiece;
|
|
4
5
|
nameInHeader: boolean;
|
|
6
|
+
display: Snippet<[
|
|
7
|
+
image: ArtPiece | NonNullable<ArtPiece['alts']>[number],
|
|
8
|
+
onloaded: () => void
|
|
9
|
+
]>;
|
|
5
10
|
}
|
|
6
11
|
declare const ImageView: import("svelte").Component<Props, {}, "">;
|
|
7
12
|
type ImageView = ReturnType<typeof ImageView>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageView.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/ModalGallery/ImageView.svelte.ts"],"names":[],"mappings":"
|
|
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;AA2CF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
package/dist/ModalGallery.svelte
CHANGED
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
|
|
46
46
|
const isGalleryOpen = $state(useIsModelGalleryOpen());
|
|
47
47
|
|
|
48
|
+
const currentIsComic = $derived(
|
|
49
|
+
typeof selected === 'number' ? pieces[selected]?.alts_display === 'comic_panels' : false
|
|
50
|
+
);
|
|
51
|
+
|
|
48
52
|
onMount(() => {
|
|
49
53
|
const handler = (e: KeyboardEvent) => {
|
|
50
54
|
if (e.code === 'ArrowLeft' && selected !== null) {
|
|
@@ -57,16 +61,15 @@
|
|
|
57
61
|
const hashchange = (hce: { oldURL?: string; newURL: string }) => {
|
|
58
62
|
const hash = new URL(hce.newURL).hash.replace(/^#/, '');
|
|
59
63
|
if (browser) {
|
|
60
|
-
|
|
61
|
-
const foundSelected = pieces.findIndex((piece) => piece.slug === hash);
|
|
64
|
+
const foundSelected = pieces.findIndex((piece) => piece.slug === decodeURIComponent(hash));
|
|
62
65
|
if (!isAnyModelGalleryOpen()) {
|
|
63
66
|
if (foundSelected !== -1) {
|
|
64
67
|
selected = foundSelected;
|
|
65
68
|
}
|
|
66
69
|
} else if (isGalleryOpen.open) {
|
|
67
|
-
if (!hash || hash == '#') {
|
|
70
|
+
if (!hash || hash == '#' || foundSelected === -1) {
|
|
68
71
|
selected = null;
|
|
69
|
-
} else
|
|
72
|
+
} else {
|
|
70
73
|
selected = foundSelected;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -90,13 +93,12 @@
|
|
|
90
93
|
if (isAnyModelGalleryOpen() === false && window.location.hash) {
|
|
91
94
|
window.location.hash = '##';
|
|
92
95
|
} else if (selected !== null && pieces[selected]) {
|
|
93
|
-
console.log('H <-', pieces[selected].slug);
|
|
94
96
|
window.location.hash = '#' + encodeURIComponent(pieces[selected].slug);
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
$effect.pre(() => {
|
|
99
|
-
isGalleryOpen.open = selected !== null;
|
|
101
|
+
isGalleryOpen.open = selected !== null && !!pieces[selected];
|
|
100
102
|
});
|
|
101
103
|
$effect.pre(() => {
|
|
102
104
|
if (hashUpdateReady) {
|
|
@@ -105,8 +107,8 @@
|
|
|
105
107
|
});
|
|
106
108
|
</script>
|
|
107
109
|
|
|
108
|
-
<Modal open={selected !== null} onclose={() => (selected = null)}>
|
|
109
|
-
<div class="gallery-container">
|
|
110
|
+
<Modal open={selected !== null && !!pieces[selected]} onclose={() => (selected = null)}>
|
|
111
|
+
<div class="gallery-container" style={currentIsComic ? '--carousel-height: 25px' : ''}>
|
|
110
112
|
<div
|
|
111
113
|
role="button"
|
|
112
114
|
tabindex={-1}
|
|
@@ -121,7 +123,7 @@
|
|
|
121
123
|
></div>
|
|
122
124
|
<div style="flex-grow: 1"></div>
|
|
123
125
|
|
|
124
|
-
{#if selected !== null}
|
|
126
|
+
{#if selected !== null && !!pieces[selected]}
|
|
125
127
|
<HighResContext>
|
|
126
128
|
<ImageSection
|
|
127
129
|
piece={pieces[selected]}
|
|
@@ -139,6 +141,18 @@
|
|
|
139
141
|
</Modal>
|
|
140
142
|
|
|
141
143
|
<style>
|
|
144
|
+
@media only screen and (max-width: 800px) {
|
|
145
|
+
:global(:root) {
|
|
146
|
+
--info-height: 50px;
|
|
147
|
+
--carousel-height: 75px;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:global(:root) {
|
|
152
|
+
--info-height: 50px;
|
|
153
|
+
--carousel-height: 50px;
|
|
154
|
+
}
|
|
155
|
+
|
|
142
156
|
.gallery-container {
|
|
143
157
|
display: flex;
|
|
144
158
|
flex-direction: column;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalGallery.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ModalGallery.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,+CAA+C,CAAC;AAIvD,wBAAgB,qBAAqB;UAIlB,OAAO;EAOzB;AAED,wBAAgB,qBAAqB,YAEpC;AAGF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQ7C,UAAU,KAAK;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"ModalGallery.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ModalGallery.svelte.ts"],"names":[],"mappings":"AAGC,OAAO,+CAA+C,CAAC;AAIvD,wBAAgB,qBAAqB;UAIlB,OAAO;EAOzB;AAED,wBAAgB,qBAAqB,YAEpC;AAGF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAQ7C,UAAU,KAAK;IACd,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CACjB;AA8GF,QAAA,MAAM,YAAY,uEAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -52,9 +52,6 @@ export declare const builtinSettings: {
|
|
|
52
52
|
readonly type: "selection";
|
|
53
53
|
readonly options: ["static", "vercel"];
|
|
54
54
|
};
|
|
55
|
-
readonly supportsComics: {
|
|
56
|
-
readonly type: "boolean";
|
|
57
|
-
};
|
|
58
55
|
};
|
|
59
56
|
export type BuiltinSettings = typeof builtinSettings;
|
|
60
57
|
type MaterializedOptionFor<T extends ThemeSettingsSchema[string]> = T extends z.infer<typeof ZColorOption> ? `#${string}` : T extends z.infer<typeof ZSelectionOption> ? [...T['options']] : T extends z.infer<typeof ZMultiSelectionOption> ? Array<[...T['options']]> : T extends z.infer<typeof ZStringList> ? Array<string> : T extends z.infer<typeof ZTagsOption> ? Array<string> : T extends z.infer<typeof ZStringOption> ? string : T extends z.infer<typeof ZBoolOption> ? boolean | undefined : never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/server/theme/schema.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,CAAC;AAcpB,QAAA,MAAM,YAAY;;mBAAyC,CAAC;AAC5D,QAAA,MAAM,gBAAgB;;;;mBAIpB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;mBAIzB,CAAC;AACH,QAAA,MAAM,aAAa;;mBAA0C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA2C,CAAC;AAC7D,QAAA,MAAM,WAAW;;mBAA4C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA+C,CAAC;AACjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;sBAWhC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/server/theme/schema.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,CAAC;AAcpB,QAAA,MAAM,YAAY;;mBAAyC,CAAC;AAC5D,QAAA,MAAM,gBAAgB;;;;mBAIpB,CAAC;AACH,QAAA,MAAM,qBAAqB;;;;mBAIzB,CAAC;AACH,QAAA,MAAM,aAAa;;mBAA0C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA2C,CAAC;AAC7D,QAAA,MAAM,WAAW;;mBAA4C,CAAC;AAC9D,QAAA,MAAM,WAAW;;mBAA+C,CAAC;AACjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;sBAWhC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,eAAO,MAAM,eAAe;;;;;;;;CAGY,CAAC;AACzC,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC;AAErD,KAAK,qBAAqB,CAAC,CAAC,SAAS,mBAAmB,CAAC,MAAM,CAAC,IAC/D,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,GACnC,IAAI,MAAM,EAAE,GACZ,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,GACzC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,GACjB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC9C,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GACxB,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,KAAK,CAAC,MAAM,CAAC,GACb,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GACtC,MAAM,GACN,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GACpC,OAAO,GAAG,SAAS,GACnB,KAAK,CAAC;AAEhB,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,mBAAmB,IAAI;KACvD,CAAC,IAAI,MAAM,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,CAqBjF;AAyCD,wBAAgB,cAAc,CAAC,CAAC,SAAS,mBAAmB,EAC3D,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,OAAO,GACV,GAAG,IAAI,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAEzC;AAoGD,wBAAsB,eAAe,CAAC,CAAC,SAAS,mBAAmB,EAClE,MAAM,EAAE,CAAC,GACP,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAoB3C;AAED,wBAAsB,gBAAgB,CAAC,CAAC,SAAS,mBAAmB,EACnE,MAAM,EAAE,CAAC,EACT,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,eAAe,CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -39,8 +39,7 @@ export const ZThemeSettingsSchema = z.record(z.string(), z.union([
|
|
|
39
39
|
]));
|
|
40
40
|
export const builtinSettings = {
|
|
41
41
|
defaultArtist: { type: 'string' },
|
|
42
|
-
websiteMode: { type: 'selection', options: ['static', 'vercel'] }
|
|
43
|
-
supportsComics: { type: 'boolean' }
|
|
42
|
+
websiteMode: { type: 'selection', options: ['static', 'vercel'] }
|
|
44
43
|
};
|
|
45
44
|
export async function readThemeSchema() {
|
|
46
45
|
let text;
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
3
3
|
|
|
4
|
-
import type { ArtPiece } from '../util/art.ts';
|
|
4
|
+
import type { ArtPiece, Picture } from '../util/art.ts';
|
|
5
5
|
import ImageView from './ImageView.svelte';
|
|
6
6
|
import { useLibraryConfig } from '../util/phosart_config.svelte.ts';
|
|
7
|
+
import Image from '$lib/Image.svelte';
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
piece: ArtPiece;
|
|
@@ -22,22 +23,41 @@
|
|
|
22
23
|
onprev();
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
let infoHeight = $derived(
|
|
27
|
+
parseInt(getComputedStyle(document.documentElement).getPropertyValue('--info-height') ?? '100')
|
|
28
|
+
);
|
|
29
|
+
|
|
25
30
|
let containerWidth: number = $state(0);
|
|
26
31
|
let containerHeight: number = $state(0);
|
|
27
|
-
let containerHeightLessInfo = $derived(Math.max(0, containerHeight -
|
|
32
|
+
let containerHeightLessInfo = $derived(Math.max(0, containerHeight - infoHeight));
|
|
33
|
+
let isComic = $derived(piece.alts_display === 'comic_panels');
|
|
34
|
+
let bounded: HTMLDivElement | null = $state(null);
|
|
35
|
+
|
|
36
|
+
function scale(image: Picture) {
|
|
37
|
+
const scaleByHeight =
|
|
38
|
+
!isComic &&
|
|
39
|
+
containerWidth / containerHeightLessInfo > image.full.fallback.w / image.full.fallback.h;
|
|
40
|
+
const scalingFactor: number = scaleByHeight
|
|
41
|
+
? containerHeightLessInfo / image.full.fallback.h
|
|
42
|
+
: containerWidth / image.full.fallback.w;
|
|
43
|
+
|
|
44
|
+
return scalingFactor;
|
|
45
|
+
}
|
|
28
46
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
: containerWidth / piece.image.full.fallback.w
|
|
37
|
-
);
|
|
47
|
+
function width(image: Picture) {
|
|
48
|
+
return image.full.fallback.w * scale(image);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function height(image: Picture) {
|
|
52
|
+
return image.full.fallback.h * scale(image);
|
|
53
|
+
}
|
|
38
54
|
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
function scrollDown() {
|
|
56
|
+
bounded?.scrollBy({ behavior: 'smooth', top: containerHeight });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let w = $derived(width(piece.image));
|
|
60
|
+
let h = $derived(height(piece.image) + 50);
|
|
41
61
|
|
|
42
62
|
let config = useLibraryConfig();
|
|
43
63
|
|
|
@@ -56,17 +76,65 @@
|
|
|
56
76
|
></div>
|
|
57
77
|
</div>
|
|
58
78
|
|
|
59
|
-
<div class="main-container">
|
|
60
|
-
<div
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
<div class="main-container" style={isComic ? 'overflow: visible' : ''}>
|
|
80
|
+
<div
|
|
81
|
+
class="bounding-div"
|
|
82
|
+
style={isComic ? 'overflow-y: scroll; align-items: flex-start; z-index: 100' : ''}
|
|
83
|
+
bind:clientHeight={containerHeight}
|
|
84
|
+
bind:clientWidth={containerWidth}
|
|
85
|
+
bind:this={bounded}
|
|
86
|
+
>
|
|
87
|
+
<div class="flex flex-col">
|
|
88
|
+
<div
|
|
89
|
+
class="bounded-div"
|
|
90
|
+
onclick={(e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
scrollDown();
|
|
93
|
+
}}
|
|
94
|
+
onkeypress={(e) => {
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
scrollDown();
|
|
97
|
+
}}
|
|
98
|
+
role="button"
|
|
99
|
+
tabindex={-1}
|
|
100
|
+
style="width: {w}px; height: {h}px"
|
|
101
|
+
>
|
|
102
|
+
<ImageView {piece} {nameInHeader}>
|
|
103
|
+
{#snippet display(image, onloaded)}
|
|
104
|
+
<div class="image-container">
|
|
105
|
+
<Image
|
|
106
|
+
video={image.video?.full}
|
|
107
|
+
controls
|
|
108
|
+
picture={image.image.full}
|
|
109
|
+
alt={image.alt}
|
|
110
|
+
{onloaded}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
{/snippet}
|
|
114
|
+
</ImageView>
|
|
115
|
+
</div>
|
|
116
|
+
{#if isComic && piece.alts}
|
|
117
|
+
{#each piece.alts as alt (JSON.stringify(alt))}
|
|
118
|
+
<div
|
|
119
|
+
class="bounded-div"
|
|
120
|
+
onclick={(e) => {
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
scrollDown();
|
|
123
|
+
}}
|
|
124
|
+
onkeypress={(e) => {
|
|
125
|
+
e.stopPropagation();
|
|
126
|
+
scrollDown();
|
|
127
|
+
}}
|
|
128
|
+
role="button"
|
|
129
|
+
tabindex={-1}
|
|
130
|
+
style="width: {width(alt.image)}px; height: {height(alt.image)}px"
|
|
131
|
+
>
|
|
132
|
+
<div class="image-container">
|
|
133
|
+
<Image video={alt.video?.full} controls picture={alt.image.full} alt={alt.alt} />
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
{/each}
|
|
137
|
+
{/if}
|
|
70
138
|
</div>
|
|
71
139
|
</div>
|
|
72
140
|
</div>
|
|
@@ -84,16 +152,7 @@
|
|
|
84
152
|
</div>
|
|
85
153
|
|
|
86
154
|
<style>
|
|
87
|
-
:global(:root) {
|
|
88
|
-
--info-height: 50px;
|
|
89
|
-
--carousel-height: 100px;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
155
|
@media only screen and (max-width: 800px) {
|
|
93
|
-
:global(:root) {
|
|
94
|
-
--info-height: 50px;
|
|
95
|
-
--carousel-height: 75px;
|
|
96
|
-
}
|
|
97
156
|
.nav-container.nav-container {
|
|
98
157
|
width: 0;
|
|
99
158
|
}
|
|
@@ -105,10 +164,10 @@
|
|
|
105
164
|
justify-content: center;
|
|
106
165
|
align-items: center;
|
|
107
166
|
overflow: visible;
|
|
108
|
-
max-height: calc(100% -
|
|
167
|
+
max-height: calc(100% - var(--carousel-height));
|
|
109
168
|
position: relative;
|
|
110
169
|
height: 100%;
|
|
111
|
-
padding:
|
|
170
|
+
padding: 1rem;
|
|
112
171
|
}
|
|
113
172
|
.nav-container {
|
|
114
173
|
color: white;
|
|
@@ -156,4 +215,12 @@
|
|
|
156
215
|
display: flex;
|
|
157
216
|
position: relative;
|
|
158
217
|
}
|
|
218
|
+
|
|
219
|
+
.image-container {
|
|
220
|
+
position: absolute;
|
|
221
|
+
top: var(--info-height);
|
|
222
|
+
bottom: 0;
|
|
223
|
+
left: 0;
|
|
224
|
+
right: 0;
|
|
225
|
+
}
|
|
159
226
|
</style>
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { run } from 'svelte/legacy';
|
|
3
3
|
|
|
4
|
-
import Image from '../Image.svelte';
|
|
5
|
-
|
|
6
4
|
import Spinner from './Spinner.svelte';
|
|
7
5
|
import Headline from '../Postcard/Headline.svelte';
|
|
8
6
|
import Description from '../Postcard/Description.svelte';
|
|
9
7
|
import type { ArtPiece } from '../util/art.ts';
|
|
10
8
|
import { useLibraryConfig } from '../util/phosart_config.svelte.ts';
|
|
9
|
+
import type { Snippet } from 'svelte';
|
|
11
10
|
|
|
12
11
|
interface Props {
|
|
13
12
|
piece: ArtPiece;
|
|
14
13
|
nameInHeader: boolean;
|
|
14
|
+
display: Snippet<
|
|
15
|
+
[image: ArtPiece | NonNullable<ArtPiece['alts']>[number], onloaded: () => void]
|
|
16
|
+
>;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
let { piece, nameInHeader }: Props = $props();
|
|
19
|
+
let { piece, nameInHeader, display }: Props = $props();
|
|
18
20
|
|
|
19
21
|
let config = useLibraryConfig();
|
|
20
22
|
|
|
@@ -32,15 +34,7 @@
|
|
|
32
34
|
|
|
33
35
|
<Spinner {loading} />
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
<Image
|
|
37
|
-
video={image.video?.full}
|
|
38
|
-
controls
|
|
39
|
-
picture={image.image.full}
|
|
40
|
-
alt={image.alt}
|
|
41
|
-
onloaded={() => (loading = false)}
|
|
42
|
-
/>
|
|
43
|
-
</div>
|
|
37
|
+
{@render display(image, () => (loading = false))}
|
|
44
38
|
|
|
45
39
|
<div class="headline-container">
|
|
46
40
|
<Headline {piece} bind:showingDescription showName={!config.modal?.hideNames && nameInHeader} />
|
|
@@ -56,13 +50,6 @@
|
|
|
56
50
|
</div>
|
|
57
51
|
|
|
58
52
|
<style lang="postcss">
|
|
59
|
-
.image-container {
|
|
60
|
-
position: absolute;
|
|
61
|
-
top: var(--info-height);
|
|
62
|
-
bottom: 0;
|
|
63
|
-
left: 0;
|
|
64
|
-
right: 0;
|
|
65
|
-
}
|
|
66
53
|
.description-container {
|
|
67
54
|
position: absolute;
|
|
68
55
|
top: var(--info-height);
|
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
|
|
46
46
|
const isGalleryOpen = $state(useIsModelGalleryOpen());
|
|
47
47
|
|
|
48
|
+
const currentIsComic = $derived(
|
|
49
|
+
typeof selected === 'number' ? pieces[selected]?.alts_display === 'comic_panels' : false
|
|
50
|
+
);
|
|
51
|
+
|
|
48
52
|
onMount(() => {
|
|
49
53
|
const handler = (e: KeyboardEvent) => {
|
|
50
54
|
if (e.code === 'ArrowLeft' && selected !== null) {
|
|
@@ -57,16 +61,15 @@
|
|
|
57
61
|
const hashchange = (hce: { oldURL?: string; newURL: string }) => {
|
|
58
62
|
const hash = new URL(hce.newURL).hash.replace(/^#/, '');
|
|
59
63
|
if (browser) {
|
|
60
|
-
|
|
61
|
-
const foundSelected = pieces.findIndex((piece) => piece.slug === hash);
|
|
64
|
+
const foundSelected = pieces.findIndex((piece) => piece.slug === decodeURIComponent(hash));
|
|
62
65
|
if (!isAnyModelGalleryOpen()) {
|
|
63
66
|
if (foundSelected !== -1) {
|
|
64
67
|
selected = foundSelected;
|
|
65
68
|
}
|
|
66
69
|
} else if (isGalleryOpen.open) {
|
|
67
|
-
if (!hash || hash == '#') {
|
|
70
|
+
if (!hash || hash == '#' || foundSelected === -1) {
|
|
68
71
|
selected = null;
|
|
69
|
-
} else
|
|
72
|
+
} else {
|
|
70
73
|
selected = foundSelected;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -90,13 +93,12 @@
|
|
|
90
93
|
if (isAnyModelGalleryOpen() === false && window.location.hash) {
|
|
91
94
|
window.location.hash = '##';
|
|
92
95
|
} else if (selected !== null && pieces[selected]) {
|
|
93
|
-
console.log('H <-', pieces[selected].slug);
|
|
94
96
|
window.location.hash = '#' + encodeURIComponent(pieces[selected].slug);
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
$effect.pre(() => {
|
|
99
|
-
isGalleryOpen.open = selected !== null;
|
|
101
|
+
isGalleryOpen.open = selected !== null && !!pieces[selected];
|
|
100
102
|
});
|
|
101
103
|
$effect.pre(() => {
|
|
102
104
|
if (hashUpdateReady) {
|
|
@@ -105,8 +107,8 @@
|
|
|
105
107
|
});
|
|
106
108
|
</script>
|
|
107
109
|
|
|
108
|
-
<Modal open={selected !== null} onclose={() => (selected = null)}>
|
|
109
|
-
<div class="gallery-container">
|
|
110
|
+
<Modal open={selected !== null && !!pieces[selected]} onclose={() => (selected = null)}>
|
|
111
|
+
<div class="gallery-container" style={currentIsComic ? '--carousel-height: 25px' : ''}>
|
|
110
112
|
<div
|
|
111
113
|
role="button"
|
|
112
114
|
tabindex={-1}
|
|
@@ -121,7 +123,7 @@
|
|
|
121
123
|
></div>
|
|
122
124
|
<div style="flex-grow: 1"></div>
|
|
123
125
|
|
|
124
|
-
{#if selected !== null}
|
|
126
|
+
{#if selected !== null && !!pieces[selected]}
|
|
125
127
|
<HighResContext>
|
|
126
128
|
<ImageSection
|
|
127
129
|
piece={pieces[selected]}
|
|
@@ -139,6 +141,18 @@
|
|
|
139
141
|
</Modal>
|
|
140
142
|
|
|
141
143
|
<style>
|
|
144
|
+
@media only screen and (max-width: 800px) {
|
|
145
|
+
:global(:root) {
|
|
146
|
+
--info-height: 50px;
|
|
147
|
+
--carousel-height: 75px;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:global(:root) {
|
|
152
|
+
--info-height: 50px;
|
|
153
|
+
--carousel-height: 50px;
|
|
154
|
+
}
|
|
155
|
+
|
|
142
156
|
.gallery-container {
|
|
143
157
|
display: flex;
|
|
144
158
|
flex-direction: column;
|
|
@@ -47,8 +47,7 @@ export type ThemeSettingsSchema = z.infer<typeof ZThemeSettingsSchema>;
|
|
|
47
47
|
|
|
48
48
|
export const builtinSettings = {
|
|
49
49
|
defaultArtist: { type: 'string' },
|
|
50
|
-
websiteMode: { type: 'selection', options: ['static', 'vercel'] }
|
|
51
|
-
supportsComics: { type: 'boolean' }
|
|
50
|
+
websiteMode: { type: 'selection', options: ['static', 'vercel'] }
|
|
52
51
|
} as const satisfies ThemeSettingsSchema;
|
|
53
52
|
export type BuiltinSettings = typeof builtinSettings;
|
|
54
53
|
|