@reuters-graphics/graphics-components 0.0.4 → 0.0.7

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.
@@ -0,0 +1,10 @@
1
+ export default fuzzy;
2
+ declare namespace fuzzy {
3
+ function simpleFilter(pattern: any, array: any): any;
4
+ function test(pattern: any, str: any): boolean;
5
+ function match(pattern: any, str: any, opts: any): {
6
+ rendered: string;
7
+ score: number;
8
+ };
9
+ function filter(pattern: any, arr: any, opts: any): any;
10
+ }
@@ -0,0 +1,143 @@
1
+ /** @typedef {typeof __propDef.props} IndexProps */
2
+ /** @typedef {typeof __propDef.events} IndexEvents */
3
+ /** @typedef {typeof __propDef.slots} IndexSlots */
4
+ export default class Index extends SvelteComponentTyped<{
5
+ [x: string]: any;
6
+ value?: string;
7
+ extract?: (item: {
8
+ index: number;
9
+ embed: string;
10
+ }) => any;
11
+ id?: string;
12
+ data?: {
13
+ index: number;
14
+ embed: string;
15
+ }[];
16
+ disable?: (item: {
17
+ index: number;
18
+ embed: string;
19
+ }) => boolean;
20
+ filter?: (item: {
21
+ index: number;
22
+ embed: string;
23
+ }) => boolean;
24
+ autoselect?: boolean;
25
+ inputAfterSelect?: "update" | "clear" | "keep";
26
+ results?: {
27
+ original: {
28
+ index: number;
29
+ embed: string;
30
+ };
31
+ index: number;
32
+ score: number;
33
+ string: string;
34
+ disabled?: boolean;
35
+ }[];
36
+ focusAfterSelect?: boolean;
37
+ showDropdownOnFocus?: boolean;
38
+ limit?: number;
39
+ }, {
40
+ type: CustomEvent<string>;
41
+ input: Event;
42
+ change: Event;
43
+ focus: FocusEvent;
44
+ clear: CustomEvent<any>;
45
+ blur: FocusEvent;
46
+ keydown: KeyboardEvent;
47
+ select: CustomEvent<any>;
48
+ } & {
49
+ [evt: string]: CustomEvent<any>;
50
+ }, {
51
+ default: {
52
+ result: {
53
+ original: {
54
+ index: number;
55
+ embed: string;
56
+ };
57
+ index: number;
58
+ score: number;
59
+ string: string;
60
+ disabled?: boolean;
61
+ };
62
+ index: any;
63
+ value: string;
64
+ };
65
+ 'no-results': {
66
+ value: string;
67
+ };
68
+ }> {
69
+ }
70
+ export type IndexProps = typeof __propDef.props;
71
+ export type IndexEvents = typeof __propDef.events;
72
+ export type IndexSlots = typeof __propDef.slots;
73
+ import { SvelteComponentTyped } from "svelte";
74
+ declare const __propDef: {
75
+ props: {
76
+ [x: string]: any;
77
+ value?: string;
78
+ extract?: (item: {
79
+ index: number;
80
+ embed: string;
81
+ }) => any;
82
+ id?: string;
83
+ data?: {
84
+ index: number;
85
+ embed: string;
86
+ }[];
87
+ disable?: (item: {
88
+ index: number;
89
+ embed: string;
90
+ }) => boolean;
91
+ filter?: (item: {
92
+ index: number;
93
+ embed: string;
94
+ }) => boolean;
95
+ autoselect?: boolean;
96
+ inputAfterSelect?: "update" | "clear" | "keep";
97
+ results?: {
98
+ original: {
99
+ index: number;
100
+ embed: string;
101
+ };
102
+ index: number;
103
+ score: number;
104
+ string: string;
105
+ disabled?: boolean;
106
+ }[];
107
+ focusAfterSelect?: boolean;
108
+ showDropdownOnFocus?: boolean;
109
+ limit?: number;
110
+ };
111
+ events: {
112
+ type: CustomEvent<string>;
113
+ input: Event;
114
+ change: Event;
115
+ focus: FocusEvent;
116
+ clear: CustomEvent<any>;
117
+ blur: FocusEvent;
118
+ keydown: KeyboardEvent;
119
+ select: CustomEvent<any>;
120
+ } & {
121
+ [evt: string]: CustomEvent<any>;
122
+ };
123
+ slots: {
124
+ default: {
125
+ result: {
126
+ original: {
127
+ index: number;
128
+ embed: string;
129
+ };
130
+ index: number;
131
+ score: number;
132
+ string: string;
133
+ disabled?: boolean;
134
+ };
135
+ index: any;
136
+ value: string;
137
+ };
138
+ 'no-results': {
139
+ value: string;
140
+ };
141
+ };
142
+ };
143
+ export {};
@@ -7,6 +7,7 @@
7
7
  import Resizer from './Resizer/index.svelte';
