@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.
- package/dist/Container.svelte +14 -2
- package/dist/Container.svelte.d.ts +1 -0
- package/dist/FieldChoices.svelte +1 -1
- package/dist/FieldMultiple.svelte +19 -2
- package/dist/FieldMultiple.svelte.d.ts +7 -0
- package/dist/FieldRadio.svelte +1 -1
- package/dist/FieldStandard.svelte +1 -1
- package/dist/Form.svelte +41 -17
- package/dist/Icon.svelte +3 -1
- package/dist/Icon.svelte.d.ts +1 -0
- package/dist/InlineMessage.svelte +9 -18
- package/dist/Switcher.svelte +2 -1
- package/dist/Switcher.svelte.d.ts +1 -0
- package/dist/Tab.svelte +15 -4
- package/dist/TabStore.d.ts +5 -0
- package/dist/TabStore.js +28 -1
- package/dist/Tabs.svelte +11 -4
- package/dist/errorIcons.d.ts +6 -0
- package/dist/errorIcons.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/Container.svelte
CHANGED
|
@@ -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;
|
package/dist/FieldChoices.svelte
CHANGED
|
@@ -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>;
|
package/dist/FieldRadio.svelte
CHANGED
|
@@ -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 './
|
|
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
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
</
|
|
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
|
-
|
|
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>
|
package/dist/Icon.svelte.d.ts
CHANGED
|
@@ -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
|
|
5
|
-
import
|
|
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
|
-
|
|
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;
|
package/dist/Switcher.svelte
CHANGED
|
@@ -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}`}
|
package/dist/Tab.svelte
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
<script>import
|
|
2
|
-
import {
|
|
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>
|
package/dist/TabStore.d.ts
CHANGED
|
@@ -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
|
|
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,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