@dosgato/dialog 1.0.7 → 1.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.
@@ -47,6 +47,7 @@ $: setNeedsShowHelp(helpelement);
47
47
  <div class="dialog-field-content">
48
48
  {#if helptext}
49
49
  <!-- svelte-ignore a11y-click-events-have-key-events -->
50
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
50
51
  <div use:resize={{ debounce: 10 }} on:resize={setNeedsShowHelp} id={helptextid} class="dialog-field-help" class:needsShowHelp class:expanded={showhelp} on:click={() => { if (needsShowHelp) showhelp = !showhelp }}>
51
52
  <span bind:this={helpelement}>{@html helptext}</span>
52
53
  {#if needsShowHelp}
@@ -69,6 +70,7 @@ $: setNeedsShowHelp(helpelement);
69
70
  .dialog-field-container[data-related~="1"] {
70
71
  padding-top: 0;
71
72
  padding-left: calc(var(--dialog-container-padding, 1em) + var(--dialog-related-padding, 1em));
73
+ margin-top: -0.5em;
72
74
  }
73
75
  .dialog-field-container[data-related~="2"] {
74
76
  padding-left: calc(var(--dialog-container-padding, 1em) + (2 * var(--dialog-related-padding, 1em)));
@@ -130,6 +130,10 @@ $: describedby = [title ? labelid : undefined, descid].filter(isNotBlank).join('
130
130
  margin-right: 0.4em;
131
131
  }
132
132
 
133
+ .header-buttons button {
134
+ color: black;
135
+ }
136
+
133
137
  .dialog-content {
134
138
  position: relative;
135
139
  padding: 2em;
@@ -69,7 +69,7 @@ onMount(reactToChoices);
69
69
  <div class="dialog-choices {className}" class:valid class:invalid>
70
70
  {#each choices as choice, idx (choice.value)}
71
71
  {@const checkid = `${path}.${idx}`}
72
- {@const included = value && value.includes(choice.value)}
72
+ {@const included = value?.includes(choice.value)}
73
73
  {@const label = choice.label || (typeof choice.value === 'string' ? choice.value : '')}
74
74
  <label for={checkid} style:width style:order={orders[idx]}>
75
75
  <Checkbox id={checkid} name={checkid} value={included} descid={getDescribedBy([descid, messagesid, helptextid, extradescid])} disabled={choice.disabled} onChange={() => onChangeCheckbox(setVal, choice, included)} {onBlur} />
@@ -66,7 +66,7 @@ async function userUrlEntryDebounced() {
66
66
  store.clearPreview();
67
67
  if (isBlank(url)) {
68
68
  selectedAsset = undefined;
69
- formStore.setField(finalPath, undefined);
69
+ void formStore.setField(finalPath, undefined);
70
70
  return;
71
71
  }
72
72
  let found = false;
@@ -117,7 +117,7 @@ async function userUrlEntryDebounced() {
117
117
  }
118
118
  }
119
119
  }
120
- formStore.setField(finalPath, selectedAsset?.id);
120
+ void formStore.setField(finalPath, selectedAsset?.id);
121
121
  formStore.dirtyField(finalPath);
122
122
  }
123
123
  const urlToValueCache = {};
@@ -159,7 +159,7 @@ async function updateSelected(..._) {
159
159
  }
160
160
  }
161
161
  }
162
- $: updateSelected($value);
162
+ $: void updateSelected($value);
163
163
  </script>
164
164
 
165
165
  <FieldStandard bind:id {path} {descid} {label} {defaultValue} {conditional} {required} {related} {helptext} let:value let:messagesid let:helptextid let:valid let:invalid let:id let:onBlur let:setVal>
@@ -243,6 +243,11 @@ $: updateSelected($value);
243
243
  align-items: flex-start;
244
244
  margin-top: 0.2em;
245
245
  }
246
+ .dialog-chooser-entry > button {
247
+ border-radius: 0.25em;
248
+ border: 1px solid #808080;
249
+ color: black;
250
+ }
246
251
  .dialog-chooser-entry-input {
247
252
  position: relative;
248
253
  flex-grow: 1;
@@ -261,6 +266,7 @@ $: updateSelected($value);
261
266
  transform: translateY(-50%);
262
267
  cursor: pointer;
263
268
  line-height: 1;
269
+ color: black;
264
270
  }