8
8
  import { width } from './stores.js';
9
9
  import getUniqNames from './uniqNames.js';
10
+ import Typeahead from './Typeahead/index.svelte';
10
11
 
11
12
  export let embeds;
12
13
  export let breakpoints = [330, 510, 660, 930, 1200];
@@ -46,22 +47,33 @@
46
47
  />
47
48
  </header>
48
49
 
49
- <nav>
50
- {#each embeds as embed, i}
51
- <button
52
- on:click="{() => {
53
- activeEmbed = embed;
54
- }}"
55
- class:active="{activeEmbed === embed}"
50
+ <div id="typeahead-container">
51
+ <div class="embed-link">
52
+ <a
53
+ rel="external"
54
+ target="_blank"
55
+ href="{activeEmbed}"
56
+ title="{activeEmbed}"
56
57
  >
57
- {embedTitles[i]}
58
- <a rel="external" target="_blank" href="{embed}" title="{embed}">
59
- <Fa icon="{faLink}" />
60
- </a>
61
- </button>
62
- {/each}
63
- </nav>
58
+ Live link <Fa icon="{faLink}" />
59
+ </a>
60
+ </div>
61
+ <Typeahead
62
+ label="Select an embed"
63
+ value="{embedTitles[0]}"
64
+ extract="{(d) => embedTitles[d.index]}"
65
+ data="{embeds.map((embed, index) => ({ index, embed }))}"
66
+ placeholder="{'Search'}"
67
+ showDropdownOnFocus="{true}"
68
+ on:select="{({ detail }) => {
69
+ activeEmbed = detail.original.embed;
70
+ }}"
71
+ />
72
+ </div>
64
73
 
74
+ <div id="preview-label" style="width:{$width}px;">
75
+ <p>Preview</p>
76
+ </div>
65
77
  <div id="frame-parent" style="width:{$width}px;"></div>
66
78
  </div>
67
79
 
@@ -86,36 +98,38 @@
86
98
  margin: 20px 0;
87
99
  }
88
100
 
89
- nav {
90
- text-align: center;
91
- margin: 0 auto 20px;
92
- max-width: 900px;
93
- }
94
- nav button {
95
- margin: 0 4px 5px;
96
- background-color: transparent;
97
- border: 0;
98
- color: #999;
99
- padding: 2px 2px;
100
- cursor: pointer;
101
- font-family: "Knowledge", "Source Sans Pro", Arial, sans-serif;
102
- font-weight: 400;
103
- }
104
- nav button.active {
105
- border-bottom: 2px solid #666;
106
- color: #666;
101
+ div#typeahead-container {
102
+ max-width: 660px;
103
+ margin: 0 auto 15px;
104
+ position: relative;
107
105
  }
108
- nav button:focus {
109
- outline: none;
106
+ div#typeahead-container div.embed-link {
107
+ position: absolute;
108
+ top: 0;
109
+ right: 0;
110
+ display: inline-block;
111
+ z-index: 2;
110
112
  }
111
- nav button a {
113
+ div#typeahead-container div.embed-link a {
114
+ font-family: "Knowledge", "Source Sans Pro", Arial, sans-serif;
112
115
  color: #bbb;
113
116
  font-size: 12px;
117
+ text-decoration: none !important;
114
118
  }
115
- nav button a:hover {
119
+ div#typeahead-container div.embed-link a:hover {
116
120
  color: #666;
117
121
  }
118
122
 
123
+ div#preview-label {
124
+ margin: 0 auto;
125
+ }
126
+ div#preview-label p {
127
+ font-family: "Knowledge", "Source Sans Pro", Arial, sans-serif;
128
+ color: #aaa;
129
+ font-size: 0.75rem;
130
+ margin: 0 0 0.25rem;
131
+ }
132
+
119
133
  #frame-parent {
120
134
  border: 1px solid #ddd;
