@dosgato/dialog 1.1.8 → 1.1.9

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.
@@ -4,10 +4,11 @@
4
4
  -->
5
5
  <script>import { eq, resize, ScreenReaderOnly } from '@txstate-mws/svelte-components';
6
6
  import { randomid } from 'txstate-utils';
7
- import { getContext } from 'svelte';
7
+ import { getContext, onDestroy } from 'svelte';
8
8
  import { DG_DIALOG_FIELD_MULTIPLE } from './FieldMultiple.svelte';
9
9
  import InlineMessages from './InlineMessages.svelte';
10
- import { getDescribedBy } from './';
10
+ import { getDescribedBy, TAB_CONTEXT, TAB_NAME_CONTEXT } from './';
11
+ export let path;
11
12
  /** A label for the Container `<div>`. */
12
13
  export let label;
13
14
  export let messages;
@@ -35,6 +36,17 @@ function setNeedsShowHelp(..._) {
35
36
  showhelp = false;
36
37
  }
37
38
  $: setNeedsShowHelp(helpelement);
39
+ const tabContext = getContext(TAB_CONTEXT);
40
+ const tabNameStore = getContext(TAB_NAME_CONTEXT);
41
+ $: if (messages.length) {
42
+ tabContext?.store.notifyErrorPath($tabNameStore, path);
43
+ }
44
+ else {
45
+ tabContext?.store.notifyErrorPathGone(path);
46
+ }
47
+ onDestroy(() => {
48
+ tabContext?.store.notifyErrorPathGone(path);
49
+ });
38
50
  </script>
39
51
 
