@dosgato/dialog 1.2.2 → 1.2.4

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.
@@ -24,9 +24,11 @@ export let leftToRight = false;
24
24
  export let related = 0;
25
25
  export let extradescid = undefined;
26
26
  export let helptext = undefined;
27
+ export let selectAll = false;
27
28
  const store = getContext(FORM_CONTEXT);
28
29
  const inheritedPath = getContext(FORM_INHERITED_PATH);
29
30
  const finalPath = [inheritedPath, path].filter(isNotBlank).join('.');
31
+ const valStore = store.getField(finalPath);
30
32
  const currentWidth = derivedStore(store, 'width');
31
33
  $: cols = Math.min(Math.ceil($currentWidth / maxwidth), choices.length);
32
34
  let orders;
@@ -47,10 +49,25 @@ function onChangeCheckbox(setVal, choice, included) {
47
49
  return [...v, choice.value];
48
50
  });
49
51
  }
52
+ $: selected = new Set($valStore ?? []);
53
+ let selectAllElement;
54
+ const selectAllId = randomid();
55
+ $: selectAllChecked = choices.every(choice => choice.disabled || selected.has(choice.value));
56
+ function selectAllChanged() {
57
+ if (selectAllChecked) {
58
+ // it was checked and is now unchecked, clear it out
59
+ void store.setField(finalPath, []);
60
+ }
61
+ else {
62
+ // it was not checked and now it is checked
63
+ void store.setField(finalPath, choices.filter(choice => !choice.disabled).map(choice => choice.value));
64
+ }
65
+ }
50
66
  const descid = randomid();