121
135
  margin: 0 auto;
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Fuzzy
3
+ * https://github.com/myork/fuzzy
4
+ *
5
+ * Copyright (c) 2012 Matt York
6
+ * Licensed under the MIT license.
7
+ */
8
+
9
+ const fuzzy = {};
10
+
11
+ // Return all elements of `array` that have a fuzzy
12
+ // match against `pattern`.
13
+ fuzzy.simpleFilter = function (pattern, array) {
14
+ return array.filter(function (str) {
15
+ return fuzzy.test(pattern, str);
16
+ });
17
+ };
18
+
19
+ // Does `pattern` fuzzy match `str`?
20
+ fuzzy.test = function (pattern, str) {
21
+ return fuzzy.match(pattern, str) !== null;
22
+ };
23
+
24
+ // If `pattern` matches `str`, wrap each matching character
25
+ // in `opts.pre` and `opts.post`. If no match, return null
26
+ fuzzy.match = function (pattern, str, opts) {
27
+ opts = opts || {};
28
+ let patternIdx = 0;
29
+ const result = [];
30
+ const len = str.length;
31
+ let totalScore = 0;
32
+ let currScore = 0;
33
+ // prefix
34
+ const pre = opts.pre || '';
35
+ // suffix
36
+ const post = opts.post || '';
37
+ // String to compare against. This might be a lowercase version of the
38
+ // raw string
39
+ const compareString = (opts.caseSensitive && str) || str.toLowerCase();
40
+ let ch;
41
+
42
+ pattern = (opts.caseSensitive && pattern) || pattern.toLowerCase();
43
+
44
+ // For each character in the string, either add it to the result
45
+ // or wrap in template if it's the next string in the pattern
46
+ for (let idx = 0; idx < len; idx++) {
47
+ ch = str[idx];
48
+ if (compareString[idx] === pattern[patternIdx]) {
49
+ ch = pre + ch + post;
50
+ patternIdx += 1;
51
+
52
+ // consecutive characters should increase the score more than linearly
53
+ currScore += 1 + currScore;
54
+ } else {
55
+ currScore = 0;
56
+ }
57
+ totalScore += currScore;
58
+ result[result.length] = ch;
59
+ }
60
+
61
+ // return rendered string if we have a match for every char
62
+ if (patternIdx === pattern.length) {
63
+ // if the string is an exact match with pattern, totalScore should be maxed
64
+ totalScore = compareString === pattern ? Infinity : totalScore;
65
+ return { rendered: result.join(''), score: totalScore };
66
+ }
67
+
68
+ return null;
69
+ };
70
+
71
+ // The normal entry point. Filters `arr` for matches against `pattern`.
72
+ // It returns an array with matching values of the type:
73
+ //
74
+ // [{
75
+ // string: '<b>lah' // The rendered string
76
+ // , index: 2 // The index of the element in `arr`
77
+ // , original: 'blah' // The original element in `arr`
78
+ // }]
79
+ //
80
+ // `opts` is an optional argument bag. Details:
81
+ //
82
+ // opts = {
83
+ // // string to put before a matching character
84
+ // pre: '<b>'
85
+ //
86
+ // // string to put after matching character
87
+ // , post: '</b>'
88
+ //
89
+ // // Optional function. Input is an entry in the given arr`,
90
+ // // output should be the string to test `pattern` against.
91
+ // // In this example, if `arr = [{crying: 'koala'}]` we would return
92
+ // // 'koala'.
93
+ // , extract: function(arg) { return arg.crying; }
94
+ // }
95
+ fuzzy.filter = function (pattern, arr, opts) {
96
+ if (!arr || arr.length === 0) {
97
+ return [];
98
+ }
99
+ if (typeof pattern !== 'string') {
100
+ return arr;
101
+ }
102
+ opts = opts || {};
103
+ return (
104
+ arr
105
+ .reduce(function (prev, element, idx, arr) {
106
+ let str = element;
107
+ if (opts.extract) {
108
+ str = opts.extract(element);
109
+ }
110
+ const rendered = fuzzy.match(pattern, str, opts);
111
+ if (rendered != null) {
112
+ prev[prev.length] = {
113
+ string: rendered.rendered,
114
+ score: rendered.score,
115
+ index: idx,
116
+ original: element,
117
+ };
118
+ }
119
+ return prev;
120
+ }, [])
121
+
122
+ // Sort by score. Browsers are inconsistent wrt stable/unstable
123
+ // sorting, so force stable by using the index in the case of tie.
124
+ // See http://ofb.net/~sethml/is-sort-stable.html
125
+ .sort(function (a, b) {
126
+ const compare = b.score - a.score;
127
+ if (compare) return compare;
128
+ return a.index - b.index;
129
+ })
130
+ );
131
+ };
132
+
133
+ export default fuzzy;
@@ -0,0 +1,349 @@
1
+ <script>
2
+ /**
3
+ * @typedef {Object} TItem
4
+ * @property {number} index
5
+ * @property {string} embed
6
+ */
7
+
8
+ export let id = 'typeahead-' + Math.random().toString(36);
9
+ export let value = '';
10
+
11
+ /** @type {TItem[]} */
12
+ export let data = [];
13
+
14
+ /** @type {(item: TItem) => any} */
15
+ export let extract = (item) => item;
16
+
17
+ /** @type {(item: TItem) => boolean} */
18
+ export let disable = (item) => false;
19
+
20
+ /** @type {(item: TItem) => boolean} */
21
+ export let filter = (item) => false;
22
+
23
+ /** Set to `false` to prevent the first result from being selected */
24
+ export let autoselect = true;
25
+
26
+ /**
27
+ * Set to `keep` to keep the search field unchanged after select, set to `clear` to auto-clear search field
28
+ * @type {"update" | "clear" | "keep"}
29
+ */
30
+ export let inputAfterSelect = 'update';
31
+
32
+ /** @type {{ original: TItem; index: number; score: number; string: string; disabled?: boolean; }[]} */
33
+ export let results = [];
34
+
35
+ /** Set to `true` to re-focus the input after selecting a result */
36
+ export let focusAfterSelect = false;
37
+
38
+ /** Set to `true` to only show results when the input is focused */
39
+ export let showDropdownOnFocus = false;
40
+
41
+ /**
42
+ * Specify the maximum number of results to return
43
+ * @type {number}
44
+ */
45
+ export let limit = Infinity;
46
+
47
+ import fuzzy from './fuzzy.js';
48
+ import Search from 'svelte-search';
49
+ import { tick, createEventDispatcher, afterUpdate } from 'svelte';
50
+
51
+ const dispatch = createEventDispatcher();
52
+
53
+ let comboboxRef = null;
54
+ let searchRef = null;
55
+ let hideDropdown = false;
56
+ let selectedIndex = -1;
57
+ let prevResults = '';
58
+ let isFocused = false;
59
+
60
+ afterUpdate(() => {
61
+ if (prevResults !== resultsId && autoselect) {
62
+ selectedIndex = getNextNonDisabledIndex();
63
+ }
64
+
65
+ if (prevResults !== resultsId && !$$slots['no-results']) {
66
+ hideDropdown = results.length === 0;
67
+ }
68
+
69
+ prevResults = resultsId;
70
+ });
71
+
72
+ async function select() {
73
+ const result = results[selectedIndex];
74
+
75
+ if (result.disabled) return;
76
+
77
+ const selectedValue = extract(result.original);
78
+ const searchedValue = value;
79
+
80
+ if (inputAfterSelect === 'clear') value = '';
81
+ if (inputAfterSelect === 'update') value = selectedValue;
82
+
83
+ dispatch('select', {
84
+ selectedIndex,
85
+ searched: searchedValue,
86
+ selected: selectedValue,
87
+ original: result.original,
88
+ originalIndex: result.index,
89
+ });
90
+
91
+ await tick();
92
+
93
+ if (focusAfterSelect) searchRef.focus();
94
+ close();
95
+ }
96
+
97
+ /** @type {() => number} */
98
+ function getNextNonDisabledIndex() {
99
+ let index = 0;
100
+ let disabled = results[index]?.disabled ?? false;
101
+
102
+ while (disabled) {
103
+ if (index === results.length) {
104
+ index = 0;
105
+ } else {
106
+ index += 1;
107
+ }
108
+
109
+ disabled = results[index]?.disabled ?? false;
110
+ }
111
+
112
+ return index;
113
+ }
114
+
115
+ /** @type {(direction: -1 | 1) => void} */
116
+ function change(direction) {
117
+ let index =
118
+ direction === 1 && selectedIndex === results.length - 1
119
+ ? 0
120
+ : selectedIndex + direction;
121
+ if (index < 0) index = results.length - 1;
122
+
123
+ let disabled = results[index].disabled;
124
+
125
+ while (disabled) {
126
+ if (index === results.length) {
127
+ index = 0;
128
+ } else {
129
+ index += direction;
130
+ }
131
+
132
+ disabled = results[index].disabled;
133
+ }
134
+
135
+ selectedIndex = index;
136
+ }
137
+
138
+ const open = () => (hideDropdown = false);
139
+ const close = () => (hideDropdown = true);
140
+
141
+ $: options = { pre: '<mark>', post: '</mark>', extract };
142
+ $: results =
143
+ value !== ''
144
+ ? fuzzy
145
+ .filter(value, data, options)
146
+ .filter(({ score }) => score > 0)
147
+ .slice(0, limit)
148
+ .filter((result) => !filter(result.original))
149
+ .map((result) => ({ ...result, disabled: disable(result.original) }))
150
+ : data.map((d) => ({ string: extract(d), original: d }));
151
+
152
+ $: resultsId = results.map((result) => extract(result.original)).join('');
153
+ $: showResults = !hideDropdown && results.length > 0 && isFocused;
154
+ $: if (showDropdownOnFocus) {
155
+ showResults = showResults && isFocused;
156
+ }
157
+ </script>
158
+
159
+ <svelte:window
160
+ on:click="{({ target }) => {
161
+ if (!hideDropdown && !comboboxRef?.contains(target)) {
162
+ close();
163
+ }
164
+ }}"
165
+ />
166
+
167
+ <div
168
+ data-svelte-typeahead
169
+ bind:this="{comboboxRef}"
170
+ role="combobox"
171
+ aria-haspopup="listbox"
172
+ aria-owns="{id}-listbox"
173
+ class:dropdown="{results.length > 0}"
174
+ aria-expanded="{showResults}"
175
+ id="{id}-typeahead"
176
+ >
177
+ <Search
178
+ id="{id}"
179
+ removeFormAriaAttributes="{true}"
180
+ {...$$restProps}
181
+ bind:ref="{searchRef}"
182
+ aria-autocomplete="list"
183
+ aria-controls="{id}-listbox"
184
+ aria-labelledby="{id}-label"
185
+ aria-activedescendant="{selectedIndex >= 0 &&
186
+ !hideDropdown &&
187
+ results.length > 0
188
+ ? `${id}-result-${selectedIndex}`
189
+ : null}"
190
+ bind:value
191
+ on:type
192
+ on:input
193
+ on:change
194
+ on:focus
195
+ on:focus="{() => {
196
+ open();
197
+ if (showDropdownOnFocus) {
198
+ showResults = true;
199
+ isFocused = true;
200
+ }
201
+ }}"
202
+ on:clear
203
+ on:clear="{open}"
204
+ on:blur
205
+ on:keydown
206
+ on:keydown="{(e) => {
207
+ if (results.length === 0) return;
208
+
209
+ switch (e.key) {
210
+ case 'Enter':
211
+ select();
212
+ break;
213
+ case 'ArrowDown':
214
+ e.preventDefault();
215
+ change(1);
216
+ break;
217
+ case 'ArrowUp':
218
+ e.preventDefault();
219
+ change(-1);
220
+ break;
221
+ case 'Escape':
222
+ e.preventDefault();
223
+ value = '';
224
+ searchRef?.focus();
225
+ close();
226
+ break;
227
+ }
228
+ }}"
229
+ />
230
+ <ul
231
+ class:svelte-typeahead-list="{true}"
232
+ role="listbox"
233
+ aria-labelledby="{id}-label"
234
+ id="{id}-listbox"
235
+ >
236
+ {#if showResults}
237
+ {#each results as result, index}
238
+ <li
239
+ role="option"
240
+ id="{id}-result-{index}"
241
+ class:selected="{selectedIndex === index}"
242
+ class:disabled="{result.disabled}"
243
+ aria-selected="{selectedIndex === index}"
244
+ on:click="{() => {
245
+ if (result.disabled) return;
246
+ selectedIndex = index;
247
+ select();
248
+ }}"
249
+ on:mouseenter="{() => {
250
+ if (result.disabled) return;
251
+ selectedIndex = index;
252
+ }}"
253
+ >
254
+ <slot result="{result}" index="{index}" value="{value}">
255
+ {@html result.string}
256
+ </slot>
257
+ </li>
258
+ {/each}
259
+ {/if}
260
+ {#if $$slots['no-results'] && !hideDropdown && value.length > 0 && results.length === 0}
261
+ <div class:no-results="{true}">
262
+ <slot name="no-results" value="{value}" />
263
+ </div>
264
+ {/if}
265
+ </ul>
266
+ </div>
267
+
268
+ <style>[data-svelte-typeahead] {
269
+ position: relative;
270
+ }
271
+
272
+ ul {
273
+ position: absolute;
274
+ top: 100%;
275
+ left: 0;
276
+ width: calc(100% - 2px);
277
+ padding: 0;
278
+ margin: 0;
279
+ list-style: none;
280
+ background-color: inherit;
281
+ }
282
+
283
+ [aria-expanded=true] ul {
284
+ z-index: 1;
285
+ border: 1px solid #ddd;
286
+ max-height: 50vh;
287
+ overflow-y: scroll;
288
+ }
289
+
290
+ li,
291
+ .no-results {
292
+ padding: 0.25rem 1rem;
293
+ font-family: "Knowledge", "Source Sans Pro", Arial, sans-serif;
294
+ color: #333;
295
+ }
296
+
297
+ li {
298
+ cursor: pointer;
299
+ }
300
+ li :global(mark) {
301
+ padding: 0;
302
+ background-color: #ffff9a;
303
+ }
304
+
305
+ li:not(:last-of-type) {
306
+ border-bottom: 1px solid #e0e0e0;
307
+ }
308
+
309
+ li:hover {
310
+ background-color: #efefef;
311
+ }
312
+
313
+ .selected {
314
+ background-color: #efefef;
315
+ }
316
+
317
+ .selected:hover {
318
+ background-color: #e5e5e5;
319
+ }
320
+
321
+ .disabled {
322
+ opacity: 0.4;
323
+ cursor: not-allowed;
324
+ }
325
+
326
+ :global([data-svelte-search] label) {
327
+ margin-bottom: 0.25rem;
328
+ display: inline-flex;
329
+ font-size: 0.75rem;
330
+ color: #aaa;
331
+ font-family: "Knowledge", "Source Sans Pro", Arial, sans-serif;
332
+ }
333
+
334
+ :global([data-svelte-search] input) {
335
+ width: 100%;
336
+ padding: 0.5rem 0.75rem;
337
+ background: none;
338
+ font-size: 1rem;
339
+ border: 0;
340
+ border-radius: 0 !important;
341
+ background-color: #fff;
342
+ border: 1px solid #ddd;
343
+ font-family: var(--theme-font-family-sans-serif, "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif);
344
+ }
345
+
346
+ :global([data-svelte-search] input:focus) {
347
+ outline: none;
348
+ border: 1px solid #ccc;
349
+ }</style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reuters-graphics/graphics-components",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "homepage": "https://reuters-graphics.github.io/graphics-components",
@@ -89,6 +89,7 @@
89
89
  "pym.js": "^1.3.2",
90
90
  "svelte-fa": "^2.4.0",
91
91
  "svelte-intersection-observer": "^0.10.0",
92
+ "svelte-search": "^2.0.1",
92
93
  "ua-parser-js": "^0.7.27"
93
94
  },
94
95
  "exports": {
@@ -105,6 +106,8 @@
105
106
  "./components/FeaturePhoto/FeaturePhoto.svelte": "./dist/components/FeaturePhoto/FeaturePhoto.svelte",
106
107
  "./components/Framer/Framer.svelte": "./dist/components/Framer/Framer.svelte",
107
108
  "./components/Framer/Resizer/index.svelte": "./dist/components/Framer/Resizer/index.svelte",
109
+ "./components/Framer/Typeahead/fuzzy": "./dist/components/Framer/Typeahead/fuzzy.js",
110
+ "./components/Framer/Typeahead/index.svelte": "./dist/components/Framer/Typeahead/index.svelte",
108
111
  "./components/Framer/stores": "./dist/components/Framer/stores.js",
109
112
  "./components/Framer/uniqNames": "./dist/components/Framer/uniqNames.js",
110
113
  "./components/GraphicBlock/AriaHidden.svelte": "./dist/components/GraphicBlock/AriaHidden.svelte",