40
52
  {#if conditional !== false}
@@ -2,6 +2,7 @@ import { SvelteComponentTyped } from "svelte";
2
2
  import type { Feedback } from '@txstate-mws/svelte-forms';
3
3
  declare const __propDef: {
4
4
  props: {
5
+ path: string;
5
6
  /** A label for the Container `<div>`. */ label: string;
6
7
  messages: Feedback[];
7
8
  /** If the input that's being built has an id pass it here so the label can point at it. */ id?: string | undefined;
@@ -65,7 +65,7 @@ onMount(reactToChoices);
65
65
 
66
66
  <Field {path} {defaultValue} {conditional} let:path let:value let:onBlur let:setVal let:messages let:valid let:invalid serialize={arraySerialize}>
67
67
  {@const _ = updateValue(value, setVal)}
68
- <Container {id} {label} {messages} {descid} {related} {helptext} let:messagesid let:helptextid>
68
+ <Container {path} {id} {label} {messages} {descid} {related} {helptext} let:messagesid let:helptextid>
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}`}
@@ -26,6 +26,14 @@ export let maxedText = addMoreText;
26
26
  export let addMoreClass = undefined;
27
27
  export let related = 0;
28
28
  export let helptext = undefined;
29
+ /**
30
+ * If you want to ask users if they're sure before removing an array element, fill this
31
+ * prop with the question that should be in the confirmation dialog.
32
+ *
33
+ * Alternatively, you can provide a function to generate the question from the item being
34
+ * deleted. e.g. (item) => `Are you sure you want to delete ${item.name}?`
35
+ */
36
+ export let confirmDelete = undefined;
29
37
  const fieldMultipleContext = { helptextid: undefined };
30
38
  setContext(DG_DIALOG_FIELD_MULTIPLE, fieldMultipleContext);
31
39
  const inheritedPath = getContext(FORM_INHERITED_PATH);
@@ -56,10 +64,19 @@ function moveDownAndFocus(onMove, idx) {
56
64
  }
57
65
  };
58
66
  }
67
+ function confirmedDelete(onDelete, item) {
68
+ return () => {
69
+ if (confirmDelete == null)
70
+ return onDelete();
71
+ const msg = typeof confirmDelete === 'string' ? confirmDelete : confirmDelete(item);
72
+ if (confirm(msg))
73
+ onDelete();
74
+ };
75
+ }
59
76
  $: messages = compact ? $messageStore : [];
60
77
  </script>
61
78
 
62
- <Container {label} {messages} {conditional} {related} {helptext} let:helptextid>
79
+ <Container {path} {label} {messages} {conditional} {related} {helptext} let:helptextid>
63
80
  {noOp(fieldMultipleContext.helptextid = helptextid)}
64
81
  <AddMore {path} {initialState} {minLength} {maxLength} {conditional} let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp let:onMoveDown>
65
82
  {@const showDelete = removable && !minned}
@@ -73,7 +90,7 @@ $: messages = compact ? $messageStore : [];
73
90
  <button bind:this={reorderdownelements[index]} class="dialog-multiple-move" type="button" disabled={index === currentLength - 1} on:click|preventDefault|stopPropagation={moveDownAndFocus(onMoveDown, index)}><Icon icon={caretCircleDown} hiddenLabel="move down in the list" /></button>
74
91
  <button bind:this={reorderupelements[index]} class="dialog-multiple-move" type="button" disabled={index === 0} on:click|preventDefault|stopPropagation={moveUpAndFocus(onMoveUp, index)}><Icon icon={caretCircleUp} hiddenLabel="move up in the list" /></button>
75
92
  {/if}
76
- {#if showDelete}<button class="dialog-multiple-delete" type="button" on:click|preventDefault|stopPropagation={onDelete}><Icon icon={xCircle} hiddenLabel="remove from list" /></button>{/if}
93
+ {#if showDelete}<button class="dialog-multiple-delete" type="button" on:click|preventDefault|stopPropagation={confirmedDelete(onDelete, value)}><Icon icon={xCircle} hiddenLabel="remove from list" /></button>{/if}
77
94
  </div>{/if}
78
95
  </div>
79
96
  <svelte:fragment slot="addbutton" let:maxed let:onClick>
@@ -16,6 +16,13 @@ declare const __propDef: {
16
16
  addMoreClass?: string | undefined;
17
17
  related?: number | true | undefined;
18
18
  helptext?: string | undefined;
19
+ /**
20
+ * If you want to ask users if they're sure before removing an array element, fill this
21
+ * prop with the question that should be in the confirmation dialog.
22
+ *
23
+ * Alternatively, you can provide a function to generate the question from the item being
24
+ * deleted. e.g. (item) => `Are you sure you want to delete ${item.name}?`
25
+ */ confirmDelete?: string | ((item: any) => string) | undefined;
19
26
  };
20
27
  events: {
21
28
  [evt: string]: CustomEvent<any>;
@@ -43,5 +43,5 @@ onMount(reactToChoices);
43
43
 
44
44
  <Field {path} {defaultValue} {conditional} {notNull} {number} {date} {datetime} {boolean} {serialize} {deserialize} let:value let:valid let:invalid let:onBlur let:onChange let:messages let:serialize let:setVal let:deserialize>
45
45
  {@const _ = updateValue(value, setVal, deserialize)}
46
- <Switcher bind:id class={className} name={path} {horizontal} {label} iptValue={value} {valid} {invalid} {required} {related} {extradescid} {helptext} {messages} on:change={onChange} {onBlur} choices={choices.map(c => ({ ...c, value: serialize(c.value) }))} />
46
+ <Switcher bind:id {path} class={className} name={path} {horizontal} {label} iptValue={value} {valid} {invalid} {required} {related} {extradescid} {helptext} {messages} on:change={onChange} {onBlur} choices={choices.map(c => ({ ...c, value: serialize(c.value) }))} />
47
47
  </Field>
@@ -36,7 +36,7 @@ export let helptext = undefined;
36
36
  </script>
37
37
 
38
38
  <Field {path} {defaultValue} {conditional} {notNull} {number} {date} {datetime} {boolean} {serialize} {deserialize} {initialize} {finalize} let:path let:value let:onBlur let:onChange let:setVal let:messages let:valid let:invalid let:serialize let:deserialize>
39
- <Container {id} {label} {messages} {descid} {required} {related} {helptext} let:messagesid let:helptextid>
39
+ <Container {path} {id} {label} {messages} {descid} {required} {related} {helptext} let:messagesid let:helptextid>
40
40
  <slot {id} {path} {value} {onBlur} {onChange} {setVal} {valid} {invalid} {messagesid} {helptextid} {serialize} {deserialize} />
41
41
  </Container>
42
42
  </Field>
package/dist/Form.svelte CHANGED
@@ -1,6 +1,7 @@
1
1
  <script>import { Form } from '@txstate-mws/svelte-forms';
2
2
  import { setContext } from 'svelte';
3
- import { CHOOSER_API_CONTEXT } from './chooser';
3
+ import { CHOOSER_API_CONTEXT, messageIcons } from './';
4
+ import Icon from './Icon.svelte';
4
5
  let className = '';
5
6
  export { className as class };
6
7
  export let submit = undefined;
@@ -15,21 +16,32 @@ setContext(CHOOSER_API_CONTEXT, chooserClient);
15
16
 
16
17
  <Form bind:store class="{className} dialog-form" {submit} {validate} on:saved {autocomplete} {name} {preload} let:messages let:allMessages let:showingInlineErrors let:saved let:valid let:invalid let:validating let:submitting let:data>
17
18
  <slot {messages} {saved} {validating} {submitting} {valid} {invalid} {data} {allMessages} {showingInlineErrors} />
18
- {#if messages.length || showingInlineErrors}
19
- <div class="form-errors" aria-live='assertive'>
20
- {#if messages.length}
21
- <ul>
22
- {#each messages as message}
23
- <li>{message.message}</li>
24
- {/each}
25
- {#if showingInlineErrors}
26
- <li>More errors. See inline messages for details.</li>
27
- {/if}
28
- </ul>
29
- {:else if showingInlineErrors}
30
- This form contains validation errors. See inline messages for details.
19
+ {@const errorMessages = messages.filter(m => m.type === 'error' || m.type === 'system')}
20
+ {@const warningMessages = messages.filter(m => m.type === 'warning')}
21
+ {@const successMessages = messages.filter(m => m.type === 'success')}
22
+ {#if errorMessages.length || showingInlineErrors}
23
+ <ul class="form-errors" aria-live='assertive'>
24
+ {#each errorMessages as message}
25
+ <li><Icon icon={messageIcons[message.type] ?? messageIcons.error} inline hiddenLabel="error"/> {message.message}</li>
26
+ {/each}
27
+ {#if showingInlineErrors}
28
+ <li><Icon icon={messageIcons.error} inline /> {#if errorMessages.length}More errors.{:else}This form contains validation errors.{/if} See inline messages for details.</li>
31
29
  {/if}
32
- </div>
30
+ </ul>
31
+ {/if}
32
+ {#if warningMessages.length}
33
+ <ul class="form-warnings" aria-live='assertive'>
34
+ {#each warningMessages as message}
35
+ <li><Icon icon={messageIcons.warning} inline hiddenLabel="warning"/> {message.message}</li>
36
+ {/each}
37
+ </ul>
38
+ {/if}
39
+ {#if successMessages.length}
40
+ <ul class="form-successes" aria-live='assertive'>
41
+ {#each successMessages as message}
42
+ <li><Icon icon={messageIcons.success} inline hiddenLabel="success" /> {message.message}</li>
43
+ {/each}
44
+ </ul>
33
45
  {/if}
34
46
  <slot name="submit" {saved} {validating} {submitting} {valid} {invalid} {allMessages} {showingInlineErrors} />
35
47
  </Form>
@@ -44,9 +56,21 @@ setContext(CHOOSER_API_CONTEXT, chooserClient);
44
56
  :global(.dialog-content > .dialog-form) {
45
57
  margin: -2em 0;
46
58
  }
59
+ ul {
60
+ list-style-type: none;
61
+ padding: 0.5em;
62
+ }
47
63
  .form-errors {
48
- color: var(--dg-danger-bg, #9a3332);
49
- padding: 1em;
64
+ background-color: var(--dg-danger-bg, #9a3332);
65
+ color: var(--dg-danger-text, white);
66
+ }
67
+ .form-warnings {
68
+ background-color: var(--dg-warning-bg, #ffc107);
69
+ color: var(--dg-warning-text, black);
70
+ }
71
+ .form-successes {
72
+ background-color: var(--dg-success-bg, #218739);
73
+ color: var(--dg-success-text, white);
50
74
  }
51
75
 
52
76
  </style>
package/dist/Icon.svelte CHANGED
@@ -12,6 +12,8 @@ export let inline = false;
12
12
  export let width = undefined;
13
13
  export let height = undefined;
14
14
  export let tooltip = undefined;
15
+ let className = undefined;
16
+ export { className as class };
15
17
  function replaceIDs(body) {
16
18
  const matches = body.matchAll(/\sid="(\S+)"/g);
17
19
  const ids = Array.from(matches).map(m => m[1]);
@@ -41,7 +43,7 @@ height ??= width;
41
43
 
42
44
  {#if icon}
43
45
  <Tooltip tip={tooltip} top>
44
- <svg role="img" viewBox="{icon.left ?? 0} {icon.top ?? 0} {icon.width ?? 256} {icon.height ?? 256}" class:vFlip={icon.vFlip} class:hFlip={icon.hFlip} class:inline {width} {height} aria-hidden={!hiddenLabel} aria-label={hiddenLabel} xmlns="http://www.w3.org/2000/svg">
46
+ <svg role="img" class={className} viewBox="{icon.left ?? 0} {icon.top ?? 0} {icon.width ?? 256} {icon.height ?? 256}" class:vFlip={icon.vFlip} class:hFlip={icon.hFlip} class:inline {width} {height} aria-hidden={!hiddenLabel} aria-label={hiddenLabel} xmlns="http://www.w3.org/2000/svg">
45
47
  {@html svgBody(icon)}
46
48
  </svg>
47
49
  </Tooltip>
@@ -8,6 +8,7 @@ declare const __propDef: {
8
8
  width?: string | number | undefined;
9
9
  height?: string | number | undefined;
10
10
  tooltip?: string | undefined;
11
+ class?: string | undefined;
11
12
  };
12
13
  events: {
13
14
  [evt: string]: CustomEvent<any>;
@@ -1,20 +1,11 @@
1
1
  <!-- @component
2
2
  The purpose of this component is to provide common `Feedback` message styling with icons that support screen readers.
3
3
  -->
4
- <script>import alertCircleOutline from '@iconify-icons/mdi/alert-circle-outline.js';
5
- import checkCircleOutline from '@iconify-icons/mdi/check-circle-outline.js';
6
- import informationOutline from '@iconify-icons/mdi/information-outline.js';
7
- import closeOctagonOutline from '@iconify-icons/mdi/close-octagon-outline.js';
8
- import { htmlEncode } from 'txstate-utils';
4
+ <script>import { htmlEncode } from 'txstate-utils';
5
+ import { messageIcons } from './';
9
6
  import Icon from './Icon.svelte';
10
7
  export let message;
11
- const icons = {
12
- error: alertCircleOutline,
13
- warning: informationOutline,
14
- success: checkCircleOutline,
15
- system: closeOctagonOutline
16
- };
17
- $: icon = icons[message.type] ?? alertCircleOutline;
8
+ $: icon = messageIcons[message.type] ?? messageIcons.error;
18
9
  // Would we like to add something like the following for non-Error message types being used in aria descriptions?
19
10
  /*
20
11
  const ariaLables = {
@@ -67,16 +58,16 @@ function addMarkup(msg) {
67
58
  margin-left: 0.5em;
68
59
  }
69
60
  div.error, div.system {
70
- background-color: #9a3332;
71
- color: white;
61
+ background-color: var(--dg-danger-bg, #9a3332);
62
+ color: var(--dg-danger-text, white);
72
63
  }
73
64
  div.warning {
74
- background-color: #ffc107;
75
- color: black;
65
+ background-color: var(--dg-warning-bg, #ffc107);
66
+ color: var(--dg-warning-text, black);
76
67
  }
77
68
  div.success {
78
- background-color: #218739;
79
- color: white;
69
+ background-color: var(--dg-success-bg, #218739);
70
+ color: var(--dg-success-text, white);
80
71
  }
81
72
  div :global(a) {
82
73
  color: inherit;
@@ -7,6 +7,7 @@ import Radio from './Radio.svelte';
7
7
  let className = '';
8
8
  export { className as class };
9
9
  export let id = randomid();
10
+ export let path = '';
10
11
  export let name = randomid();
11
12
  export let choices;
12
13
  export let horizontal = false;
@@ -30,7 +31,7 @@ function onChange(e) {
30
31
  $: columns = Math.floor($store.width / 250);
31
32
  $: width = (horizontal ? 100 / Math.min(choices.length, choices.length === 4 && columns === 3 ? 2 : columns) : 100) + '%';
32
33
  </script>
33
- <Container {id} {label} {messages} descid={groupid} {required} {related} {helptext} let:helptextid>
34
+ <Container {path} {id} {label} {messages} descid={groupid} {required} {related} {helptext} let:helptextid>
34
35
  <div class="dialog-radio {className}" use:eq={{ store }} class:horizontal role="radiogroup" aria-labelledby={groupid} class:valid class:invalid>
35
36
  {#each choices as choice, idx (choice.value)}
36
37
  {@const radioid = `${groupid}.${idx}`}
@@ -4,6 +4,7 @@ declare const __propDef: {
4
4
  props: {
5
5
  class?: string | undefined;
6
6
  id?: string | undefined;
7
+ path?: string | undefined;
7
8
  name?: string | undefined;
8
9
  choices: {
9
10
  label?: string | undefined;
package/dist/Tab.svelte CHANGED
@@ -1,7 +1,9 @@
1
- <script>import { derivedStore } from '@txstate-mws/svelte-store';
2
- import { getContext } from 'svelte';
1
+ <script>import warningCircleFill from '@iconify-icons/ph/warning-circle-fill';
2
+ import { derivedStore } from '@txstate-mws/svelte-store';
3
+ import { getContext, setContext } from 'svelte';
4
+ import { writable } from 'svelte/store';
3
5
  import Icon from './Icon.svelte';
4
- import { TAB_CONTEXT } from './TabStore';
6
+ import { TAB_CONTEXT, TAB_NAME_CONTEXT } from './TabStore';
5
7
  export let name;
6
8
  const { store, onClick, onKeyDown, tabelements } = getContext(TAB_CONTEXT);
7
9
  const accordion = store.accordion();
@@ -13,11 +15,14 @@ const panelid = derivedStore(store, v => v.panelids[name]);
13
15
  $: active = $current === name;
14
16
  const idx = $store.tabs.findIndex(t => t.name === name);
15
17
  const last = idx === $store.tabs.length - 1;
18
+ const tabNameStore = writable(name);
19
+ $: $tabNameStore = name;
20
+ setContext(TAB_NAME_CONTEXT, tabNameStore);
16
21
  </script>
17
22
 
18
23
  {#if $store.tabs.length > 1}
19
24
  {#if $accordion}
20
- <div bind:this={tabelements[idx]} id={$tabid} class="tabs-tab" class:last aria-selected={active} aria-controls={$panelid} role="tab" tabindex={0} on:click={onClick(idx)} on:keydown={onKeyDown(idx)}><Icon icon={$store.tabs[idx].icon} inline />{$title}<i class="tabs-accordion-arrow" aria-hidden="true"></i></div>
25
+ <div bind:this={tabelements[idx]} id={$tabid} class="tabs-tab" class:last aria-selected={active} aria-controls={$panelid} role="tab" tabindex={0} on:click={onClick(idx)} on:keydown={onKeyDown(idx)}><Icon icon={$store.tabs[idx].icon} inline />{$title}{#if $store.hasError[name] && !active} <Icon icon={warningCircleFill} inline class='errorIcon' />{/if}<i class="tabs-accordion-arrow" aria-hidden="true"></i></div>
21
26
  {/if}
22
27
  <div id={$panelid} hidden={!active} role="tabpanel" tabindex="-1" aria-labelledby={$tabid} class="tabs-panel" class:accordion={$accordion}>
23
28
  <slot />
@@ -64,5 +69,11 @@ const last = idx === $store.tabs.length - 1;
64
69
  transform: translateY(-50%) rotate(225deg);
65
70
  top: calc(50% + 0.08em);
66
71
  }
72
+ .tabs-tab :global(.errorIcon) {
73
+ color: var(--dg-danger-bg, #9a3332);
74
+ font-size: 1.2em;
75
+ margin-left: 0.4em;
76
+ }
77
+
67
78
 
68
79
  </style>
@@ -2,6 +2,7 @@ import { Store } from '@txstate-mws/svelte-store';
2
2
  import type { ElementSize } from '@txstate-mws/svelte-components';
3
3
  import type { IconifyIcon } from '@iconify/svelte';
4
4
  export declare const TAB_CONTEXT: {};
5
+ export declare const TAB_NAME_CONTEXT: {};
5
6
  export interface TabDef {
6
7
  name: string;
7
8
  title?: string;
@@ -18,6 +19,8 @@ interface ITabStore extends ElementSize {
18
19
  tabids: Record<string, string>;
19
20
  panelids: Record<string, string>;
20
21
  accordionOnMobile: boolean;
22
+ errorPaths: Record<string, string[] | undefined>;
23
+ hasError: Record<string, boolean | undefined>;
21
24
  }
22
25
  export declare class TabStore extends Store<ITabStore> {
23
26
  initialTab?: string | undefined;
@@ -35,5 +38,7 @@ export declare class TabStore extends Store<ITabStore> {
35
38
  end(): void;
36
39
  activate(idx: number): void;
37
40
  activateName(name: string): void;
41
+ notifyErrorPath(tabname: string, path: string): void;
42
+ notifyErrorPathGone(path: string): void;
38
43
  }
39
44
  export {};
package/dist/TabStore.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Store, derivedStore } from '@txstate-mws/svelte-store';
2
2
  import { findIndex, randomid } from 'txstate-utils';
3
3
  export const TAB_CONTEXT = {};
4
+ export const TAB_NAME_CONTEXT = {};
4
5
  function checkNext(v) {
5
6
  v.visited = { ...v.visited, [v.tabs[v.current].name]: true };
6
7
  v.prevTitle = v.current > 0 ? v.tabs[v.current - 1].title : undefined;
@@ -20,7 +21,9 @@ export class TabStore extends Store {
20
21
  tabids: tabs.reduce((acc, curr) => ({ ...acc, [curr.name]: randomid() }), {}),
21
22
  panelids: tabs.reduce((acc, curr) => ({ ...acc, [curr.name]: randomid() }), {}),
22
23
  clientWidth: 1024,
23
- accordionOnMobile: true
24
+ accordionOnMobile: true,
25
+ errorPaths: {},
26
+ hasError: {}
24
27
  }));
25
28
  this.initialTab = initialTab;
26
29
  }
@@ -70,4 +73,28 @@ export class TabStore extends Store {
70
73
  activateName(name) {
71
74
  this.update(v => ({ ...v, current: findIndex(v.tabs, t => t.name === name) ?? v.current }));
72
75
  }
76
+ notifyErrorPath(tabname, path) {
77
+ this.update(v => {
78
+ const errorPaths = {};
79
+ for (const k of Object.keys(v.errorPaths))
80
+ errorPaths[k] = v.errorPaths[k]?.filter(p => p !== path);
81
+ errorPaths[tabname] ??= [];
82
+ errorPaths[tabname].push(path);
83
+ const hasError = {};
84
+ for (const k of Object.keys(errorPaths))
85
+ hasError[k] = !!errorPaths[k].length;
86
+ return { ...v, errorPaths, hasError };
87
+ });
88
+ }
89
+ notifyErrorPathGone(path) {
90
+ this.update(v => {
91
+ const errorPaths = {};
92
+ for (const k of Object.keys(v.errorPaths))
93
+ errorPaths[k] = v.errorPaths[k]?.filter(p => p !== path);
94
+ const hasError = {};
95
+ for (const k of Object.keys(errorPaths))
96
+ hasError[k] = !!errorPaths[k].length;
97
+ return { ...v, errorPaths, hasError };
98
+ });
99
+ }
73
100
  }
package/dist/Tabs.svelte CHANGED
@@ -1,11 +1,12 @@
1
- <script>import { modifierKey, resize, offset, PopupMenu } from '@txstate-mws/svelte-components';
1
+ <script>import caretRightFill from '@iconify-icons/ph/caret-right-fill';
2
+ import warningCircleFill from '@iconify-icons/ph/warning-circle-fill';
3
+ import { modifierKey, resize, offset, PopupMenu } from '@txstate-mws/svelte-components';
2
4
  import { Store } from '@txstate-mws/svelte-store';
3
5
  import { getContext, onMount, setContext, tick } from 'svelte';
4
6
  import { roundTo } from 'txstate-utils';
5
7
  import { DIALOG_TABS_CONTEXT } from './Dialog.svelte';
6
8
  import Icon from './Icon.svelte';
7
9
  import { TabStore, TAB_CONTEXT } from './TabStore';
8
- import caretRightFill from '@iconify-icons/ph/caret-right-fill';
9
10
  export let tabs;
10
11
  export let active = undefined;
11
12
  export let store = new TabStore(tabs, active);
@@ -117,7 +118,7 @@ onMount(reactToCurrent);
117
118
  {#each $store.tabs as tab, idx (tab.name)}
118
119
  {@const active = isActive(idx, $store.current)}
119
120
  {@const left = idx === 0}
120
- <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>
121
+ <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}{#if $store.hasError[tab.name] && !active} <Icon icon={warningCircleFill} inline class='errorIcon' />{/if}</span></li>
121
122
  {/each}
122
123
  {#if cols < $store.tabs.length}
123
124
  <li class="overflow" role="presentation"><button bind:this={tabOverflowButton} type="button" tabindex="-1"><Icon icon={caretRightFill} hiddenLabel="More Tabs Menu" inline /></button></li>
@@ -161,7 +162,13 @@ onMount(reactToCurrent);
161
162
  word-break: break-word;
162
163
  text-transform: uppercase;
163
164
  font-weight: 500;
164
- color: var(--tabs-text, #363534)
165
+ color: var(--tabs-text, #363534);
166
+ }
167
+ li :global(.errorIcon) {
168
+ color: var(--dg-danger-bg, #9a3332);
169
+ font-size: 1.2em;
170
+ margin-left: 0.4em;
171
+ vertical-align: -0.25em;
165
172
  }
166
173
  :global(.tabs-tab.hidden) {
167
174
  display: none;
@@ -0,0 +1,6 @@
1
+ export declare const messageIcons: {
2
+ error: import("@iconify/types").IconifyIcon;
3
+ warning: import("@iconify/types").IconifyIcon;
4
+ success: import("@iconify/types").IconifyIcon;
5
+ system: import("@iconify/types").IconifyIcon;
6
+ };
@@ -0,0 +1,10 @@
1
+ import alertCircleOutline from '@iconify-icons/mdi/alert-circle-outline';
2
+ import checkCircleOutline from '@iconify-icons/mdi/check-circle-outline';
3
+ import closeOctagonOutline from '@iconify-icons/mdi/close-octagon-outline';
4
+ import informationOutline from '@iconify-icons/mdi/information-outline';
5
+ export const messageIcons = {
6
+ error: alertCircleOutline,
7
+ warning: informationOutline,
8
+ success: checkCircleOutline,
9
+ system: closeOctagonOutline
10
+ };
package/dist/index.d.ts CHANGED
@@ -34,6 +34,7 @@ export { default as Tab } from './Tab.svelte';
34
34
  export { default as Tabs } from './Tabs.svelte';
35
35
  export * from './chooser/index.js';
36
36
  export * from './fileIcons.js';
37
+ export * from './errorIcons.js';
37
38
  export * from './TabStore.js';
38
39
  export * from './iconpicker/index.js';
39
40
  export * from './colorpicker/index.js';
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ export { default as Tab } from './Tab.svelte';
34
34
  export { default as Tabs } from './Tabs.svelte';
35
35
  export * from './chooser/index.js';
36
36
  export * from './fileIcons.js';
37
+ export * from './errorIcons.js';
37
38
  export * from './TabStore.js';
38
39
  export * from './iconpicker/index.js';
39
40
  export * from './colorpicker/index.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dosgato/dialog",
3
3
  "description": "A component library for building forms that edit a JSON document.",
4
- "version": "1.1.8",
4
+ "version": "1.1.9",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",