265
271
  :global([data-eq~="400px"] .dialog-chooser-container .dialog-chooser-thumbnail img) {
266
272
  object-position: left;
@@ -15,8 +15,24 @@ export let required = false;
15
15
  export let related = 0;
16
16
  export let extradescid = undefined;
17
17
  export let helptext = undefined;
18
+ export let inputelement = undefined;
19
+ let showBadInputMessage = false;
20
+ function wrapOnBlur(onBlur) {
21
+ const date = new Date(inputelement.value);
22
+ showBadInputMessage = inputelement.validity.badInput || !(date instanceof Date && !isNaN(date.valueOf()));
23
+ onBlur();
24
+ }
18
25
  </script>
19
26
 
20
27
  <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {related} {helptext} serialize={datetimeSerialize} deserialize={datetimeDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:helptextid let:messagesid>
21
- <Input type="datetime-local" name={path} {value} {id} class="dialog-input {className}" {onChange} {onBlur} {valid} {invalid} {min} {max} {step} {extradescid} {messagesid} {helptextid}/>
28
+ {#if showBadInputMessage}<div class="bad-input-warning" aria-live='polite'>{`Field ${label}`} must include both a date and time</div>{/if}
29
+ <Input bind:inputelement={inputelement} type="datetime-local" name={path} {value} {id} class="dialog-input {className}" onBlur={() => { wrapOnBlur(onBlur) }} {onChange} {valid} {invalid} {min} {max} {step} {extradescid} {messagesid} {helptextid}/>
22
30
  </FieldStandard>
31
+
32
+ <style>
33
+ .bad-input-warning {
34
+ margin-bottom: 0.3em;
35
+ color: var(--dg-danger-bg, #9a3332);
36
+ }
37
+
38
+ </style>
@@ -18,6 +18,7 @@ declare const __propDef: {
18
18
  related?: number | true | undefined;
19
19
  extradescid?: string | undefined;
20
20
  helptext?: string | undefined;
21
+ inputelement?: HTMLInputElement | undefined;
21
22
  };
22
23
  events: {
23
24
  [evt: string]: CustomEvent<any>;
@@ -62,7 +62,7 @@ function valueToSelectedChoices(value) {
62
62
  return ret;
63
63
  }
64
64
  function getAvailable(value) {
65
- return choices.filter(choice => value.indexOf(choice.value) === -1);
65
+ return choices.filter(choice => !value.includes(choice.value));
66
66
  }
67
67
  function onkeydown(value, setVal) {
68
68
  return (e) => {
@@ -85,6 +85,7 @@ function onkeydown(value, setVal) {
85
85
  </script>
86
86
 
87
87
  <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {descid} {related} {helptext} let:value let:valid let:invalid let:id let:onBlur let:setVal let:helptextid let:messagesid serialize={arraySerialize}>
88
+ <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
88
89
  <div {id} role="group" class="dual-list-container" on:keydown={onkeydown(value, setVal)}>
89
90
  <ScreenReaderOnly>
90
91
  <span aria-live="polite">{instructions}</span>
@@ -1,4 +1,4 @@
1
- <script>import { Field, nullableSerialize, nullableDeserialize, FormStore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
1
+ <script>import { Field, nullableSerialize, nullableDeserialize, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
2
2
  import { getContext } from 'svelte';
3
3
  import { isNotBlank } from 'txstate-utils';
4
4
  export let id = undefined;
@@ -11,7 +11,7 @@ export let conditional = undefined;
11
11
  const store = getContext(FORM_CONTEXT);
12
12
  const inheritedPath = getContext(FORM_INHERITED_PATH);
13
13
  const finalPath = [inheritedPath, path].filter(isNotBlank).join('.');
14
- $: store.setField(finalPath, value);
14
+ $: void store.setField(finalPath, value);
15
15
  </script>
16
16
 
17
17
  <Field {path} {conditional} {boolean} {number} serialize={!notNull ? nullableSerialize : undefined} deserialize={!notNull ? nullableDeserialize : undefined} let:value>
@@ -111,6 +111,10 @@ $: messages = compact ? $messageStore : [];
111
111
  padding: 0.15em;
112
112
  cursor: pointer;
113
113
  font-size: 1.3em;
114
+ color: black;
115
+ }
116
+ .dialog-multiple-buttons button:disabled {
117
+ color: #6d6d6d;
114
118
  }
115
119
 
116
120
  </style>
@@ -53,3 +53,10 @@ onMount(reactToChoices);
53
53
  {/each}
54
54
  </select>
55
55
  </FieldStandard>
56
+
57
+ <style>
58
+ select {
59
+ color: black;
60
+ }
61
+
62
+ </style>
package/dist/Form.svelte CHANGED
@@ -1,4 +1,4 @@
1
- <script>import { Form, FormStore } from '@txstate-mws/svelte-forms';
1
+ <script>import { Form } from '@txstate-mws/svelte-forms';
2
2
  import { setContext } from 'svelte';
3
3
  import { CHOOSER_API_CONTEXT } from './chooser';
4
4
  let className = '';
@@ -26,7 +26,7 @@ async function reactToItems(..._) {
26
26
  if (listboxElement)
27
27
  listboxElement.removeAttribute('aria-activedescendant');
28
28
  }
29
- $: reactToItems(items);
29
+ $: void reactToItems(items);
30
30
  const selectItem = (item, index) => (e) => {
31
31
  e.stopPropagation();
32
32
  e.preventDefault();
@@ -33,4 +33,4 @@ function getMaxlengthMessage(length) {
33
33
  font-weight: bold;
34
34
  }
35
35
 
36
- </style>
36
+ </style>
package/dist/Tab.svelte CHANGED
@@ -1,7 +1,7 @@
1
1
  <script>import { derivedStore } from '@txstate-mws/svelte-store';
2
2
  import { getContext } from 'svelte';
3
3
  import Icon from './Icon.svelte';
4
- import { TabStore, TAB_CONTEXT } from './TabStore';
4
+ import { TAB_CONTEXT } from './TabStore';
5
5
  export let name;
6
6
  const { store, onClick, onKeyDown, tabelements } = getContext(TAB_CONTEXT);
7
7
  const accordion = store.accordion();
package/dist/Tabs.svelte CHANGED
@@ -1,10 +1,11 @@
1
- <script>import { modifierKey, resize, offset } from '@txstate-mws/svelte-components';
1
+ <script>import { modifierKey, resize, offset, PopupMenu } from '@txstate-mws/svelte-components';
2
2
  import { Store } from '@txstate-mws/svelte-store';
3
- import { getContext, onMount, setContext } from 'svelte';
3
+ import { getContext, onMount, setContext, tick } from 'svelte';
4
4
  import { roundTo } from 'txstate-utils';
5
5
  import { DIALOG_TABS_CONTEXT } from './Dialog.svelte';
6
6
  import Icon from './Icon.svelte';
7
7
  import { TabStore, TAB_CONTEXT } from './TabStore';
8
+ import caretRightFill from '@iconify-icons/ph/caret-right-fill';
8
9
  export let tabs;
9
10
  export let active = undefined;
10
11
  export let store = new TabStore(tabs, active);
@@ -13,7 +14,9 @@ export let accordionOnMobile = true;
13
14
  $: store.update(v => ({ ...v, tabs, accordionOnMobile }));
14
15
  const activeStore = new Store({});
15
16
  const tabelements = [];
17
+ let tabOverflowButton;
16
18
  let activeelement;
19
+ let hiddenTabs = [];
17
20
  setContext(TAB_CONTEXT, { store, onClick, onKeyDown, tabelements });
18
21
  const dialogContext = (disableDialogControl ? undefined : getContext(DIALOG_TABS_CONTEXT)) ?? { change: () => { } };
19
22
  dialogContext.onNext = () => { store.right(); };
@@ -47,18 +50,22 @@ function onKeyDown(idx) {
47
50
  }
48
51
  else if (e.key === 'ArrowLeft' && $store.current > 0) {
49
52
  store.left();
53
+ await tick();
50
54
  tabelements[$store.current].focus();
51
55
  }
52
56
  else if (e.key === 'ArrowRight' && $store.current < $store.tabs.length - 1) {
53
57
  store.right();
58
+ await tick();
54
59
  tabelements[$store.current].focus();
55
60
  }
56
61
  else if (e.key === 'Home' && $store.current > 0) {
57
62
  store.home();
63
+ await tick();
58
64
  tabelements[$store.current].focus();
59
65
  }
60
66
  else if (e.key === 'End' && $store.current < $store.tabs.length - 1) {
61
67
  store.end();
68
+ await tick();
62
69
  tabelements[$store.current].focus();
63
70
  }
64
71
  };
@@ -66,6 +73,17 @@ function onKeyDown(idx) {
66
73
  function isActive(idx, activeidx) {
67
74
  return idx === (activeidx ?? 0);
68
75
  }
76
+ function onOverflowChange(e) {
77
+ store.activateName(e.detail.value);
78
+ }
79
+ function tabIsHidden(index) {
80
+ if (!wrapping)
81
+ return false;
82
+ const left = Math.max($currentIdx - cols + 1, 0);
83
+ const right = Math.min(left + cols - 1);
84
+ // return (index !== $currentIdx && index >= cols) || (index === cols - 1 && $currentIdx > cols - 1)
85
+ return (index !== $currentIdx) && ((index < left) || (index > right));
86
+ }
69
87
  const activeOversize = 2;
70
88
  async function reactToCurrent(..._) {
71
89
  if (!activeelement)
@@ -80,9 +98,10 @@ async function reactToCurrent(..._) {
80
98
  const width = span.offsetWidth + (activeOversize * 2);
81
99
  activeelement.style.transform = `translateX(${left}px)`;
82
100
  activeelement.style.width = `${width}px`;
101
+ hiddenTabs = $store.tabs.filter((tab, idx) => { return tabIsHidden(idx); });
83
102
  }
84
103
  $: active = $currentName;
85
- $: reactToCurrent($activeStore);
104
+ $: void reactToCurrent($activeStore);
86
105
  onMount(reactToCurrent);
87
106
  </script>
88
107
 
@@ -92,9 +111,13 @@ onMount(reactToCurrent);
92
111
  <ul use:resize={{ store }} class="tabs-buttons" role="tablist">
93
112
  {#each $store.tabs as tab, idx (tab.name)}
94
113
  {@const active = isActive(idx, $store.current)}
95
- {@const left = idx % cols === 0}
96
- <li bind:this={tabelements[idx]} use:offset={{ store: active ? activeStore : undefined }} id={$store.tabids[tab.name]} class="tabs-tab" class:left class:wrapping class:active style:font-size="{scalefactor}em" style:line-height={1.2 / scalefactor} aria-selected={active} aria-controls={$store.panelids[tab.name]} role="tab" tabindex={active ? 0 : -1} on:click={onClick(idx)} on:keydown={onKeyDown(idx)}><span><Icon icon={tab.icon} inline />{tab.title}</span></li>
114
+ {@const left = idx === 0}
115
+ <li bind:this={tabelements[idx]} use:offset={{ store: active ? activeStore : undefined }} id={$store.tabids[tab.name]} class="tabs-tab" class:left class:wrapping class:active class:hidden={tabIsHidden(idx)} style:font-size="{scalefactor}em" style:line-height={1.2 / scalefactor} aria-selected={active} aria-controls={$store.panelids[tab.name]} role="tab" tabindex={active ? 0 : -1} on:click={onClick(idx)} on:keydown={onKeyDown(idx)}><span><Icon icon={tab.icon} inline />{tab.title}</span></li>
97
116
  {/each}
117
+ {#if cols < $store.tabs.length}
118
+ <li class="overflow" role="presentation"><button bind:this={tabOverflowButton} type="button" tabindex="-1"><Icon icon={caretRightFill} hiddenLabel="More Tabs Menu" inline /></button></li>
119
+ <PopupMenu buttonelement={tabOverflowButton} items={hiddenTabs.map(t => ({ value: t.name }))} on:change={onOverflowChange}/>
120
+ {/if}
98
121
  </ul>
99
122
  <div bind:this={activeelement} class="tabs-active"></div>
100
123
  <slot current={$store.current} />
@@ -135,6 +158,22 @@ onMount(reactToCurrent);
135
158
  font-weight: 500;
136
159
  color: var(--tabs-text, #363534)
137
160
  }
161
+ :global(.tabs-tab.hidden) {
162
+ display: none;
163
+ }
164
+ li.overflow {
165
+ min-width: 2em;
166
+ display: flex;
167
+ align-items: center;
168
+ background-color: transparent !important;
169
+ padding: 0;
170
+ }
171
+ li.overflow button {
172
+ background: none;
173
+ cursor: pointer;
174
+ border: 0;
175
+ color: black;
176
+ }
138
177
  li:not(.left) {
139
178
  margin-left: -1px;
140
179
  }
@@ -158,20 +197,4 @@ onMount(reactToCurrent);
158
197
  border-radius: 2px;
159
198
  }
160
199
 
161
- /* .tabs-active {
162
- background: var(--dg-tabs-active, var(--dg-button-bg, #501214));
163
- height: 3px;
164
- border-radius: 2px;
165
- overflow: hidden;
166
- width: 0px;
167
- margin-top: calc(-1 * var(--tabs-padding-vert, 0.7em));
168
- margin-bottom: var(--tabs-padding-vert, 0.7em);
169
- transition: 0.2s transform;
170
- }
171
- @media (prefers-reduced-motion) {
172
- .tabs-active {
173
- transition: none;
174
- }
175
- } */
176
-
177
200
  </style>
@@ -142,4 +142,4 @@ $: reactToPreview($preview);
142
142
  }
143
143
  }
144
144
 
145
- </style>
145
+ </style>
@@ -33,7 +33,7 @@ onMount(reactToOptions);
33
33
  </script>
34
34
 
35
35
  <FieldStandard bind:id descid={groupid} {path} {label} {required} {defaultValue} {conditional} {helptext} let:value let:valid let:invalid let:onBlur let:onChange let:messagesid let:helptextid let:setVal>
36
- {@const _ = updateValue(value, setVal)}
36
+ {@const _ = void updateValue(value, setVal)}
37
37
  <div class="container-query-wrapper">
38
38
  <div class="color-container {className}" role="radiogroup" aria-labelledby={groupid} class:invalid class:valid>
39
39
  {#if addAllOption}
@@ -112,6 +112,7 @@ let focusWithin = false;
112
112
  {@const _ = init(value, setVal)}
113
113
  {#if isNotBlank(imageSrc)}
114
114
  <div on:focusin={() => { focusWithin = true }} on:focusout={() => { focusWithin = false }}>
115
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
115
116
  <div bind:this={container} use:resize on:resize={() => updateRect()} class="crop-image-container" on:mousedown={onMouseDown} on:touchstart={onMouseDown} on:touchmove={onMouseMove} style:cursor={$store.cursor}>
116
117
  <img class="crop-image" src={imageSrc} alt="" />
117
118
  {#if $selection && $outputPct}
@@ -119,6 +120,7 @@ let focusWithin = false;
119
120
  <img class='crop-image clipped' src={imageSrc} alt="" style:clip-path="polygon({$outputPct.left}% {$outputPct.top}%, {100 - $outputPct.right}% {$outputPct.top}%, {100 - $outputPct.right}% {100 - $outputPct.bottom}%, {$outputPct.left}% {100 - $outputPct.bottom}%, {$outputPct.left}% {$outputPct.top}%)" />
120
121
  </div>
121
122
  <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
123
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
122
124
  <div class='selectionHilite' {id} tabindex="0" on:keydown={onKeyDown('move')}
123
125
  aria-labelledby={descid}
124
126
  aria-describedby="{movedescid} {helptextid ?? ''}"
@@ -132,24 +134,28 @@ let focusWithin = false;
132
134
  <ScreenReaderOnly arialive="polite">top left x y coordinate is ({Math.round($selection.left)}, {Math.round($selection.top)}) bottom right x y coordinate is ({Math.round($selection.right)}, {Math.round($selection.bottom)})</ScreenReaderOnly>
133
135
  <ScreenReaderOnly arialive="polite">crop area is {Math.round($store.width)} pixels wide by {Math.round($store.height)} pixels tall</ScreenReaderOnly>
134
136
  {/if}
137
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
135
138
  <div class='selectionCorner tl'
136
139
  tabindex="0"
137
140
  on:keydown={onKeyDown('tl')}
138
141
  >
139
142
  <ScreenReaderOnly>arrows adjust crop size, bottom right is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
140
143
  </div>
144
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
141
145
  <div class='selectionCorner tr'
142
146
  tabindex="0"
143
147
  on:keydown={onKeyDown('tr')}
144
148
  >
145
149
  <ScreenReaderOnly>arrows adjust crop size, bottom left is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
146
150
  </div>
151
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
147
152
  <div class='selectionCorner bl'
148
153
  tabindex="0"
149
154
  on:keydown={onKeyDown('bl')}
150
155
  >
151
156
  <ScreenReaderOnly>arrows adjust crop size, top right is fixed, hold shift and/or cmd/alt for bigger steps</ScreenReaderOnly>
152
157
  </div>
158
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
153
159
  <div class='selectionCorner br'
154
160
  tabindex="0"
155
161
  on:keydown={onKeyDown('br')}
@@ -18,7 +18,7 @@ let selected = defaultValue;
18
18
  const iconToPrefix = {};
19
19
  const categoriesToIcons = keyby(IconCategories, 'key');
20
20
  for (const icon of FontAwesomeIcons) {
21
- iconToPrefix[icon.class] = icon.free.indexOf('brands') > -1 ? 'fab' : 'fas';
21
+ iconToPrefix[icon.class] = icon.free.includes('brands') ? 'fab' : 'fas';
22
22
  }
23
23
  const iconElements = [];
24
24
  let visibleIcons = FontAwesomeIcons;
@@ -104,8 +104,8 @@ function onKeyDown(e) {
104
104
  </script>
105
105
 
106
106
  <FieldStandard bind:id {path} {descid} {label} {required} {defaultValue} {conditional} {helptext} let:value let:valid let:invalid let:id let:onBlur let:setVal let:messagesid let:helptextid>
107
- <Icon icon={`${value.prefix === 'fab' ? 'fa-brands' : 'fa-solid'}:${value.icon?.slice(3) ?? 'graduation-cap'}`}/>
108
- <button type="button" id="btnSelectIcon" on:click={() => { modalOpen = true }} aria-describedby={getDescribedBy([descid, messagesid, helptextid])}>Select New Icon</button>
107
+ <Icon icon={`${value.prefix === 'fab' ? 'fa6-brands' : 'fa6-solid'}:${value.icon?.slice(3) ?? 'graduation-cap'}`}/>
108
+ <button type="button" id="btnSelectIcon" class="select-icon" on:click={() => { modalOpen = true }} aria-describedby={getDescribedBy([descid, messagesid, helptextid])}>Select New Icon</button>
109
109
  {#if modalOpen}
110
110
  <Modal>
111
111
  <section>
@@ -130,8 +130,9 @@ function onKeyDown(e) {
130
130
  <ScreenReaderOnly><legend class="sr-only">Icons</legend></ScreenReaderOnly>
131
131
  <div class="icon-picker-items" role="radiogroup">
132
132
  {#each visibleIcons as icon, idx (icon.class)}
133
- <div bind:this={iconElements[idx]} id={icon.class} class="icon-picker-item" role="radio" aria-checked={icon.class === selected.icon} tabindex={icon.class === selected.icon ? 0 : -1} data-index={idx} on:click={() => onSelectIcon(icon.class)} on:keydown={onKeyDown}>
134
- <Icon icon={`${iconToPrefix[icon.class] === 'fab' ? 'fa-brands' : 'fa-solid'}:${icon.class.slice(3)}`}/>
133
+
134
+ <div bind:this={iconElements[idx]} id={icon.class} class="icon-picker-item" role="radio" aria-checked={icon.class === selected.icon} tabindex={icon.class === selected.icon ? 0 : -1} data-index={idx} on:click={() => onSelectIcon(icon.class)} on:keydown={onKeyDown}>
135
+ <Icon icon={`${iconToPrefix[icon.class] === 'fab' ? 'fa6-brands' : 'fa6-solid'}:${icon.class.slice(3)}`}/>
135
136
  <ScreenReaderOnly>{icon.label}</ScreenReaderOnly>
136
137
  </div>
137
138
  {:else}
@@ -153,6 +154,11 @@ function onKeyDown(e) {
153
154
 
154
155
 
155
156
  <style>
157
+ .select-icon {
158
+ border-radius: 0.25em;
159
+ border: 1px solid #808080;
160
+ color: black;
161
+ }
156
162
  section {
157
163
  position: relative;
158
164
  background-color: #f4f4f4;