@dosgato/dialog 0.0.18 → 0.0.20

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.
Files changed (58) hide show
  1. package/Button.svelte +77 -0
  2. package/Button.svelte.d.ts +30 -0
  3. package/Checkbox.svelte +2 -1
  4. package/Checkbox.svelte.d.ts +1 -0
  5. package/Container.svelte +18 -19
  6. package/Container.svelte.d.ts +2 -0
  7. package/Dialog.svelte +137 -0
  8. package/Dialog.svelte.d.ts +49 -0
  9. package/FieldAutocomplete.svelte +4 -7
  10. package/FieldAutocomplete.svelte.d.ts +1 -1
  11. package/FieldCheckbox.svelte +3 -2
  12. package/FieldCheckbox.svelte.d.ts +2 -1
  13. package/FieldChoices.svelte +3 -2
  14. package/FieldChoices.svelte.d.ts +1 -0
  15. package/FieldChooserLink.svelte +4 -2
  16. package/FieldChooserLink.svelte.d.ts +1 -0
  17. package/FieldDate.svelte +3 -2
  18. package/FieldDate.svelte.d.ts +1 -0
  19. package/FieldDateTime.svelte +3 -2
  20. package/FieldDateTime.svelte.d.ts +1 -0
  21. package/FieldMultiple.svelte +14 -7
  22. package/FieldMultiple.svelte.d.ts +3 -0
  23. package/FieldMultiselect.svelte +29 -22
  24. package/FieldNumber.svelte +3 -2
  25. package/FieldNumber.svelte.d.ts +1 -0
  26. package/FieldRadio.svelte +9 -4
  27. package/FieldRadio.svelte.d.ts +6 -0
  28. package/FieldSelect.svelte +9 -3
  29. package/FieldSelect.svelte.d.ts +6 -0
  30. package/FieldStandard.svelte +8 -3
  31. package/FieldStandard.svelte.d.ts +6 -0
  32. package/FieldText.svelte +3 -2
  33. package/FieldText.svelte.d.ts +1 -0
  34. package/FieldTextArea.svelte +5 -3
  35. package/FieldTextArea.svelte.d.ts +1 -0
  36. package/Form.svelte +3 -3
  37. package/FormDialog.svelte +34 -0
  38. package/FormDialog.svelte.d.ts +38 -0
  39. package/Input.svelte +4 -1
  40. package/Input.svelte.d.ts +1 -0
  41. package/Listbox.svelte +1 -0
  42. package/Radio.svelte +5 -2
  43. package/Radio.svelte.d.ts +1 -0
  44. package/Tab.svelte +4 -3
  45. package/TabStore.d.ts +8 -1
  46. package/TabStore.js +18 -3
  47. package/Tabs.svelte +65 -18
  48. package/Tabs.svelte.d.ts +2 -4
  49. package/chooser/Chooser.svelte +1 -1
  50. package/chooser/Details.svelte +1 -1
  51. package/colorpicker/{FieldColorPIcker.svelte → FieldColorPicker.svelte} +0 -0
  52. package/fileIcons.d.ts +2 -1
  53. package/helpers.d.ts +1 -0
  54. package/helpers.js +5 -0
  55. package/iconpicker/FieldIconPicker.svelte +6 -6
  56. package/index.d.ts +4 -0
  57. package/index.js +4 -0
  58. package/package.json +14 -10
