slidev-theme-practicum 0.1.0
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/README.md +270 -0
- package/components/DebugGrid.vue +50 -0
- package/components/DecorLibrary.vue +1117 -0
- package/components/Header.vue +124 -0
- package/components/Image.vue +82 -0
- package/components/ImageRenderer.vue +126 -0
- package/components/Logo.vue +142 -0
- package/components/Person.vue +89 -0
- package/components/Slide.vue +324 -0
- package/components/Slot.vue +419 -0
- package/components/Text.vue +216 -0
- package/components/Timeline.vue +202 -0
- package/composables/decor-catalog.mjs +339 -0
- package/composables/decor-config.mjs +488 -0
- package/composables/decor-file-store.ts +192 -0
- package/composables/decor-http-store.ts +25 -0
- package/composables/decor-store.ts +39 -0
- package/composables/decor-tuning-overrides.mjs +2 -0
- package/composables/decor-workbench.ts +799 -0
- package/composables/grid-placement.ts +396 -0
- package/composables/image-config.mjs +484 -0
- package/composables/layout-authoring.ts +372 -0
- package/composables/layout-markdown.mjs +56 -0
- package/composables/layout-recipes.ts +347 -0
- package/composables/layout-registry.ts +47 -0
- package/composables/layout-shorthands.ts +891 -0
- package/composables/layout-vnode.ts +43 -0
- package/composables/logo-mark.mjs +5 -0
- package/composables/slide-layout.ts +43 -0
- package/composables/slot-placement.ts +195 -0
- package/composables/slot-rules.ts +147 -0
- package/composables/text-fit-runtime.ts +577 -0
- package/composables/text-priority-fit.mjs +166 -0
- package/composables/text-typography.mjs +138 -0
- package/composables/theme-foundation.ts +192 -0
- package/composables/theme-media.mjs +202 -0
- package/composables/use-theme-config.ts +71 -0
- package/decor-library.md +10 -0
- package/env.d.ts +6 -0
- package/example.md +786 -0
- package/layouts/collection.vue +5 -0
- package/layouts/cover.vue +5 -0
- package/layouts/default.vue +5 -0
- package/layouts/explainer.vue +5 -0
- package/layouts/message.vue +5 -0
- package/layouts/none.vue +5 -0
- package/layouts/section.vue +5 -0
- package/layouts/two-cols.vue +11 -0
- package/package.json +84 -0
- package/public/decor/decor-1.svg +83 -0
- package/public/decor/decor-10.svg +40 -0
- package/public/decor/decor-11.svg +162 -0
- package/public/decor/decor-12.svg +83 -0
- package/public/decor/decor-13.svg +5 -0
- package/public/decor/decor-14.svg +7 -0
- package/public/decor/decor-15.svg +10 -0
- package/public/decor/decor-16.svg +9 -0
- package/public/decor/decor-17.png +0 -0
- package/public/decor/decor-18.png +0 -0
- package/public/decor/decor-19.svg +5 -0
- package/public/decor/decor-2.png +0 -0
- package/public/decor/decor-3.svg +9 -0
- package/public/decor/decor-4.svg +31 -0
- package/public/decor/decor-5.png +0 -0
- package/public/decor/decor-6.png +0 -0
- package/public/decor/decor-7.png +0 -0
- package/public/decor/decor-8.svg +21 -0
- package/public/decor/decor-9.svg +38 -0
- package/public/favicon.svg +11 -0
- package/public/photos/photo-1.png +0 -0
- package/public/photos/photo-10.png +0 -0
- package/public/photos/photo-2.png +0 -0
- package/public/photos/photo-3.png +0 -0
- package/public/photos/photo-4.png +0 -0
- package/public/photos/photo-5.png +0 -0
- package/public/photos/photo-6.png +0 -0
- package/public/photos/photo-7.png +0 -0
- package/public/photos/photo-8.png +0 -0
- package/public/photos/photo-9.png +0 -0
- package/setup/main.ts +3 -0
- package/setup/shiki.ts +10 -0
- package/setup/vite-plugins.ts +106 -0
- package/slidev-theme-practicum.png +0 -0
- package/slidev-theme-practicum.svg +102 -0
- package/styles/example.css +0 -0
- package/styles/index.css +192 -0
- package/styles/vars.css +119 -0
- package/types/slidev-client.d.ts +15 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {'cover' | 'contain' | 'fill' | 'none' | 'scale-down'} ImageFit
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{
|
|
7
|
+
* src: string
|
|
8
|
+
* fit?: ImageFit
|
|
9
|
+
* position?: string
|
|
10
|
+
* anchor?: string
|
|
11
|
+
* x?: number | string
|
|
12
|
+
* y?: number | string
|
|
13
|
+
* zoom?: number | string
|
|
14
|
+
* rotate?: number | string
|
|
15
|
+
* color?: string
|
|
16
|
+
* opacity?: number | string
|
|
17
|
+
* backgroundColor?: string
|
|
18
|
+
* 'background-color'?: string
|
|
19
|
+
* }} ImageDataInput
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @typedef {{
|
|
24
|
+
* src: string
|
|
25
|
+
* fit: ImageFit
|
|
26
|
+
* position: string
|
|
27
|
+
* anchor: string
|
|
28
|
+
* x: string
|
|
29
|
+
* y: string
|
|
30
|
+
* zoom: number
|
|
31
|
+
* rotate: string
|
|
32
|
+
* color: string
|
|
33
|
+
* opacity: number
|
|
34
|
+
* backgroundColor: string
|
|
35
|
+
* }} NormalizedImageData
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {{
|
|
40
|
+
* src: string
|
|
41
|
+
* alt?: string
|
|
42
|
+
* fit?: ImageFit
|
|
43
|
+
* position?: string
|
|
44
|
+
* anchor?: string
|
|
45
|
+
* x?: number | string
|
|
46
|
+
* y?: number | string
|
|
47
|
+
* zoom?: number | string
|
|
48
|
+
* rotate?: number | string
|
|
49
|
+
* color?: string
|
|
50
|
+
* opacity?: number | string
|
|
51
|
+
* backgroundColor?: string
|
|
52
|
+
* 'background-color'?: string
|
|
53
|
+
* }} ImageConfigInput
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {NormalizedImageData & { alt: string }} NormalizedImageConfig
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
const IMAGE_FITS = new Set(['cover', 'contain', 'fill', 'none', 'scale-down'])
|
|
61
|
+
const IMAGE_MASK_SIZE_BY_FIT = Object.freeze({
|
|
62
|
+
'cover': 'cover',
|
|
63
|
+
'contain': 'contain',
|
|
64
|
+
'fill': '100% 100%',
|
|
65
|
+
'none': 'auto',
|
|
66
|
+
'scale-down': 'contain',
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const DEFAULT_IMAGE_CONFIG = Object.freeze({
|
|
70
|
+
alt: '',
|
|
71
|
+
fit: 'cover',
|
|
72
|
+
position: 'center',
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const DEFAULT_IMAGE_DATA = Object.freeze({
|
|
76
|
+
fit: 'cover',
|
|
77
|
+
position: 'center',
|
|
78
|
+
anchor: '',
|
|
79
|
+
x: 0,
|
|
80
|
+
y: 0,
|
|
81
|
+
zoom: 1,
|
|
82
|
+
rotate: 0,
|
|
83
|
+
color: '',
|
|
84
|
+
opacity: 1,
|
|
85
|
+
backgroundColor: '',
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
export const IMAGE_DATA_FIELDS = Object.freeze([
|
|
89
|
+
'fit',
|
|
90
|
+
'position',
|
|
91
|
+
'anchor',
|
|
92
|
+
'x',
|
|
93
|
+
'y',
|
|
94
|
+
'zoom',
|
|
95
|
+
'rotate',
|
|
96
|
+
'color',
|
|
97
|
+
'opacity',
|
|
98
|
+
'backgroundColor',
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
export const IMAGE_SOURCE_FIELDS = Object.freeze([
|
|
102
|
+
'src',
|
|
103
|
+
...IMAGE_DATA_FIELDS,
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {unknown} value
|
|
108
|
+
* @returns {value is Record<string, unknown>}
|
|
109
|
+
*/
|
|
110
|
+
function isPlainObject(value) {
|
|
111
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {unknown} input
|
|
116
|
+
* @returns {Partial<ImageDataInput>}
|
|
117
|
+
*/
|
|
118
|
+
export function pickImageDataFields(input) {
|
|
119
|
+
if (!isPlainObject(input))
|
|
120
|
+
return {}
|
|
121
|
+
|
|
122
|
+
const fields = new Set(IMAGE_DATA_FIELDS)
|
|
123
|
+
return Object.fromEntries(
|
|
124
|
+
Object.entries(input)
|
|
125
|
+
.flatMap(([key, value]) => {
|
|
126
|
+
if (value === undefined)
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
if (fields.has(key))
|
|
130
|
+
return [[key, value]]
|
|
131
|
+
|
|
132
|
+
if (key === 'background-color')
|
|
133
|
+
return [['backgroundColor', value]]
|
|
134
|
+
|
|
135
|
+
return []
|
|
136
|
+
}),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {unknown} value
|
|
142
|
+
* @returns {value is ImageFit}
|
|
143
|
+
*/
|
|
144
|
+
export function isImageFit(value) {
|
|
145
|
+
return typeof value === 'string' && IMAGE_FITS.has(value)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {unknown} value
|
|
150
|
+
* @returns {string}
|
|
151
|
+
*/
|
|
152
|
+
export function normalizeImagePosition(value) {
|
|
153
|
+
if (typeof value !== 'string')
|
|
154
|
+
return DEFAULT_IMAGE_CONFIG.position
|
|
155
|
+
|
|
156
|
+
const normalized = value.trim().replace(/\s+/g, ' ')
|
|
157
|
+
|
|
158
|
+
return normalized || DEFAULT_IMAGE_CONFIG.position
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} position
|
|
163
|
+
* @returns {string}
|
|
164
|
+
*/
|
|
165
|
+
export function normalizeImageTransformOrigin(position) {
|
|
166
|
+
const normalized = normalizeImagePosition(position)
|
|
167
|
+
const tokens = normalized.split(' ')
|
|
168
|
+
|
|
169
|
+
if (tokens.length === 1) {
|
|
170
|
+
if (tokens[0] === 'top' || tokens[0] === 'bottom')
|
|
171
|
+
return `center ${tokens[0]}`
|
|
172
|
+
|
|
173
|
+
return `${tokens[0]} center`
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (tokens.length === 2)
|
|
177
|
+
return normalized
|
|
178
|
+
|
|
179
|
+
return 'center'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* @param {unknown} value
|
|
184
|
+
* @returns {boolean}
|
|
185
|
+
*/
|
|
186
|
+
export function isSvgImageSource(value) {
|
|
187
|
+
if (typeof value !== 'string')
|
|
188
|
+
return false
|
|
189
|
+
|
|
190
|
+
const normalized = value.trim().split(/[?#]/u, 1)[0]?.toLowerCase() ?? ''
|
|
191
|
+
return normalized.endsWith('.svg')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {unknown} value
|
|
196
|
+
* @param {string} label
|
|
197
|
+
* @returns {ImageFit}
|
|
198
|
+
*/
|
|
199
|
+
function normalizeImageFit(value, label) {
|
|
200
|
+
const normalized = typeof value === 'string' && value.trim()
|
|
201
|
+
? value.trim()
|
|
202
|
+
: DEFAULT_IMAGE_CONFIG.fit
|
|
203
|
+
|
|
204
|
+
if (!isImageFit(normalized))
|
|
205
|
+
throw new Error(`[${label}] image fit "${String(value)}" is not supported.`)
|
|
206
|
+
|
|
207
|
+
return normalized
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param {unknown} value
|
|
212
|
+
* @returns {string}
|
|
213
|
+
*/
|
|
214
|
+
export function resolveImageMaskSize(value) {
|
|
215
|
+
const fit = normalizeImageFit(value, 'image.fit')
|
|
216
|
+
return IMAGE_MASK_SIZE_BY_FIT[fit]
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {unknown} value
|
|
221
|
+
* @param {string} label
|
|
222
|
+
*/
|
|
223
|
+
function normalizeImageZoom(value, label) {
|
|
224
|
+
if (value === undefined || value === null || value === '')
|
|
225
|
+
return DEFAULT_IMAGE_DATA.zoom
|
|
226
|
+
|
|
227
|
+
const normalized = Number(value)
|
|
228
|
+
|
|
229
|
+
if (!Number.isFinite(normalized) || normalized <= 0)
|
|
230
|
+
throw new Error(`[${label}] image zoom must be a positive number.`)
|
|
231
|
+
|
|
232
|
+
return normalized
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* @param {unknown} value
|
|
237
|
+
* @param {string} label
|
|
238
|
+
*/
|
|
239
|
+
function normalizeImageRotate(value, label) {
|
|
240
|
+
if (value === undefined || value === null || value === '')
|
|
241
|
+
return '0deg'
|
|
242
|
+
|
|
243
|
+
if (typeof value === 'number') {
|
|
244
|
+
if (!Number.isFinite(value))
|
|
245
|
+
throw new Error(`[${label}] image rotate must be a finite number or CSS angle.`)
|
|
246
|
+
|
|
247
|
+
return `${value}deg`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (typeof value !== 'string')
|
|
251
|
+
throw new Error(`[${label}] image rotate must be a finite number or CSS angle.`)
|
|
252
|
+
|
|
253
|
+
const normalized = value.trim()
|
|
254
|
+
if (!normalized)
|
|
255
|
+
return '0deg'
|
|
256
|
+
|
|
257
|
+
return /^[-+]?(?:\d+|\d*\.\d+)$/.test(normalized) ? `${normalized}deg` : normalized
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {unknown} value
|
|
262
|
+
* @param {string} label
|
|
263
|
+
*/
|
|
264
|
+
function normalizeImageOffset(value, label) {
|
|
265
|
+
if (value === undefined || value === null || value === '')
|
|
266
|
+
return '0px'
|
|
267
|
+
|
|
268
|
+
if (typeof value === 'number') {
|
|
269
|
+
if (!Number.isFinite(value))
|
|
270
|
+
throw new Error(`[${label}] image offset must be a finite number or CSS length.`)
|
|
271
|
+
|
|
272
|
+
return `${value}px`
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (typeof value !== 'string')
|
|
276
|
+
throw new Error(`[${label}] image offset must be a finite number or CSS length.`)
|
|
277
|
+
|
|
278
|
+
const normalized = value.trim()
|
|
279
|
+
return normalized || '0px'
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {unknown} value
|
|
284
|
+
* @param {string} position
|
|
285
|
+
*/
|
|
286
|
+
function normalizeImageAnchor(value, position) {
|
|
287
|
+
if (typeof value !== 'string')
|
|
288
|
+
return normalizeImageTransformOrigin(position)
|
|
289
|
+
|
|
290
|
+
const normalized = value.trim().replace(/\s+/g, ' ')
|
|
291
|
+
return normalized || normalizeImageTransformOrigin(position)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* @param {unknown} value
|
|
296
|
+
*/
|
|
297
|
+
function normalizeImageColor(value) {
|
|
298
|
+
if (typeof value !== 'string')
|
|
299
|
+
return DEFAULT_IMAGE_DATA.color
|
|
300
|
+
|
|
301
|
+
return value.trim()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* @param {unknown} value
|
|
306
|
+
*/
|
|
307
|
+
function normalizeImageBackgroundColor(value) {
|
|
308
|
+
if (typeof value !== 'string')
|
|
309
|
+
return DEFAULT_IMAGE_DATA.backgroundColor
|
|
310
|
+
|
|
311
|
+
return value.trim()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @param {unknown} value
|
|
316
|
+
* @param {string} label
|
|
317
|
+
*/
|
|
318
|
+
function normalizeImageOpacity(value, label) {
|
|
319
|
+
if (value === undefined || value === null || value === '')
|
|
320
|
+
return DEFAULT_IMAGE_DATA.opacity
|
|
321
|
+
|
|
322
|
+
const normalized = Number(value)
|
|
323
|
+
|
|
324
|
+
if (!Number.isFinite(normalized) || normalized < 0 || normalized > 1)
|
|
325
|
+
throw new Error(`[${label}] image opacity must be a number between 0 and 1.`)
|
|
326
|
+
|
|
327
|
+
return normalized
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @param {unknown} value
|
|
332
|
+
* @param {string} label
|
|
333
|
+
*/
|
|
334
|
+
function normalizeImageSource(value, label) {
|
|
335
|
+
if (typeof value !== 'string' || !value.trim())
|
|
336
|
+
throw new Error(`[${label}] image src must be a non-empty string.`)
|
|
337
|
+
|
|
338
|
+
return value.trim()
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @param {unknown} value
|
|
343
|
+
* @param {string} label
|
|
344
|
+
*/
|
|
345
|
+
function assertNoLegacyScale(value, label) {
|
|
346
|
+
if (!isPlainObject(value))
|
|
347
|
+
return
|
|
348
|
+
|
|
349
|
+
if (Object.prototype.hasOwnProperty.call(value, 'scale'))
|
|
350
|
+
throw new Error(`[${label}] image scale is no longer supported; use zoom instead.`)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @param {string} input
|
|
355
|
+
* @param {string} label
|
|
356
|
+
* @returns {{ src: string } | null}
|
|
357
|
+
*/
|
|
358
|
+
function parseImageString(input, label) {
|
|
359
|
+
const normalized = input.trim()
|
|
360
|
+
|
|
361
|
+
if (!normalized)
|
|
362
|
+
return null
|
|
363
|
+
|
|
364
|
+
const tokens = normalized.split(/\s+/)
|
|
365
|
+
|
|
366
|
+
if (tokens.length > 1 && isImageFit(tokens[1])) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`[${label}] image shorthand is no longer supported. Use object syntax with src, fit, position, anchor, x, y, zoom, rotate, and backgroundColor.`,
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return { src: normalized }
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @param {ImageDataInput} input
|
|
377
|
+
* @param {Partial<NormalizedImageData>} [defaults]
|
|
378
|
+
* @param {string} [label]
|
|
379
|
+
* @returns {NormalizedImageData}
|
|
380
|
+
*/
|
|
381
|
+
export function normalizeImageData(input, defaults = {}, label = 'image') {
|
|
382
|
+
if (!isPlainObject(input))
|
|
383
|
+
throw new Error(`[${label}] image data must be a plain object.`)
|
|
384
|
+
|
|
385
|
+
assertNoLegacyScale(defaults, label)
|
|
386
|
+
assertNoLegacyScale(input, label)
|
|
387
|
+
|
|
388
|
+
const merged = {
|
|
389
|
+
...DEFAULT_IMAGE_DATA,
|
|
390
|
+
...defaults,
|
|
391
|
+
...input,
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const position = normalizeImagePosition(merged.position)
|
|
395
|
+
const zoom = normalizeImageZoom(merged.zoom, label)
|
|
396
|
+
const rotate = normalizeImageRotate(merged.rotate, label)
|
|
397
|
+
const backgroundColor = normalizeImageBackgroundColor(
|
|
398
|
+
Object.prototype.hasOwnProperty.call(merged, 'background-color')
|
|
399
|
+
? merged['background-color']
|
|
400
|
+
: merged.backgroundColor,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
src: normalizeImageSource(merged.src, label),
|
|
405
|
+
fit: normalizeImageFit(merged.fit, label),
|
|
406
|
+
position,
|
|
407
|
+
anchor: normalizeImageAnchor(merged.anchor, position),
|
|
408
|
+
x: normalizeImageOffset(merged.x, label),
|
|
409
|
+
y: normalizeImageOffset(merged.y, label),
|
|
410
|
+
zoom,
|
|
411
|
+
rotate,
|
|
412
|
+
color: normalizeImageColor(merged.color),
|
|
413
|
+
opacity: normalizeImageOpacity(merged.opacity, label),
|
|
414
|
+
backgroundColor,
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @param {string | ImageDataInput | Array<string | ImageDataInput> | null | undefined} input
|
|
420
|
+
* @param {Partial<NormalizedImageData>} [defaults]
|
|
421
|
+
* @param {string} [label]
|
|
422
|
+
* @returns {NormalizedImageData[]}
|
|
423
|
+
*/
|
|
424
|
+
export function parseImageBackgrounds(input, defaults = {}, label = 'image') {
|
|
425
|
+
if (input === undefined || input === null || input === '')
|
|
426
|
+
return []
|
|
427
|
+
|
|
428
|
+
const items = Array.isArray(input) ? input : [input]
|
|
429
|
+
|
|
430
|
+
return items.flatMap((item, index) => {
|
|
431
|
+
if (item === undefined || item === null || item === '')
|
|
432
|
+
return []
|
|
433
|
+
|
|
434
|
+
let raw
|
|
435
|
+
|
|
436
|
+
if (typeof item === 'string')
|
|
437
|
+
raw = parseImageString(item, `${label}[${index}]`)
|
|
438
|
+
else if (isPlainObject(item))
|
|
439
|
+
raw = item
|
|
440
|
+
else
|
|
441
|
+
throw new Error(`[${label}[${index}]] image background must be a string or plain object.`)
|
|
442
|
+
|
|
443
|
+
if (!raw)
|
|
444
|
+
return []
|
|
445
|
+
|
|
446
|
+
return [normalizeImageData(raw, defaults, `${label}[${index}]`)]
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* @param {string | ImageConfigInput | null | undefined} input
|
|
452
|
+
* @param {Partial<NormalizedImageConfig>} [defaults]
|
|
453
|
+
* @param {string} [label]
|
|
454
|
+
* @returns {NormalizedImageConfig | null}
|
|
455
|
+
*/
|
|
456
|
+
export function parseImageConfig(input, defaults = {}, label = 'image') {
|
|
457
|
+
if (input === undefined || input === null || input === '')
|
|
458
|
+
return null
|
|
459
|
+
|
|
460
|
+
let raw
|
|
461
|
+
|
|
462
|
+
if (typeof input === 'string')
|
|
463
|
+
raw = parseImageString(input, label)
|
|
464
|
+
else if (isPlainObject(input))
|
|
465
|
+
raw = input
|
|
466
|
+
else
|
|
467
|
+
throw new Error(`[${label}] image config must be a string or plain object.`)
|
|
468
|
+
|
|
469
|
+
if (!raw)
|
|
470
|
+
return null
|
|
471
|
+
|
|
472
|
+
const imageData = normalizeImageData(raw, defaults, label)
|
|
473
|
+
const merged = {
|
|
474
|
+
...DEFAULT_IMAGE_CONFIG,
|
|
475
|
+
...defaults,
|
|
476
|
+
...raw,
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
...imageData,
|
|
481
|
+
src: imageData.src,
|
|
482
|
+
alt: typeof merged.alt === 'string' ? merged.alt : DEFAULT_IMAGE_CONFIG.alt,
|
|
483
|
+
}
|
|
484
|
+
}
|