@abcnews/components-builder 0.0.1
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 +134 -0
- package/dist/BuilderStyleRoot/BuilderStyleRoot.svelte +175 -0
- package/dist/BuilderStyleRoot/BuilderStyleRoot.svelte.d.ts +5 -0
- package/dist/ContextMenu/ContextMenu.stories.svelte +66 -0
- package/dist/ContextMenu/ContextMenu.stories.svelte.d.ts +27 -0
- package/dist/ContextMenu/ContextMenu.svelte +111 -0
- package/dist/ContextMenu/ContextMenu.svelte.d.ts +7 -0
- package/dist/GoogleDocScrollyteller/GoogleDocScrollyteller.stories.svelte +24 -0
- package/dist/GoogleDocScrollyteller/GoogleDocScrollyteller.stories.svelte.d.ts +27 -0
- package/dist/GoogleDocScrollyteller/GoogleDocScrollyteller.svelte +211 -0
- package/dist/GoogleDocScrollyteller/GoogleDocScrollyteller.svelte.d.ts +9 -0
- package/dist/GoogleDocScrollyteller/utils.d.ts +23 -0
- package/dist/GoogleDocScrollyteller/utils.js +94 -0
- package/dist/Modal/Modal.stories.svelte +32 -0
- package/dist/Modal/Modal.stories.svelte.d.ts +27 -0
- package/dist/Modal/Modal.svelte +116 -0
- package/dist/Modal/Modal.svelte.d.ts +8 -0
- package/dist/ScreenshotTool/ScreenshotTool.stories.svelte +26 -0
- package/dist/ScreenshotTool/ScreenshotTool.stories.svelte.d.ts +27 -0
- package/dist/ScreenshotTool/ScreenshotTool.svelte +292 -0
- package/dist/ScreenshotTool/ScreenshotTool.svelte.d.ts +17 -0
- package/dist/Typeahead/Typeahead.stories.svelte +26 -0
- package/dist/Typeahead/Typeahead.stories.svelte.d.ts +27 -0
- package/dist/Typeahead/Typeahead.svelte +177 -0
- package/dist/Typeahead/Typeahead.svelte.d.ts +12 -0
- package/dist/UpdateChecker/UpdateChecker.stories.svelte +27 -0
- package/dist/UpdateChecker/UpdateChecker.stories.svelte.d.ts +27 -0
- package/dist/UpdateChecker/UpdateChecker.svelte +127 -0
- package/dist/UpdateChecker/UpdateChecker.svelte.d.ts +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +71 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { untrack } from "svelte";
|
|
3
|
+
import JSZip from "jszip";
|
|
4
|
+
import { saveAs } from "file-saver";
|
|
5
|
+
import eachLimit from "async/eachLimit";
|
|
6
|
+
import Modal from "../Modal/Modal.svelte";
|
|
7
|
+
|
|
8
|
+
const GENERATOR_URL = "https://fallback-automations-yknow.kyd.au/api";
|
|
9
|
+
const GENERATOR_MAX_PARALLEL = 3;
|
|
10
|
+
const GENERATOR_WIDTH = "1000";
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
defaultMarkerName = () => "Marker",
|
|
14
|
+
prefixes = {},
|
|
15
|
+
parseMarker = (str) => ({}),
|
|
16
|
+
iframeUrl = "",
|
|
17
|
+
} = $props();
|
|
18
|
+
|
|
19
|
+
// closed => pasting => preview => generate => done
|
|
20
|
+
let status = $state("closed");
|
|
21
|
+
let pastedState = $state();
|
|
22
|
+
let preview = $state([]);
|
|
23
|
+
let progress = $state(0);
|
|
24
|
+
let error = $state("");
|
|
25
|
+
|
|
26
|
+
// When the user first hits the "preview" screen, parse our textarea and show them a preview
|
|
27
|
+
$effect(() => {
|
|
28
|
+
(async () => {
|
|
29
|
+
let _pastedState = untrack(() => pastedState);
|
|
30
|
+
let _status = status;
|
|
31
|
+
if (_status !== "preview") {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const allowedPrefixes = Object.values(prefixes).map((prefix) =>
|
|
35
|
+
prefix.replace(/\d$/, ""),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Find markers in the text
|
|
39
|
+
const markers = _pastedState
|
|
40
|
+
.split("\n")
|
|
41
|
+
.filter((row) =>
|
|
42
|
+
allowedPrefixes.find(
|
|
43
|
+
(prefix) => row.slice(0, prefix.length) === prefix,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const uniqueMarkers = Array.from(new Set(markers));
|
|
48
|
+
|
|
49
|
+
// parse to object
|
|
50
|
+
const encodedMarkers = uniqueMarkers.map((marker) => parse(marker));
|
|
51
|
+
|
|
52
|
+
// pass through schema
|
|
53
|
+
const parsedMarkers = await Promise.all(encodedMarkers.map(parseMarker));
|
|
54
|
+
|
|
55
|
+
// generate a friendly name & reencode markers with hexFlip="none"
|
|
56
|
+
preview = await Promise.all(
|
|
57
|
+
parsedMarkers.map(async (marker) => ({
|
|
58
|
+
name: defaultMarkerName(marker),
|
|
59
|
+
marker,
|
|
60
|
+
markerString: "#" + marker,
|
|
61
|
+
})),
|
|
62
|
+
);
|
|
63
|
+
})();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function doFetch(generatorUrl) {
|
|
67
|
+
return fetch(generatorUrl).then((response) => {
|
|
68
|
+
if (response.status !== 200) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return response.blob();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function sleep(seconds) {
|
|
76
|
+
return new Promise((resolve) => setTimeout(resolve, seconds));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function createScreenshots({ preview }) {
|
|
80
|
+
const zip = new JSZip();
|
|
81
|
+
let completed = 0;
|
|
82
|
+
// Put a little bit in the bar at the start so it's clear what it is.
|
|
83
|
+
progress = 0.5 / preview.length;
|
|
84
|
+
error = "";
|
|
85
|
+
const imageBlobs = [];
|
|
86
|
+
await eachLimit(
|
|
87
|
+
preview,
|
|
88
|
+
GENERATOR_MAX_PARALLEL,
|
|
89
|
+
async ({ markerString }, callback) => {
|
|
90
|
+
const thisIframeUrl = iframeUrl + markerString;
|
|
91
|
+
const generatorUrl = [
|
|
92
|
+
GENERATOR_URL,
|
|
93
|
+
new URLSearchParams({
|
|
94
|
+
url: thisIframeUrl,
|
|
95
|
+
selector: `.iframe-mount > *`,
|
|
96
|
+
width: GENERATOR_WIDTH,
|
|
97
|
+
}).toString(),
|
|
98
|
+
].join("?");
|
|
99
|
+
|
|
100
|
+
let blob;
|
|
101
|
+
// retry. It fails sometimes.
|
|
102
|
+
for (let retry = 0; retry < 5; retry++) {
|
|
103
|
+
console.log(thisIframeUrl, "attempt ", retry);
|
|
104
|
+
blob = await doFetch(generatorUrl);
|
|
105
|
+
if (!blob) {
|
|
106
|
+
await sleep(1000 * 2 * retry);
|
|
107
|
+
}
|
|
108
|
+
if (blob) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
completed += 1;
|
|
113
|
+
progress = completed / preview.length;
|
|
114
|
+
|
|
115
|
+
imageBlobs.push(blob);
|
|
116
|
+
callback();
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const errors = [];
|
|
121
|
+
imageBlobs.forEach((blob, index) => {
|
|
122
|
+
const filename = `${String(index).padStart(3, "0")}-${preview[index].name}.png`;
|
|
123
|
+
if (blob) {
|
|
124
|
+
zip.file(filename, blob);
|
|
125
|
+
} else {
|
|
126
|
+
errors.push(filename);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (errors.length) {
|
|
131
|
+
status = "error";
|
|
132
|
+
if (errors.length === imageBlobs.length) {
|
|
133
|
+
error =
|
|
134
|
+
"Could not download files. The fallback service may not be running.";
|
|
135
|
+
return;
|
|
136
|
+
} else if (errors.length) {
|
|
137
|
+
error =
|
|
138
|
+
"Error: could not download some files. These might be missing from the zip:\n\n" +
|
|
139
|
+
errors.join("\n");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return zip.generateAsync({ type: "blob" }).then((content) => {
|
|
144
|
+
if (!errors.length) {
|
|
145
|
+
status = "complete";
|
|
146
|
+
}
|
|
147
|
+
saveAs(content, `fallback-bundle-${Date.now()}.zip`);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// When the user first hits the "generate" screen, call createScreenshot()
|
|
152
|
+
$effect(() => {
|
|
153
|
+
let _preview = untrack(() => preview);
|
|
154
|
+
let _status = status;
|
|
155
|
+
if (_status !== "generate") {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
createScreenshots({ preview: _preview });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
function onClose() {
|
|
162
|
+
status = "closed";
|
|
163
|
+
}
|
|
164
|
+
</script>
|
|
165
|
+
|
|
166
|
+
<button
|
|
167
|
+
onclick={(e) => {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
status = "pasting";
|
|
170
|
+
pastedState = "";
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
Screenshot tool
|
|
174
|
+
</button>
|
|
175
|
+
|
|
176
|
+
{#snippet footerChildren()}
|
|
177
|
+
{#if status === "preview"}
|
|
178
|
+
<button
|
|
179
|
+
onclick={(e) => {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
status = "pasting";
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
184
|
+
Back
|
|
185
|
+
</button>
|
|
186
|
+
{/if}
|
|
187
|
+
<button
|
|
188
|
+
disabled={status === "generate"}
|
|
189
|
+
onclick={(e) => {
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
switch (status) {
|
|
192
|
+
case "error":
|
|
193
|
+
status = "closed";
|
|
194
|
+
return;
|
|
195
|
+
case "pasting":
|
|
196
|
+
status = "preview";
|
|
197
|
+
return;
|
|
198
|
+
case "preview":
|
|
199
|
+
status = "generate";
|
|
200
|
+
return;
|
|
201
|
+
default:
|
|
202
|
+
status = "closed";
|
|
203
|
+
}
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
{#if status === "preview"}
|
|
207
|
+
Generate screenshots
|
|
208
|
+
{:else if status === "error" || status === "complete"}
|
|
209
|
+
Close
|
|
210
|
+
{:else}
|
|
211
|
+
Next
|
|
212
|
+
{/if}
|
|
213
|
+
</button>
|
|
214
|
+
{/snippet}
|
|
215
|
+
|
|
216
|
+
{#if status !== "closed"}
|
|
217
|
+
<Modal title="Screenshot tool" {onClose} {footerChildren}>
|
|
218
|
+
<div class="screenshot-tool">
|
|
219
|
+
{#if status === "pasting"}
|
|
220
|
+
<p>Paste your story, or a series of markers to create screenshots</p>
|
|
221
|
+
<textarea bind:value={pastedState} placeholder="#markVER1"></textarea>
|
|
222
|
+
{/if}
|
|
223
|
+
|
|
224
|
+
{#if status === "preview"}
|
|
225
|
+
<p>
|
|
226
|
+
This will create {preview.length} screenshot{preview.length === 1
|
|
227
|
+
? ""
|
|
228
|
+
: "s"}:
|
|
229
|
+
</p>
|
|
230
|
+
<table class="builder__table">
|
|
231
|
+
<thead>
|
|
232
|
+
<tr><th>#</th><th>Marker</th><th>Marker</th></tr>
|
|
233
|
+
</thead>
|
|
234
|
+
<tbody>
|
|
235
|
+
{#each preview as { name, markerString }, i}
|
|
236
|
+
<tr
|
|
237
|
+
><td>{i + 1}</td><td>{name}</td><td
|
|
238
|
+
><code class="marker">{markerString}</code></td
|
|
239
|
+
></tr
|
|
240
|
+
>
|
|
241
|
+
{/each}
|
|
242
|
+
</tbody>
|
|
243
|
+
</table>
|
|
244
|
+
{/if}
|
|
245
|
+
|
|
246
|
+
{#if status === "generate"}
|
|
247
|
+
<p>Generating screenshots. This may take some time.</p>
|
|
248
|
+
<progress max="100" value={Math.round(progress * 100)}></progress>
|
|
249
|
+
{/if}
|
|
250
|
+
|
|
251
|
+
{#if status === "error"}
|
|
252
|
+
<p>{error}</p>
|
|
253
|
+
{/if}
|
|
254
|
+
|
|
255
|
+
{#if status === "complete"}
|
|
256
|
+
<p>
|
|
257
|
+
Your screenshots are finished. Check for the zip file in your
|
|
258
|
+
Downloads folder.
|
|
259
|
+
</p>
|
|
260
|
+
{/if}
|
|
261
|
+
</div>
|
|
262
|
+
</Modal>
|
|
263
|
+
{/if}
|
|
264
|
+
|
|
265
|
+
<style>
|
|
266
|
+
.screenshot-tool {
|
|
267
|
+
width: 100vw;
|
|
268
|
+
max-width: 640px;
|
|
269
|
+
position: relative;
|
|
270
|
+
height: 100vh;
|
|
271
|
+
max-height: 200px;
|
|
272
|
+
}
|
|
273
|
+
textarea {
|
|
274
|
+
min-height: 150px;
|
|
275
|
+
}
|
|
276
|
+
table,
|
|
277
|
+
progress,
|
|
278
|
+
textarea {
|
|
279
|
+
width: calc(100% - 2px);
|
|
280
|
+
}
|
|
281
|
+
table {
|
|
282
|
+
max-width: calc(100% - 2px);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.marker {
|
|
286
|
+
white-space: nowrap;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
text-overflow: ellipsis;
|
|
289
|
+
display: block;
|
|
290
|
+
max-width: 20em;
|
|
291
|
+
}
|
|
292
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default ScreenshotTool;
|
|
2
|
+
type ScreenshotTool = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const ScreenshotTool: import("svelte").Component<{
|
|
7
|
+
defaultMarkerName?: Function;
|
|
8
|
+
prefixes?: Record<string, any>;
|
|
9
|
+
parseMarker?: Function;
|
|
10
|
+
iframeUrl?: string;
|
|
11
|
+
}, {}, "">;
|
|
12
|
+
type $$ComponentProps = {
|
|
13
|
+
defaultMarkerName?: Function;
|
|
14
|
+
prefixes?: Record<string, any>;
|
|
15
|
+
parseMarker?: Function;
|
|
16
|
+
iframeUrl?: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import Typeahead from "./Typeahead.svelte";
|
|
4
|
+
import BuilderStyleRoot from "../BuilderStyleRoot/BuilderStyleRoot.svelte";
|
|
5
|
+
|
|
6
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: "Example/Typeahead",
|
|
9
|
+
component: Typeahead,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
argTypes: {},
|
|
12
|
+
args: {},
|
|
13
|
+
decorators: [() => BuilderStyleRoot],
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -->
|
|
18
|
+
<Story
|
|
19
|
+
name="Primary"
|
|
20
|
+
args={{
|
|
21
|
+
values: [
|
|
22
|
+
{ value: "chicken", label: "Chicken" },
|
|
23
|
+
{ value: "egg", label: "Egg" },
|
|
24
|
+
],
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default Typeahead;
|
|
2
|
+
type Typeahead = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const Typeahead: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
import Typeahead from "./Typeahead.svelte";
|
|
15
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
16
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
17
|
+
$$bindings?: Bindings;
|
|
18
|
+
} & Exports;
|
|
19
|
+
(internal: unknown, props: {
|
|
20
|
+
$$events?: Events;
|
|
21
|
+
$$slots?: Slots;
|
|
22
|
+
}): Exports & {
|
|
23
|
+
$set?: any;
|
|
24
|
+
$on?: any;
|
|
25
|
+
};
|
|
26
|
+
z_$$bindings?: Bindings;
|
|
27
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { untrack } from "svelte";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
values = [],
|
|
6
|
+
value = [],
|
|
7
|
+
disabled,
|
|
8
|
+
onChange = () => {},
|
|
9
|
+
}: {
|
|
10
|
+
values: {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}[];
|
|
14
|
+
value: string[];
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
onChange?: (val: string[]) => void;
|
|
17
|
+
} = $props();
|
|
18
|
+
|
|
19
|
+
const uniqueId = "list" + (Math.random() * 10e15).toString(16);
|
|
20
|
+
let selectedValues = $state<string[]>(value);
|
|
21
|
+
let isFocused = $state(false);
|
|
22
|
+
let inputElement = $state<HTMLInputElement>();
|
|
23
|
+
let inputValue = $state("");
|
|
24
|
+
|
|
25
|
+
function getValueFromLabel(label: string) {
|
|
26
|
+
const foundValue = values.find((value) => value.label === label);
|
|
27
|
+
return foundValue;
|
|
28
|
+
}
|
|
29
|
+
$effect(() => {
|
|
30
|
+
const foundValue = getValueFromLabel(inputValue);
|
|
31
|
+
if (!foundValue) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
untrack(() => {
|
|
35
|
+
selectedValues = Array.from(
|
|
36
|
+
new Set([...selectedValues, foundValue.value]),
|
|
37
|
+
);
|
|
38
|
+
inputValue = "";
|
|
39
|
+
onChange(selectedValues);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
$effect(() => {
|
|
43
|
+
selectedValues = value;
|
|
44
|
+
});
|
|
45
|
+
let displayValues = $derived.by(() => {
|
|
46
|
+
return selectedValues.map(
|
|
47
|
+
(value) =>
|
|
48
|
+
values.find((thisValue) => thisValue.value === value)?.label || "",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<!-- this interaction is supplementary to the input element, so it's not an accessibility issue -->
|
|
54
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
55
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
56
|
+
<div
|
|
57
|
+
class="typeahead"
|
|
58
|
+
class:typeahead--focused={isFocused}
|
|
59
|
+
class:typeahead--disabled={disabled}
|
|
60
|
+
onclick={(e) => inputElement?.focus()}
|
|
61
|
+
>
|
|
62
|
+
<input
|
|
63
|
+
class="typeahead__input"
|
|
64
|
+
{disabled}
|
|
65
|
+
bind:this={inputElement}
|
|
66
|
+
bind:value={inputValue}
|
|
67
|
+
list={uniqueId}
|
|
68
|
+
onfocus={() => {
|
|
69
|
+
isFocused = true;
|
|
70
|
+
}}
|
|
71
|
+
onblur={() => {
|
|
72
|
+
isFocused = false;
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
<ul class="typeahead__selected-items">
|
|
76
|
+
{#each displayValues as label}
|
|
77
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
78
|
+
<li class="typeahead__selected-item" onclick={(e) => e.stopPropagation()}>
|
|
79
|
+
<div class="typeahead__selected-item-text">
|
|
80
|
+
{label}
|
|
81
|
+
</div>
|
|
82
|
+
<button
|
|
83
|
+
class="typeahead__selected-item-butt"
|
|
84
|
+
aria-label={`Remove ${label}`}
|
|
85
|
+
onclick={(e) => {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
const foundValue = getValueFromLabel(label);
|
|
88
|
+
if (!foundValue) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
selectedValues = selectedValues.filter(
|
|
92
|
+
(thisValue) => thisValue !== foundValue.value,
|
|
93
|
+
);
|
|
94
|
+
onChange(selectedValues);
|
|
95
|
+
}}>x</button
|
|
96
|
+
>
|
|
97
|
+
</li>
|
|
98
|
+
{/each}
|
|
99
|
+
</ul>
|
|
100
|
+
</div>
|
|
101
|
+
<datalist id={uniqueId}>
|
|
102
|
+
{#each values as value}
|
|
103
|
+
<option value={value.label}></option>
|
|
104
|
+
{/each}
|
|
105
|
+
</datalist>
|
|
106
|
+
|
|
107
|
+
<style>
|
|
108
|
+
.typeahead {
|
|
109
|
+
padding: 0.25rem 0.5rem;
|
|
110
|
+
background: var(--background);
|
|
111
|
+
color: var(--text);
|
|
112
|
+
border: 1px solid var(--border);
|
|
113
|
+
border-radius: 0.2rem;
|
|
114
|
+
}
|
|
115
|
+
.typeahead--focused {
|
|
116
|
+
outline: 2px solid Highlight;
|
|
117
|
+
}
|
|
118
|
+
.typeahead--disabled {
|
|
119
|
+
background: transparent;
|
|
120
|
+
cursor: not-allowed;
|
|
121
|
+
}
|
|
122
|
+
.typeahead--disabled * {
|
|
123
|
+
pointer-events: none;
|
|
124
|
+
}
|
|
125
|
+
ul,
|
|
126
|
+
li {
|
|
127
|
+
list-style: none;
|
|
128
|
+
margin: 0;
|
|
129
|
+
padding: 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.typeahead input.typeahead__input {
|
|
133
|
+
border: none;
|
|
134
|
+
outline: none;
|
|
135
|
+
width: 100%;
|
|
136
|
+
padding: 0.25rem 0;
|
|
137
|
+
background: transparent;
|
|
138
|
+
color: var(--c-black);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.typeahead__selected-items {
|
|
142
|
+
display: flex;
|
|
143
|
+
gap: 0.25rem;
|
|
144
|
+
flex-wrap: wrap;
|
|
145
|
+
max-height: 6rem;
|
|
146
|
+
overflow: auto;
|
|
147
|
+
}
|
|
148
|
+
.typeahead__selected-item {
|
|
149
|
+
background: var(--background);
|
|
150
|
+
color: var(--text);
|
|
151
|
+
border: 1px solid var(--border);
|
|
152
|
+
border-radius: 0.2rem;
|
|
153
|
+
position: relative;
|
|
154
|
+
padding: 0.25rem;
|
|
155
|
+
padding-right: 1.75rem;
|
|
156
|
+
display: inline-block;
|
|
157
|
+
font-size: 0.75rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.typeahead__selected-item-text {
|
|
161
|
+
display: inline-flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
}
|
|
165
|
+
.typeahead button.typeahead__selected-item-butt {
|
|
166
|
+
position: absolute;
|
|
167
|
+
right: 0;
|
|
168
|
+
top: 0;
|
|
169
|
+
padding: 0;
|
|
170
|
+
height: 100%;
|
|
171
|
+
width: 1.5rem;
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
justify-content: center;
|
|
175
|
+
border: none;
|
|
176
|
+
}
|
|
177
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
values: {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}[];
|
|
6
|
+
value: string[];
|
|
7
|
+
disabled: boolean;
|
|
8
|
+
onChange?: (val: string[]) => void;
|
|
9
|
+
};
|
|
10
|
+
declare const Typeahead: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type Typeahead = ReturnType<typeof Typeahead>;
|
|
12
|
+
export default Typeahead;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script module>
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import UpdateChecker from "./UpdateChecker.svelte";
|
|
4
|
+
import BuilderStyleRoot from "../BuilderStyleRoot/BuilderStyleRoot.svelte";
|
|
5
|
+
|
|
6
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: "Example/UpdateChecker",
|
|
9
|
+
component: UpdateChecker,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
argTypes: {},
|
|
12
|
+
args: {},
|
|
13
|
+
decorators: [() => BuilderStyleRoot],
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -->
|
|
18
|
+
<Story
|
|
19
|
+
name="Primary"
|
|
20
|
+
args={{
|
|
21
|
+
// don't pass anything here, this is just so we can preview it
|
|
22
|
+
overrideNewVersion: {
|
|
23
|
+
thisVersion: "1.2.3",
|
|
24
|
+
newVersion: "2.0.0",
|
|
25
|
+
},
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default UpdateChecker;
|
|
2
|
+
type UpdateChecker = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const UpdateChecker: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
import UpdateChecker from "./UpdateChecker.svelte";
|
|
15
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
16
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
17
|
+
$$bindings?: Bindings;
|
|
18
|
+
} & Exports;
|
|
19
|
+
(internal: unknown, props: {
|
|
20
|
+
$$events?: Events;
|
|
21
|
+
$$slots?: Slots;
|
|
22
|
+
}): Exports & {
|
|
23
|
+
$set?: any;
|
|
24
|
+
$on?: any;
|
|
25
|
+
};
|
|
26
|
+
z_$$bindings?: Bindings;
|
|
27
|
+
}
|