51
67
  function reactToChoices(..._) {
52
68
  const choiceSet = new Set(choices?.filter(c => !c.disabled).map(c => c.value));
53
- const val = get($store, finalPath);
69
+ selected = new Set(Array.from(selected).filter(v => choiceSet.has(v)));
70
+ const val = get($store.data, finalPath);
54
71
  const filtered = val?.filter(v => choiceSet.has(v));
55
72
  if (filtered?.length !== val?.length)
56
73
  store.setField(finalPath, filtered).catch(console.error);
@@ -62,6 +79,12 @@ onMount(reactToChoices);
62
79
  <Field {path} {defaultValue} {conditional} let:path let:value let:onBlur let:setVal let:messages let:valid let:invalid serialize={arraySerialize}>
63
80
  <Container {path} {id} {label} {messages} {descid} {related} {helptext} let:messagesid let:helptextid>
64
81
  <div class="dialog-choices {className}" class:valid class:invalid>
82
+ {#if selectAll}
83
+ <label for={selectAllId} style:width>
84
+ <Checkbox id={selectAllId} name={selectAllId} bind:inputelement={selectAllElement} value={selectAllChecked} onChange={selectAllChanged} />
85
+ <span>Select All</span>
86
+ </label>
87
+ {/if}
65
88
  {#each choices as choice, idx (choice.value)}
66
89
  {@const checkid = `${path}.${idx}`}
67
90
  {@const included = value?.includes(choice.value)}
@@ -17,6 +17,7 @@ declare const __propDef: {
17
17
  related?: true | number;
18
18
  extradescid?: string | undefined;
19
19
  helptext?: string | undefined;
20
+ selectAll?: boolean;
20
21
  };
21
22
  events: {
22
23
  [evt: string]: CustomEvent<any>;
@@ -1,10 +1,10 @@
1
1
  <script context="module">export const DG_DIALOG_FIELD_MULTIPLE = {};
2
2
  function noOp(..._) { return ''; }
3
3
  </script>
4
- <script>import caretCircleDown from '@iconify-icons/ph/caret-circle-down';
5
- import caretCircleUp from '@iconify-icons/ph/caret-circle-up';
4
+ <script>import caretCircleDown from '@iconify-icons/ph/caret-circle-down-fill';
5
+ import caretCircleUp from '@iconify-icons/ph/caret-circle-up-fill';
6
6
  import plusCircleLight from '@iconify-icons/ph/plus-circle-light';
7
- import xCircle from '@iconify-icons/ph/x-circle';
7
+ import xCircle from '@iconify-icons/ph/x-circle-fill';
8
8
  import { AddMore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
9
9
  import { derivedStore } from '@txstate-mws/svelte-store';
10
10
  import { getContext, setContext } from 'svelte';
@@ -132,6 +132,9 @@ $: messages = compact ? $messageStore : [];
132
132
  font-size: 1.3em;
133
133
  color: black;
134
134
  }
135
+ .dialog-multiple-buttons .dialog-multiple-delete {
136
+ color: var(--dg-danger-bg, #9a3332);
137
+ }
135
138
  .dialog-multiple-buttons button:disabled {
136
139
  color: #6d6d6d;
137
140
  }
@@ -24,6 +24,9 @@ export let id = undefined;
24
24
  export let label = '';
25
25
  /** Text to display in the text input when it's empty. */
26
26
  export let placeholder = '';
27
+ /** When there are no items (e.g. it's a filtered search and there were no results), we still display one
28
+ disabled item in the menu to let the user know what is going on. Use this prop to specify the message. */
29
+ export let emptyText = undefined;
27
30
  export let disabled = false;
28
31
  export let defaultValue = [];
29
32
  export let conditional = undefined;
@@ -73,7 +76,7 @@ async function reactToValue(value) {
73
76
  {@const _ = reactToValue(value)}
74
77
  <div class:valid class:invalid>
75
78
  <MultiSelect {id} name={path} descid={getDescribedBy([messagesid, helptextid, extradescid])}
76
- {disabled} {maxSelections} selected={$selectedStore} {placeholder} getOptions={wrapGetOptions}
79
+ {disabled} {maxSelections} selected={$selectedStore} {placeholder} {emptyText} getOptions={wrapGetOptions}
77
80
  inputClass='multiselect-input'
78
81
  on:change={e => setVal(e.detail.map(itm => itm.value))} on:blur={onBlur}
79
82
  />
@@ -11,6 +11,8 @@ declare const __propDef: {
11
11
  id?: string | undefined;
12
12
  label?: string;
13
13
  /** Text to display in the text input when it's empty. */ placeholder?: string;
14
+ /** When there are no items (e.g. it's a filtered search and there were no results), we still display one
15
+ disabled item in the menu to let the user know what is going on. Use this prop to specify the message. */ emptyText?: string | undefined;
14
16
  disabled?: boolean;
15
17
  defaultValue?: string[];
16
18
  conditional?: boolean | undefined;
@@ -3,6 +3,9 @@ import { onMount, tick } from 'svelte';
3
3
  import { isNotBlank, randomid } from 'txstate-utils';
4
4
  import FieldStandard from '../FieldStandard.svelte';
5
5
  import { CropperStore } from './cropper';
6
+ import { Button } from '..';
7
+ import arrowsOutIcon from '@iconify-icons/ph/arrows-out-fill';
8
+ import arrowsCircleIcon from '@iconify-icons/ph/arrows-clockwise-fill';
6
9
  export let id = undefined;
7
10
  export let path;
8
11
  export let imageSrc;
@@ -127,6 +130,22 @@ async function reactToAspectRatio(ar) {
127
130
  }
128
131
  }
129
132
  $: void reactToAspectRatio(selectionAspectRatio);
133
+ let initialLoad = true;
134
+ let initialVal;
135
+ let srcChanged = false;
136
+ async function onimageload(e) {
137
+ if (initialLoad) {
138
+ initialVal = e.target.src;
139
+ initialLoad = false;
140
+ }
141
+ else {
142
+ if (e.target.src !== initialVal || srcChanged) {
143
+ await tick();
144
+ store.maximize();
145
+ srcChanged = true;
146
+ }
147
+ }
148
+ }
130
149
  </script>
131
150
 
132
151
  <svelte:window on:mousemove={onMouseMove} on:mouseup={onMouseUp} on:touchend={onMouseUp} on:touchcancel={onMouseUp} />
@@ -134,9 +153,16 @@ $: void reactToAspectRatio(selectionAspectRatio);
134
153
  {@const _ = init(value, setVal)}
135
154
  {#if isNotBlank(imageSrc)}
136
155
  <div on:focusin={() => { focusWithin = true }} on:focusout={() => { focusWithin = false }}>
156
+ <div class="action-buttons">
157
+ <Button type="button" on:click={onMaximize} icon={arrowsOutIcon}>Center and Maximize</Button>
158
+ <Button type="button" on:click={() => store.reset()} icon={arrowsCircleIcon} class="btn-clear">Clear</Button>
159
+ </div>
160
+ <div class="cropper-instructions">
161
+ Click and drag to select a section of your image to use.
162
+ </div>
137
163
  <!-- svelte-ignore a11y-no-static-element-interactions -->
138
164
  <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}>
139
- <img class="crop-image" src={imageSrc} alt="" />
165
+ <img class="crop-image" src={imageSrc} alt="" on:load={onimageload}/>
140
166
  {#if $selection && $outputPct}
141
167
  <div class='crop-bg'>
142
168
  <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}%)" />
@@ -187,13 +213,6 @@ $: void reactToAspectRatio(selectionAspectRatio);
187
213
  </div>
188
214
  {/if}
189
215
  </div>
190
- <div class="action-buttons">
191
- <button type="button" class='btn-center-max' on:click={onMaximize}>Center and Maximize</button>
192
- <button type="button" class='btn-clear' on:click={() => store.reset()}>Clear</button>
193
- </div>
194
- <div class="cropper-instructions">
195
- Click and drag to select a section of your image to use.
196
- </div>
197
216
  </div>
198
217
  {:else}
199
218
  Image not selected
@@ -201,6 +220,22 @@ $: void reactToAspectRatio(selectionAspectRatio);
201
220
  </FieldStandard>
202
221
 
203
222
  <style>
223
+ .action-buttons {
224
+ display: flex;
225
+ gap: 1em;
226
+ }
227
+ :global(button.reset.btn-clear) {
228
+ background-color: var(--dg-button-cancel-bg, #808080);
229
+
230
+ }
231
+ :global(button.reset.btn-clear:not([disabled]):hover) {
232
+ background-color: var(--dg-button-cancel-hover-bg, #595959);
233
+ }
234
+
235
+ .cropper-instructions {
236
+ padding: 0.5em 0;
237
+ }
238
+
204
239
  .crop-image-container {
205
240
  position: relative;
206
241
  user-select: none;
@@ -5,6 +5,12 @@ import { TAG_API_CONTEXT } from './TagAPI';
5
5
  export let path;
6
6
  export let label;
7
7
  export let target;
8
+ export let conditional = undefined;
9
+ export let required = false;
10
+ export let helptext = undefined;
11
+ export let emptyText = undefined;
12
+ /** Text to display in the tag picker input field when it's empty. */
13
+ export let placeholder = '';
8
14
  const tagClient = getContext(TAG_API_CONTEXT);
9
15
  let lasttarget = -1;
10
16
  let groups = [];
@@ -33,4 +39,4 @@ async function lookupByValue(id) {
33
39
  }
34
40
  </script>
35
41
 
36
- <FieldMultiselect {path} {label} {getOptions} {lookupByValue} />
42
+ <FieldMultiselect {path} {label} {getOptions} {lookupByValue} {conditional} {required} {helptext} {emptyText} {placeholder} />
@@ -4,6 +4,11 @@ declare const __propDef: {
4
4
  path: string;
5
5
  label: string;
6
6
  target: any;
7
+ conditional?: boolean | undefined;
8
+ required?: boolean;
9
+ helptext?: string | undefined;
10
+ emptyText?: string | undefined;
11
+ /** Text to display in the tag picker input field when it's empty. */ placeholder?: string;
7
12
  };
8
13
  events: {
9
14
  [evt: string]: CustomEvent<any>;
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.2.2",
4
+ "version": "1.2.4",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",