@@ -1,7 +1,9 @@
1
- <script>import { AddMore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
1
+ <script>import plusCircleLight from '@iconify-icons/ph/plus-circle-light';
2
+ import { AddMore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
2
3
  import { derivedStore } from '@txstate-mws/svelte-store';
3
4
  import { getContext } from 'svelte';
4
5
  import { isNotNull } from 'txstate-utils';
6
+ import Button from './Button.svelte';
5
7
  import Container from './Container.svelte';
6
8
  export let path;
7
9
  export let label;
@@ -12,6 +14,9 @@ export let compact = false;
12
14
  export let removable = false;
13
15
  export let reorder = false;
14
16
  export let conditional = undefined;
17
+ export let addMoreText = 'Add';
18
+ export let maxedText = addMoreText;
19
+ export let addMoreClass = undefined;
15
20
  const inheritedPath = getContext(FORM_INHERITED_PATH);
16
21
  const finalPath = [inheritedPath, path].filter(isNotNull).join('.');
17
22
  const store = getContext(FORM_CONTEXT);
@@ -27,7 +32,7 @@ $: messages = compact ? $messageStore : [];
27
32
  </script>
28
33
 
29
34
  <Container {label} {messages}>
30
- <AddMore {path} {initialState} {minLength} {maxLength} {conditional} addMoreClass="dialog-multiple-button" let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp>
35
+ <AddMore {path} {initialState} {minLength} {maxLength} {conditional} let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp>
31
36
  {@const showDelete = removable && !minned}
32
37
  {@const showMove = reorder && index > 0}
33
38
  <div class="dialog-multiple" class:has-delete-icon={showDelete}>
@@ -39,29 +44,31 @@ $: messages = compact ? $messageStore : [];
39
44
  {#if showDelete}<button class="dialog-multiple-delete" type="button" on:click|preventDefault|stopPropagation={onDelete}>X</button>{/if}
40
45
  </div>{/if}
41
46
  </div>
47
+ <svelte:fragment slot="addbutton" let:maxed let:onClick>
48
+ <Button type="button" icon={plusCircleLight} class="{addMoreClass} dialog-multiple-button" disabled={maxed} on:click={onClick}>{maxed ? maxedText : addMoreText}</Button>
49
+ </svelte:fragment>
42
50
  </AddMore>
43
51
  </Container>
44
52
 
45
53
  <style>
46
54
  .dialog-multiple {
47
55
  position: relative;
48
- border: var(--dialog-container-border, 1px solid #999999);
56
+ border: var(--dialog-container-border, 0);
49
57
  padding: var(--dialog-container-padding, 1em);
50
58
  }
51
59
  .dialog-multiple:not(:first-child) {
52
60
  border-top: 0;
53
61
  }
54
62
  .dialog-multiple:nth-of-type(even) {
55
- background-color: var(--dialog-field-bg1, #e6e6e6);
63
+ background-color: var(--dialog-field-bg1, transparent);
56
64
  color: var(--dialog-field-text1, inherit);
57
65
  }
58
66
  .dialog-multiple:nth-of-type(odd) {
59
- background-color: var(--dialog-field-bg2, #ffffff);
67
+ background-color: var(--dialog-field-bg2, transparent);
60
68
  color: var(--dialog-field-text2, inherit);
61
69
  }
62
70
  :global(.dialog-multiple-button) {
63
- padding: 0.3em 0.4em;
64
- margin-top: 0.5em;
71
+ margin-left: var(--dialog-container-padding, 1em);
65
72
  }
66
73
  .dialog-multiple.has-delete-icon {
67
74
  display: flex;
@@ -10,6 +10,9 @@ declare const __propDef: {
10
10
  removable?: boolean;
11
11
  reorder?: boolean;
12
12
  conditional?: boolean | undefined;
13
+ addMoreText?: string;
14
+ maxedText?: string;
15
+ addMoreClass?: string;
13
16
  };
14
17
  events: {
15
18
  [evt: string]: CustomEvent<any>;
@@ -1,5 +1,8 @@
1
1
  <script>import { MultiSelect } from '@txstate-mws/svelte-components';
2
+ import { Store } from '@txstate-mws/svelte-store';
2
3
  import FieldStandard from './FieldStandard.svelte';
4
+ import { onMount } from 'svelte';
5
+ import { isNotBlank } from 'txstate-utils';
3
6
  export let id = undefined;
4
7
  export let path;
5
8
  export let label = '';
@@ -11,42 +14,46 @@ export let required = false;
11
14
  export let getOptions;
12
15
  // each time we run getOptions we will save the value -> label mappings
13
16
  // that it finds, so that we can display labels on pills
14
- let valueToLabel = {};
17
+ const valueToLabel = {};
15
18
  async function wrapGetOptions(search) {
16
- let opts = await getOptions(search);
19
+ const opts = await getOptions(search);
17
20
  // if no options are returned with the search term, we can end up with an infinite loop
18
21
  // the first time reactToValue calls wrapGetOptions
19
- if (opts.length === 0) {
20
- opts = await getOptions('');
21
- }
22
- for (const opt of opts)
22
+ // if (opts.length === 0) {
23
+ // opts = await getOptions('')
24
+ // }
25
+ for (const opt of opts) {
23
26
  valueToLabel[opt.value] = opt.label || opt.value;
24
- valueToLabel = valueToLabel;
27
+ }
25
28
  return opts;
26
29
  }
27
- function valueToSelected(value, valueToLabel) {
28
- return value.map(v => ({ value: v, label: valueToLabel[v] }));
29
- }
30
+ const selectedStore = new Store([]);
31
+ let hasInit = !defaultValue.length;
32
+ onMount(async () => {
33
+ await reactToValue(defaultValue);
34
+ hasInit = true;
35
+ });
30
36
  // if we get a value from the form that we haven't seen in the popup menu
31
37
  // yet, we won't have a label for it
32
38
  // this function runs getOptions on any selected values for which the label
33
39
  // is currently unknown
34
- function reactToValue(value) {
35
- if (value == null)
36
- return;
37
- for (const v of value) {
40
+ async function reactToValue(value) {
41
+ await Promise.all(value.map(async (v) => {
38
42
  if (!valueToLabel[v])
39
- wrapGetOptions(v).catch(console.debug);
40
- }
43
+ await wrapGetOptions(v);
44
+ }));
45
+ selectedStore.set(value.map(v => ({ value: v, label: valueToLabel[v] })).filter(v => isNotBlank(v.label)));
41
46
  }
42
47
  </script>
43
48
 
44
- <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} let:value let:valid let:invalid let:id let:onBlur let:setVal>
45
- {@const _ = reactToValue(value)}
46
- <div class:valid class:invalid>
47
- <MultiSelect {id} name={path} {disabled} selected={valueToSelected(value, valueToLabel)} {placeholder} getOptions={wrapGetOptions} on:change={e => setVal(e.detail.map(itm => itm.value))} on:blur={onBlur}></MultiSelect>
48
- </div>
49
- </FieldStandard>
49
+ {#if hasInit}
50
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} let:value let:valid let:invalid let:id let:onBlur let:setVal>
51
+ {@const _ = reactToValue(value)}
52
+ <div class:valid class:invalid>
53
+ <MultiSelect {id} name={path} {disabled} selected={$selectedStore} {placeholder} getOptions={wrapGetOptions} on:change={e => setVal(e.detail.map(itm => itm.value))} on:blur={onBlur}></MultiSelect>
54
+ </div>
55
+ </FieldStandard>
56
+ {/if}
50
57
 
51
58
  <style>
52
59
  .invalid {
@@ -14,8 +14,9 @@ export let step = undefined;
14
14
  export let conditional = undefined;
15
15
  export let required = false;
16
16
  export let inputelement = undefined;
17
+ export let helptext = undefined;
17
18
  </script>
18
19
 
19
- <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} serialize={numberSerialize} deserialize={nullable ? numberNullableDeserialize : numberDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange>
20
- <Input bind:inputelement type="number" name={path} {value} {id} class="dialog-input {className}" {onChange} {onBlur} {valid} {invalid} {min} {max} {step}></Input>
20
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {helptext} serialize={numberSerialize} deserialize={nullable ? numberNullableDeserialize : numberDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:helptextid>
21
+ <Input bind:inputelement type="number" name={path} {value} {id} class="dialog-input {className}" {onChange} {onBlur} {valid} {invalid} {min} {max} {step} {helptextid}></Input>
21
22
  </FieldStandard>
@@ -13,6 +13,7 @@ declare const __propDef: {
13
13
  conditional?: boolean | undefined;
14
14
  required?: boolean;
15
15
  inputelement?: HTMLInputElement;
16
+ helptext?: string | undefined;
16
17
  };
17
18
  events: {
18
19
  [evt: string]: CustomEvent<any>;
package/FieldRadio.svelte CHANGED
@@ -1,5 +1,4 @@
1
- <script>import { nullableSerialize, nullableDeserialize } from '@txstate-mws/svelte-forms';
2
- import { randomid } from 'txstate-utils';
1
+ <script>import { randomid } from 'txstate-utils';
3
2
  import FieldStandard from './FieldStandard.svelte';
4
3
  import Radio from './Radio.svelte';
5
4
  let className = '';
@@ -13,16 +12,22 @@ export let defaultValue = notNull ? choices[0].value : undefined;
13
12
  export let conditional = undefined;
14
13
  export let required = false;
15
14
  export let horizontal = false;
15
+ export let helptext = undefined;
16
+ export let number = false;
17
+ export let date = false;
18
+ export let datetime = false;
19
+ export let serialize = undefined;
20
+ export let deserialize = undefined;
16
21
  const groupid = randomid();
17
22
  const width = '100%';
18
23
  </script>
19
24
 
20
- <FieldStandard bind:id descid={groupid} {label} {path} {required} {defaultValue} {conditional} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:onBlur let:onChange>
25
+ <FieldStandard bind:id descid={groupid} {label} {path} {required} {defaultValue} {conditional} {helptext} {notNull} {number} {date} {datetime} {serialize} {deserialize} let:value let:valid let:invalid let:onBlur let:onChange let:helptextid>
21
26
  <div class="dialog-radio {className}" class:horizontal role="radiogroup" aria-labelledby={groupid} class:invalid>
22
27
  {#each choices as choice, idx}
23
28
  {@const radioid = `${path}.${idx}`}
24
29
  <label for={radioid} style:width>
25
- <Radio id={radioid} name={path} value={choice.value} selected={value === choice.value} disabled={choice.disabled} {onChange} {onBlur} />
30
+ <Radio id={radioid} name={path} value={choice.value} selected={value === choice.value} disabled={choice.disabled} {onChange} {onBlur} {helptextid}/>
26
31
  <span>{choice.label || (typeof choice.value === 'string' ? choice.value : '')}</span>
27
32
  </label>
28
33
  {/each}
@@ -15,6 +15,12 @@ declare const __propDef: {
15
15
  conditional?: boolean | undefined;
16
16
  required?: boolean;
17
17
  horizontal?: boolean;
18
+ helptext?: string | undefined;
19
+ number?: boolean;
20
+ date?: boolean;
21
+ datetime?: boolean;
22
+ serialize?: (value: any) => string;
23
+ deserialize?: (value: string) => any;
18
24
  };
19
25
  events: {
20
26
  [evt: string]: CustomEvent<any>;
@@ -1,4 +1,4 @@
1
- <script>import { nullableSerialize, nullableDeserialize } from '@txstate-mws/svelte-forms';
1
+ <script>import { getDescribedBy } from './';
2
2
  import FieldStandard from './FieldStandard.svelte';
3
3
  let className = '';
4
4
  export { className as class };
@@ -13,10 +13,16 @@ export let defaultValue = notNull ? choices[0].value : undefined;
13
13
  export let conditional = undefined;
14
14
  export let required = false;
15
15
  export let inputelement = undefined;
16
+ export let helptext = undefined;
17
+ export let number = false;
18
+ export let date = false;
19
+ export let datetime = false;
20
+ export let serialize = undefined;
21
+ export let deserialize = undefined;
16
22
  </script>
17
23
 
18
- <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid>
19
- <select bind:this={inputelement} {id} name={path} {disabled} class="dialog-input dialog-select {className}" on:change={onChange} on:blur={onBlur} class:valid class:invalid aria-describedby={messagesid}>
24
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {helptext} {notNull} {number} {date} {datetime} {serialize} {deserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid let:helptextid>
25
+ <select bind:this={inputelement} {id} name={path} {disabled} class="dialog-input dialog-select {className}" on:change={onChange} on:blur={onBlur} class:valid class:invalid aria-describedby={getDescribedBy([messagesid, helptextid])}>
20
26
  {#if !notNull}<option value="" selected={!value}>{placeholder}</option>{/if}
21
27
  {#each choices as choice (choice.value)}
22
28
  <option value={choice.value} disabled={choice.disabled} selected={value === choice.value}>{choice.label || choice.value}</option>
@@ -17,6 +17,12 @@ declare const __propDef: {
17
17
  conditional?: boolean | undefined;
18
18
  required?: boolean;
19
19
  inputelement?: HTMLSelectElement;
20
+ helptext?: string | undefined;
21
+ number?: boolean;
22
+ date?: boolean;
23
+ datetime?: boolean;
24
+ serialize?: (value: any) => string;
25
+ deserialize?: (value: string) => any;
20
26
  };
21
27
  events: {
22
28
  [evt: string]: CustomEvent<any>;
@@ -6,14 +6,19 @@ export let descid = undefined;
6
6
  export let path;
7
7
  export let defaultValue = undefined;
8
8
  export let label;
9
+ export let notNull = false;
10
+ export let number = false;
11
+ export let date = false;
12
+ export let datetime = false;
9
13
  export let serialize = undefined;
10
14
  export let deserialize = undefined;
11
15
  export let conditional = undefined;
12
16
  export let required = false;
17
+ export let helptext = undefined;
13
18
  </script>
14
19
 
15
- <Field {path} {defaultValue} {conditional} {serialize} {deserialize} let:path let:value let:onBlur let:onChange let:setVal let:messages let:valid let:invalid>
16
- <Container {id} {label} {messages} {descid} {required} let:messagesid>
17
- <slot {id} {path} {value} {onBlur} {onChange} {setVal} {valid} {invalid} {messagesid} />
20
+ <Field {path} {defaultValue} {conditional} {notNull} {number} {date} {datetime} {serialize} {deserialize} let:path let:value let:onBlur let:onChange let:setVal let:messages let:valid let:invalid>
21
+ <Container {id} {label} {messages} {descid} {required} {helptext} let:messagesid let:helptextid>
22
+ <slot {id} {path} {value} {onBlur} {onChange} {setVal} {valid} {invalid} {messagesid} {helptextid}/>
18
23
  </Container>
19
24
  </Field>
@@ -6,10 +6,15 @@ declare const __propDef: {
6
6
  path: string;
7
7
  defaultValue?: any;
8
8
  label: string;
9
+ notNull?: boolean;
10
+ number?: boolean;
11
+ date?: boolean;
12
+ datetime?: boolean;
9
13
  serialize?: (value: any) => string;
10
14
  deserialize?: (value: string) => any;
11
15
  conditional?: boolean | undefined;
12
16
  required?: boolean;
17
+ helptext?: string | undefined;
13
18
  };
14
19
  events: {
15
20
  [evt: string]: CustomEvent<any>;
@@ -25,6 +30,7 @@ declare const __propDef: {
25
30
  valid: boolean;
26
31
  invalid: boolean;
27
32
  messagesid: any;
33
+ helptextid: string;
28
34
  };
29
35
  };
30
36
  };
package/FieldText.svelte CHANGED
@@ -15,8 +15,9 @@ export let conditional = undefined;
15
15
  export let required = false;
16
16
  export let use = [];
17
17
  export let inputelement = undefined;
18
+ export let helptext = undefined;
18
19
  </script>
19
20
 
20
- <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid>
21
- <Input bind:inputelement {type} name={path} {value} {id} class="dialog-input {className}" {allowlastpass} {onChange} {onBlur} {valid} {invalid} {maxlength} {messagesid} {use}></Input>
21
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {helptext} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid let:helptextid>
22
+ <Input bind:inputelement {type} name={path} {value} {id} class="dialog-input {className}" {allowlastpass} {onChange} {onBlur} {valid} {invalid} {maxlength} {messagesid} {helptextid} {use}></Input>
22
23
  </FieldStandard>
@@ -15,6 +15,7 @@ declare const __propDef: {
15
15
  required?: boolean;
16
16
  use?: HTMLActionEntry[];
17
17
  inputelement?: HTMLInputElement;
18
+ helptext?: string | undefined;
18
19
  };
19
20
  events: {
20
21
  [evt: string]: CustomEvent<any>;
@@ -1,4 +1,5 @@
1
- <script>import { passActions } from '@txstate-mws/svelte-components';
1
+ <script>import { getDescribedBy } from './';
2
+ import { passActions } from '@txstate-mws/svelte-components';
2
3
  import { nullableSerialize, nullableDeserialize } from '@txstate-mws/svelte-forms';
3
4
  import FieldStandard from './FieldStandard.svelte';
4
5
  let className = '';
@@ -14,10 +15,11 @@ export let conditional = undefined;
14
15
  export let required = false;
15
16
  export let use = [];
16
17
  export let inputelement = undefined;
18
+ export let helptext = undefined;
17
19
  </script>
18
20
 
19
- <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid>
20
- <textarea bind:this={inputelement} name={path} {value} {id} {rows} class="dialog-input dialog-textarea {className}" class:valid class:invalid aria-invalid={invalid} aria-describedby={messagesid} on:change={onChange} on:blur={onBlur} on:keyup={onChange} on:paste {maxlength} use:passActions={use}></textarea>
21
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} {conditional} {helptext} serialize={!notNull && nullableSerialize} deserialize={!notNull && nullableDeserialize} let:value let:valid let:invalid let:id let:onBlur let:onChange let:messagesid let:helptextid>
22
+ <textarea bind:this={inputelement} name={path} {value} {id} {rows} class="dialog-input dialog-textarea {className}" class:valid class:invalid aria-invalid={invalid} aria-describedby={getDescribedBy([messagesid, helptextid])} on:change={onChange} on:blur={onBlur} on:keyup={onChange} on:paste {maxlength} use:passActions={use}></textarea>
21
23
  </FieldStandard>
22
24
 
23
25
  <style>
@@ -14,6 +14,7 @@ declare const __propDef: {
14
14
  required?: boolean;
15
15
  use?: HTMLActionEntry[];
16
16
  inputelement?: HTMLTextAreaElement;
17
+ helptext?: string | undefined;
17
18
  };
18
19
  events: {
19
20
  paste: ClipboardEvent;
package/Form.svelte CHANGED
@@ -13,8 +13,8 @@ export let preload = undefined;
13
13
  setContext(CHOOSER_API_CONTEXT, chooserClient);
14
14
  </script>
15
15
 
16
- <Form bind:store class="{className} dialog-form" {submit} {validate} on:saved {autocomplete} {name} {preload} let:messages let:showingInlineErrors let:saved let:valid let:invalid let:validating let:submitting>
17
- <slot {messages} {saved} {validating} {submitting} {valid} {invalid} />
16
+ <Form bind:store class="{className} dialog-form" {submit} {validate} on:saved {autocomplete} {name} {preload} let:messages let:showingInlineErrors let:saved let:valid let:invalid let:validating let:submitting let:data>
17
+ <slot {messages} {saved} {validating} {submitting} {valid} {invalid} {data} />
18
18
  <div class="form-errors" aria-live='assertive'>
19
19
  {#if messages.length}
20
20
  <ul>
@@ -40,7 +40,7 @@ setContext(CHOOSER_API_CONTEXT, chooserClient);
40
40
  padding: 0;
41
41
  }
42
42
  .form-errors {
43
- color: #9a3332;
43
+ color: var(--dg-danger-bg, #9a3332);
44
44
  padding: 1em;
45
45
  }
46
46
  </style>
@@ -0,0 +1,34 @@
1
+ <script>import contentSave from '@iconify-icons/mdi/content-save';
2
+ import { createEventDispatcher, setContext } from 'svelte';
3
+ import { CHOOSER_API_CONTEXT, Form } from './';
4
+ import Dialog from './Dialog.svelte';
5
+ export let submit;
6
+ export let validate = undefined;
7
+ export let store = undefined;
8
+ export let chooserClient = undefined;
9
+ export let autocomplete = undefined;
10
+ export let name = undefined;
11
+ export let title = '';
12
+ export let icon = undefined;
13
+ export let size = 'normal';
14
+ export let preload = undefined;
15
+ const dispatch = createEventDispatcher();
16
+ async function onSubmit() {
17
+ const resp = await store.submit();
18
+ if (resp.success)
19
+ dispatch('saved', resp.data);
20
+ }
21
+ setContext(CHOOSER_API_CONTEXT, chooserClient);
22
+ </script>
23
+
24
+ <Dialog continueText="Save" continueIcon={contentSave} cancelText="Cancel" on:escape on:continue={onSubmit} {title} {icon} {size}>
25
+ <Form bind:store {submit} {validate} {chooserClient} {autocomplete} {name} {preload} on:saved let:messages let:saved let:valid let:invalid let:validating let:submitting let:data>
26
+ <slot {messages} {saved} {validating} {submitting} {valid} {invalid} {data} />
27
+ </Form>
28
+ </Dialog>
29
+
30
+ <style>
31
+ :global(:root) {
32
+ --ck-z-default: var(--popup-z, 3001);
33
+ }
34
+ </style>
@@ -0,0 +1,38 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { IconifyIcon } from '@iconify/svelte';
3
+ import type { Feedback, FormStore, SubmitResponse } from '@txstate-mws/svelte-forms';
4
+ declare class __sveltets_Render<T> {
5
+ props(): {
6
+ submit: (state: T) => Promise<SubmitResponse<T>>;
7
+ validate?: (state: T) => Promise<Feedback[]>;
8
+ store?: FormStore<T>;
9
+ chooserClient?: any;
10
+ autocomplete?: string;
11
+ name?: string;
12
+ title?: string;
13
+ icon?: IconifyIcon;
14
+ size?: "small" | "normal" | "tiny" | "large";
15
+ preload?: T;
16
+ };
17
+ events(): {
18
+ saved: CustomEvent<T>;
19
+ escape: CustomEvent<undefined>;
20
+ };
21
+ slots(): {
22
+ default: {
23
+ messages: any;
24
+ saved: any;
25
+ validating: any;
26
+ submitting: any;
27
+ valid: any;
28
+ invalid: any;
29
+ data: any;
30
+ };
31
+ };
32
+ }
33
+ export declare type FormDialogProps<T> = ReturnType<__sveltets_Render<T>['props']>;
34
+ export declare type FormDialogEvents<T> = ReturnType<__sveltets_Render<T>['events']>;
35
+ export declare type FormDialogSlots<T> = ReturnType<__sveltets_Render<T>['slots']>;
36
+ export default class FormDialog<T> extends SvelteComponentTyped<FormDialogProps<T>, FormDialogEvents<T>, FormDialogSlots<T>> {
37
+ }
38
+ export {};
package/Input.svelte CHANGED
@@ -1,5 +1,6 @@
1
1
  <script>import { passActions } from '@txstate-mws/svelte-components';
2
2
  import { dateSerialize, datetimeSerialize } from '@txstate-mws/svelte-forms';
3
+ import { isNotBlank } from 'txstate-utils';
3
4
  let className = '';
4
5
  export { className as class };
5
6
  export let name;
@@ -13,6 +14,7 @@ export let step = undefined;
13
14
  export let id = undefined;
14
15
  export let disabled = false;
15
16
  export let messagesid = undefined;
17
+ export let helptextid = undefined;
16
18
  export let valid = false;
17
19
  export let invalid = false;
18
20
  export let onChange;
@@ -20,6 +22,7 @@ export let onBlur;
20
22
  export let onSelect = undefined;
21
23
  export let use = [];
22
24
  export let inputelement = undefined;
25
+ $: descby = [helptextid, messagesid].filter(isNotBlank).join(' ');
23
26
  function resolveMinMax(dt) {
24
27
  if (typeof dt === 'undefined')
25
28
  return undefined;
@@ -33,4 +36,4 @@ $: minStr = resolveMinMax(min);
33
36
  $: maxStr = resolveMinMax(max);
34
37
  </script>
35
38
 
36
- <input bind:this={inputelement} {type} {id} class={className} autocomplete="off" data-lpignore={!allowlastpass} {name} {value} {disabled} {maxlength} min={minStr} max={maxStr} {step} class:valid class:invalid aria-invalid={invalid} aria-describedby={messagesid} on:change={onChange} on:select={onSelect} on:blur={onBlur} on:keyup={onChange} use:passActions={use}>
39
+ <input bind:this={inputelement} {type} {id} class={className} autocomplete="off" data-lpignore={!allowlastpass} {name} {value} {disabled} {maxlength} min={minStr} max={maxStr} {step} class:valid class:invalid aria-invalid={invalid} aria-describedby={isNotBlank(descby) ? descby : null} on:change={onChange} on:select={onSelect} on:blur={onBlur} on:keyup={onChange} use:passActions={use}>
package/Input.svelte.d.ts CHANGED
@@ -18,6 +18,7 @@ declare const __propDef: {
18
18
  id?: string | undefined;
19
19
  disabled?: boolean;
20
20
  messagesid?: string | undefined;
21
+ helptextid?: string | undefined;
21
22
  valid?: boolean;
22
23
  invalid?: boolean;
23
24
  onChange: any;
package/Listbox.svelte CHANGED
@@ -122,6 +122,7 @@ function focusListbox() {
122
122
  <span id={labelId}>{label}</span>
123
123
  <ul bind:this={listboxElement} role="listbox" id={listId} class="listbox-items" tabindex="0" aria-describedby={descid} aria-labelledby={labelId} aria-multiselectable={multiselect} on:keydown={onkeydown} on:focus={focusListbox}>
124
124
  {#each items as item, i (item.value)}
125
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
125
126
  <li
126
127
  bind:this={itemelements[i]}
127
128
  id={`${listId}-${i}`}
package/Radio.svelte CHANGED
@@ -1,16 +1,19 @@
1
- <script>export let id = undefined;
1
+ <script>import { isNotBlank } from 'txstate-utils';
2
+ export let id = undefined;
2
3
  export let name;
3
4
  export let value;
4
5
  export let selected = false;
5
6
  export let onChange = undefined;
6
7
  export let onBlur = undefined;
7
8
  export let messagesid = undefined;
9
+ export let helptextid = undefined;
8
10
  export let disabled = false;
9
11
  export let valid = false;
10
12
  export let invalid = false;
13
+ $: descby = [messagesid, helptextid].filter(isNotBlank).join(' ');
11
14
  </script>
12
15
 
13
- <input {id} type="radio" {name} class:valid class:invalid checked={selected} {disabled} aria-describedby={messagesid} {value} on:change={onChange} on:blur={onBlur}>
16
+ <input {id} type="radio" {name} class:valid class:invalid checked={selected} {disabled} aria-describedby={descby} {value} on:change={onChange} on:blur={onBlur}>
14
17
 
15
18
  <style>
16
19
  input, input:before, input:after {
package/Radio.svelte.d.ts CHANGED
@@ -8,6 +8,7 @@ declare const __propDef: {
8
8
  onChange?: any;
9
9
  onBlur?: any;
10
10
  messagesid?: string | undefined;
11
+ helptextid?: string | undefined;
11
12
  disabled?: boolean;
12
13
  valid?: boolean;
13
14
  invalid?: boolean;
package/Tab.svelte CHANGED
@@ -13,15 +13,16 @@ const last = idx === $store.tabs.length - 1;
13
13
  {#if $accordion}
14
14
  <div bind:this={tabelements[idx]} id={$store.tabids[title]} class="tabs-tab" class:last aria-selected={active} aria-controls={$store.panelids[title]} 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>
15
15
  {/if}
16
- <div id={$store.panelids[title]} hidden={!active} role="tabpanel" tabindex="0" aria-labelledby={$store.tabids[title]} class="tabs-panel" class:accordion={$accordion}>
16
+ <div id={$store.panelids[title]} hidden={!active} role="tabpanel" tabindex="-1" aria-labelledby={$store.tabids[title]} class="tabs-panel" class:accordion={$accordion}>
17
17
  <slot />
18
18
  </div>
19
19
 
20
20
  <style>
21
21
  .tabs-panel {
22
22
  width: 100%;
23
- border: var(--tabs-panel-border, var(--tabs-border, 1px solid #666666));
24
- padding: var(--tabs-panel-padding, 1em);
23
+ min-height: 50vh;
24
+ border: var(--tabs-panel-border, var(--tabs-border, 0));
25
+ padding: var(--tabs-margin-top, 2em) var(--tabs-padding-hori, 0.7em) 0 var(--tabs-padding-hori, 0.7em);
25
26
  }
26
27
  .tabs-panel.accordion {
27
28
  border-left: 0;
package/TabStore.d.ts CHANGED
@@ -2,19 +2,26 @@ 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
- interface TabDef {
5
+ export interface TabDef {
6
6
  title: string;
7
7
  icon?: IconifyIcon;
8
+ required?: boolean;
8
9
  }
9
10
  interface ITabStore extends ElementSize {
10
11
  current?: number;
12
+ prevTitle?: string;
13
+ nextTitle?: string;
14
+ requireNext: boolean;
11
15
  tabs: TabDef[];
16
+ visited: Record<string, boolean>;
12
17
  tabids: Record<string, string>;
13
18
  panelids: Record<string, string>;
14
19
  }
15
20
  export declare class TabStore extends Store<ITabStore> {
16
21
  initialTab?: string | undefined;
17
22
  constructor(tabs: TabDef[], initialTab?: string | undefined);
23
+ set(v: ITabStore): void;
24
+ currentIdx(): import("@txstate-mws/svelte-store").DerivedStore<number, ITabStore>;
18
25
  currentTitle(): import("@txstate-mws/svelte-store").DerivedStore<string, ITabStore>;
19
26
  currentTabId(): import("@txstate-mws/svelte-store").DerivedStore<string, ITabStore>;
20
27
  currentPanelId(): import("@txstate-mws/svelte-store").DerivedStore<string, ITabStore>;
package/TabStore.js CHANGED
@@ -1,18 +1,33 @@
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
+ function checkNext(v) {
5
+ v.visited = { ...v.visited, [v.tabs[v.current].title]: true };
6
+ v.prevTitle = v.current > 0 ? v.tabs[v.current - 1].title : undefined;
7
+ v.nextTitle = v.tabs.length - 1 > v.current ? v.tabs[v.current + 1].title : undefined;
8
+ v.requireNext = v.tabs.some((t, i) => t.required && !v.visited[t.title]);
9
+ return v;
10
+ }
4
11
  export class TabStore extends Store {
5
12
  initialTab;
6
13
  constructor(tabs, initialTab) {
7
- super({
14
+ const current = findIndex(tabs, t => t.title === initialTab, 0);
15
+ super(checkNext({
8
16
  tabs,
9
- current: findIndex(tabs, t => t.title === initialTab, 0),
17
+ current,
18
+ visited: {},
10
19
  tabids: tabs.reduce((acc, curr) => ({ ...acc, [curr.title]: randomid() }), {}),
11
20
  panelids: tabs.reduce((acc, curr) => ({ ...acc, [curr.title]: randomid() }), {}),
12
21
  clientWidth: 1024
13
- });
22
+ }));
14
23
  this.initialTab = initialTab;
15
24
  }
25
+ set(v) {
26
+ super.set(checkNext(v));
27
+ }
28
+ currentIdx() {
29
+ return derivedStore(this, v => v.current);
30
+ }
16
31
  currentTitle() {
17
32
  return derivedStore(this, v => v.tabs[v.current].title);
18
33
  }