@txstate-mws/svelte-components 1.7.1 → 1.7.2
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.
|
@@ -88,11 +88,23 @@
|
|
|
88
88
|
export let maxlevel = 5
|
|
89
89
|
/**
|
|
90
90
|
* take over the display of any leaf value; receives the default rendering
|
|
91
|
-
* (datetimes already rewritten as local ISO8601, everything else
|
|
92
|
-
* plus the value's path, so it can do something else with
|
|
93
|
-
* types
|
|
91
|
+
* (datetimes already rewritten as local ISO8601, everything else
|
|
92
|
+
* stringified) plus the value's path, so it can do something else with
|
|
93
|
+
* specific fields or types - including recognizing marker strings a data
|
|
94
|
+
* source deliberately embedded, like '{"image": "https://..."}'
|
|
95
|
+
*
|
|
96
|
+
* return a string to display as text, return { html } to display markup the
|
|
97
|
+
* formatter built itself - it renders regardless of allowHtml, because the
|
|
98
|
+
* formatter is application code rather than data, though it is still
|
|
99
|
+
* scrubbed with stripUnsafeHtml since the urls and labels interpolated into
|
|
100
|
+
* it usually do come from the data - or return undefined to keep the
|
|
101
|
+
* default rendering
|
|
102
|
+
*
|
|
103
|
+
* { html } drops below its key as an indented block by default; pass
|
|
104
|
+
* inline: true for something compact like a link that should sit beside its
|
|
105
|
+
* key the way a short scalar does
|
|
94
106
|
*/
|
|
95
|
-
export let format: ((value: string, path: (string | number)[]) => string | undefined) | undefined = undefined
|
|
107
|
+
export let format: ((value: string, path: (string | number)[]) => string | { html: string, inline?: boolean } | undefined) | undefined = undefined
|
|
96
108
|
/**
|
|
97
109
|
* leaf values longer than this many characters are cut off with an ellipsis,
|
|
98
110
|
* after `format` has had its say; set Infinity if you really want it all
|
|
@@ -144,12 +156,21 @@
|
|
|
144
156
|
* so it stays beside the key, and a scalar whose rendered text contains line
|
|
145
157
|
* breaks (or is long enough that wrapping is inevitable) drops below the key,
|
|
146
158
|
* so the breaks don't leave text hanging in the middle of the row
|
|
159
|
+
*
|
|
160
|
+
* formatted values follow the same rules: { html } is a block unless the
|
|
161
|
+
* formatter flagged it inline, text follows the scalar rules
|
|
147
162
|
*/
|
|
148
163
|
function isBlockValue (value: any, key: string) {
|
|
164
|
+
const formatted = applyFormat(value, [...path, key])
|
|
165
|
+
if (formatted != null) {
|
|
166
|
+
if (typeof formatted === 'object') return !formatted.inline
|
|
167
|
+
const t = truncate(formatted)
|
|
168
|
+
return t.includes('\n') || t.length > BLOCKTEXT
|
|
169
|
+
}
|
|
149
170
|
if (level + 1 >= maxlevel || childAncestors.includes(value)) return false
|
|
150
171
|
if (arrayEntries(value) != null || objectEntries(value) != null) return true
|
|
151
172
|
if (typeof value !== 'string') return false
|
|
152
|
-
const rendered = formatValue(value
|
|
173
|
+
const rendered = formatValue(value)
|
|
153
174
|
return isHtml(rendered) || rendered.includes('\n') || rendered.length > BLOCKTEXT
|
|
154
175
|
}
|
|
155
176
|
|
|
@@ -165,16 +186,41 @@
|
|
|
165
186
|
let htmlContentHeight = 0
|
|
166
187
|
$: htmlClipped = htmlContentHeight > htmlClipHeight + 1
|
|
167
188
|
|
|
168
|
-
function
|
|
189
|
+
function truncate (str: string) {
|
|
190
|
+
return str.length > maxtext ? str.slice(0, maxtext).trimEnd() + '…' : str
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* run the format prop on a leaf value's default rendering; an { html }
|
|
195
|
+
* result needs DOMParser for scrubbing, so without one (i.e. during SSR) it
|
|
196
|
+
* is ignored and the value renders the default way until the browser
|
|
197
|
+
* hydrates
|
|
198
|
+
*/
|
|
199
|
+
function applyFormat (value: any, atPath: (string | number)[]): string | { html: string, inline?: boolean } | undefined {
|
|
200
|
+
if (format == null) return undefined
|
|
201
|
+
if (value == null || (typeof value === 'object' && !(value instanceof Date)) || typeof value === 'function') return undefined
|
|
202
|
+
const result = format(renderScalar(value), atPath)
|
|
203
|
+
if (result != null && typeof result === 'object' && typeof DOMParser === 'undefined') return undefined
|
|
204
|
+
return result
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
$: formatted = applyFormat(data, path)
|
|
208
|
+
$: formattedHtml = formatted != null && typeof formatted === 'object' ? stripUnsafeHtml(formatted.html) : undefined
|
|
209
|
+
$: formattedText = typeof formatted === 'string' ? truncate(formatted) : undefined
|
|
210
|
+
|
|
211
|
+
function formatValue (value: any) {
|
|
169
212
|
const str = renderScalar(value)
|
|
170
|
-
const formatted = format?.(str, atPath) ?? str
|
|
171
213
|
// never truncate html - slicing markup in half would break it
|
|
172
|
-
if (isHtml(
|
|
173
|
-
return
|
|
214
|
+
if (isHtml(str)) return str
|
|
215
|
+
return truncate(str)
|
|
174
216
|
}
|
|
175
217
|
</script>
|
|
176
218
|
|
|
177
|
-
{#if
|
|
219
|
+
{#if formattedHtml != null}
|
|
220
|
+
<span class="nested-data-scalar nested-data-trusted {className}">{@html formattedHtml}</span>
|
|
221
|
+
{:else if formattedText != null}
|
|
222
|
+
<span class="nested-data-scalar {className}">{formattedText}</span>
|
|
223
|
+
{:else if arrayData}
|
|
178
224
|
{#if elided}
|
|
179
225
|
<span class="nested-data-elided {className}" title={elidedTooltip}>{elidedArrayText}</span>
|
|
180
226
|
{:else if arrayData.length}
|
|
@@ -198,19 +244,19 @@
|
|
|
198
244
|
</dl>
|
|
199
245
|
{/if}
|
|
200
246
|
{:else if data != null && (typeof data !== 'object' || data instanceof Date) && typeof data !== 'function'}
|
|
201
|
-
{#if isHtml(formatValue(data
|
|
247
|
+
{#if isHtml(formatValue(data))}
|
|
202
248
|
<!-- the frame and legend keep rendered markup (especially lists) from being mistaken for the data's own structure -->
|
|
203
249
|
<fieldset class="nested-data-scalar nested-data-html {className}" class:nested-data-clipped={htmlClipped}>
|
|
204
250
|
<legend>HTML</legend>
|
|
205
251
|
<div class="nested-data-html-clip" bind:clientHeight={htmlClipHeight}>
|
|
206
|
-
<div bind:offsetHeight={htmlContentHeight}>{@html stripUnsafeHtml(formatValue(data
|
|
252
|
+
<div bind:offsetHeight={htmlContentHeight}>{@html stripUnsafeHtml(formatValue(data))}</div>
|
|
207
253
|
</div>
|
|
208
254
|
{#if htmlClipped}
|
|
209
255
|
<div class="nested-data-html-more" title={clippedTooltip}>…</div>
|
|
210
256
|
{/if}
|
|
211
257
|
</fieldset>
|
|
212
258
|
{:else}
|
|
213
|
-
<span class="nested-data-scalar {className}">{formatValue(data
|
|
259
|
+
<span class="nested-data-scalar {className}">{formatValue(data)}</span>
|
|
214
260
|
{/if}
|
|
215
261
|
{/if}
|
|
216
262
|
|
|
@@ -253,6 +299,14 @@
|
|
|
253
299
|
white-space: pre-line;
|
|
254
300
|
overflow-wrap: break-word;
|
|
255
301
|
}
|
|
302
|
+
/* markup built by the format prop renders inline, unframed */
|
|
303
|
+
.nested-data-trusted {
|
|
304
|
+
white-space: normal;
|
|
305
|
+
}
|
|
306
|
+
.nested-data-trusted :global(img) {
|
|
307
|
+
max-width: 100%;
|
|
308
|
+
vertical-align: middle;
|
|
309
|
+
}
|
|
256
310
|
/* html brings its own paragraphs and breaks; pre-line would double them up */
|
|
257
311
|
.nested-data-html {
|
|
258
312
|
white-space: normal;
|
|
@@ -296,8 +350,8 @@
|
|
|
296
350
|
.nested-data-html :global(p:last-of-type) {
|
|
297
351
|
margin-bottom: 0;
|
|
298
352
|
}
|
|
299
|
-
/* de-emphasize blocks of text that have dropped below their key */
|
|
300
|
-
dl.nested-data dd.nested-data-block > :global(.nested-data-scalar) {
|
|
353
|
+
/* de-emphasize blocks of text that have dropped below their key; formatter-built markup keeps full strength */
|
|
354
|
+
dl.nested-data dd.nested-data-block > :global(.nested-data-scalar:not(.nested-data-trusted)) {
|
|
301
355
|
opacity: var(--nested-data-block-opacity, 0.85);
|
|
302
356
|
}
|
|
303
357
|
.nested-data-elided {
|
|
@@ -13,10 +13,25 @@ declare const __propDef: {
|
|
|
13
13
|
*/ maxlevel?: number;
|
|
14
14
|
/**
|
|
15
15
|
* take over the display of any leaf value; receives the default rendering
|
|
16
|
-
* (datetimes already rewritten as local ISO8601, everything else
|
|
17
|
-
* plus the value's path, so it can do something else with
|
|
18
|
-
* types
|
|
19
|
-
|
|
16
|
+
* (datetimes already rewritten as local ISO8601, everything else
|
|
17
|
+
* stringified) plus the value's path, so it can do something else with
|
|
18
|
+
* specific fields or types - including recognizing marker strings a data
|
|
19
|
+
* source deliberately embedded, like '{"image": "https://..."}'
|
|
20
|
+
*
|
|
21
|
+
* return a string to display as text, return { html } to display markup the
|
|
22
|
+
* formatter built itself - it renders regardless of allowHtml, because the
|
|
23
|
+
* formatter is application code rather than data, though it is still
|
|
24
|
+
* scrubbed with stripUnsafeHtml since the urls and labels interpolated into
|
|
25
|
+
* it usually do come from the data - or return undefined to keep the
|
|
26
|
+
* default rendering
|
|
27
|
+
*
|
|
28
|
+
* { html } drops below its key as an indented block by default; pass
|
|
29
|
+
* inline: true for something compact like a link that should sit beside its
|
|
30
|
+
* key the way a short scalar does
|
|
31
|
+
*/ format?: ((value: string, path: (string | number)[]) => string | {
|
|
32
|
+
html: string;
|
|
33
|
+
inline?: boolean;
|
|
34
|
+
} | undefined) | undefined;
|
|
20
35
|
/**
|
|
21
36
|
* leaf values longer than this many characters are cut off with an ellipsis,
|
|
22
37
|
* after `format` has had its say; set Infinity if you really want it all
|