@dosgato/dialog 0.0.69 → 0.0.71

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.
@@ -137,11 +137,11 @@ $: setNeedsShowHelp(helpelement);
137
137
  line-height: 1.25em;
138
138
  overflow: hidden;
139
139
  text-overflow: ellipsis;
140
- white-space: nowrap;
140
+ height: 1.25em;
141
141
  }
142
142
  .dialog-field-help.expanded {
143
- white-space: normal;
144
143
  max-height: fit-content;
144
+ height: auto;
145
145
  }
146
146
  .dialog-field-help.needsShowHelp {
147
147
  padding-right: 6em;
@@ -85,7 +85,7 @@ $: describedby = [title ? labelid : undefined, descid].filter(isNotBlank).join('
85
85
  section.normal {
86
86
  width: 75vw;
87
87
  min-width: 250px;
88
- max-width: 650px;
88
+ max-width: 750px;
89
89
  }
90
90
  section.large {
91
91
  width: 90vw;
@@ -113,13 +113,17 @@ $: describedby = [title ? labelid : undefined, descid].filter(isNotBlank).join('
113
113
 
114
114
  .dialog-content {
115
115
  position: relative;
116
- padding: 1em;
116
+ padding: 0 2em;
117
117
  min-height: 5em;
118
118
  overflow: auto;
119
119
  background-color: var(--dg-dialog-content-bg, #f4f4f4);
120
120
  max-height: calc(100vh - 7em);
121
121
  }
122
122
 
123
+ section.tiny .dialog-content, section.small .dialog-content {
124
+ padding: 0 1em;
125
+ }
126
+
123
127
  footer {
124
128
  display: flex;
125
129
  align-items: center;
@@ -1,5 +1,6 @@
1
1
  <script>import arrowClockwiseFill from '@iconify-icons/ph/arrow-clockwise-fill';
2
2
  import deleteOutline from '@iconify-icons/mdi/delete-outline';
3
+ import xCircle from '@iconify-icons/ph/x-circle';
3
4
  import { FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
4
5
  import { getContext } from 'svelte';
5
6
  import { isBlank, isNotBlank, randomid } from 'txstate-utils';
@@ -32,6 +33,7 @@ const finalPath = [inheritedPath, path].filter(isNotBlank).join('.');
32
33
  const value = formStore.getField(finalPath);
33
34
  const chooserClient = getContext(CHOOSER_API_CONTEXT);
34
35
  const store = new ChooserStore(chooserClient);
36
+ let urlEntryInput;
35
37
  const descid = randomid();
36
38
  let modalshown = false;
37
39
  async function show() {
@@ -49,6 +51,10 @@ function onChange(setVal) {
49
51
  hide();
50
52
  };
51
53
  }
54
+ function clearUrlEntry() {
55
+ urlEntryInput.value = '';
56
+ urlEntryInput.dispatchEvent(new InputEvent('input'));
57
+ }
52
58
  let timer;
53
59
  function userUrlEntry() {
54
60
  clearTimeout(timer);
@@ -116,7 +122,11 @@ async function userUrlEntryDebounced() {
116
122
  }
117
123
  const urlToValueCache = {};
118
124
  async function updateSelected(..._) {
119
- if ($value && selectedAsset?.id !== $value) {
125
+ if (selectedAsset?.id !== $value) {
126
+ if (!$value) {
127
+ selectedAsset = undefined;
128
+ return;
129
+ }
120
130
  const valueBeforeFind = $value;
121
131
  const asset = await chooserClient.findById($value);
122
132
  if ($value !== valueBeforeFind)
@@ -149,7 +159,7 @@ $: updateSelected($value);
149
159
 
150
160
  <FieldStandard bind:id {path} {descid} {label} {defaultValue} {conditional} {required} {related} {helptext} let:value let:messagesid let:helptextid let:valid let:invalid let:id let:onBlur let:setVal>
151
161
  {#if selectedAsset?.id}
152
- <div class="dialog-chooser-container">
162
+ <div class="dialog-chooser-container" class:urlEntry>
153
163
  <Thumbnail item={selectedAsset} />
154
164
  <div class="dialog-chooser-right">
155
165
  <Details item={selectedAsset} singleLine />
@@ -157,17 +167,20 @@ $: updateSelected($value);
157
167
  <button type="button" on:click={show} aria-describedby={getDescribedBy([descid, messagesid, helptextid, extradescid])}>
158
168
  <Icon icon={arrowClockwiseFill} /> Replace
159
169
  </button>
170
+ <button type="button" on:click={() => { selectedAsset = undefined; setVal(undefined) }} aria-describedby={getDescribedBy([descid, messagesid, helptextid, extradescid])}>
171
+ <Icon icon={deleteOutline} inline /> Remove
172
+ </button>
160
173
  {/if}
161
- <button type="button" on:click={() => { selectedAsset = undefined; setVal(undefined) }} aria-describedby={getDescribedBy([descid, messagesid, helptextid, extradescid])}>
162
- <Icon icon={deleteOutline} /> Remove
163
- </button>
164
174
  </div>
165
175
  </div>
166
176
  {/if}
167
177
  {#if urlEntry || !selectedAsset?.id}
168
178
  <div class="dialog-chooser-entry">
169
179
  {#if urlEntry}
170
- <input type="text" value={selectedAsset?.url ?? ''} on:change={userUrlEntry} on:keyup={userUrlEntry}>
180
+ <div class="dialog-chooser-entry-input">
181
+ <input bind:this={urlEntryInput} type="text" data-lpignore="true" value={selectedAsset?.url ?? ''} on:change={userUrlEntry} on:input={userUrlEntry}>
182
+ <button type="button" on:click={clearUrlEntry}><Icon icon={xCircle} hiddenLabel="clear input" inline/></button>
183
+ </div>
171
184
  {/if}
172
185
  <button type="button" on:click={show} aria-describedby={getDescribedBy([descid, messagesid, helptextid, extradescid])}>Select {#if value}New{/if} {#if assets && pages}Link Target{:else if images}Image{:else if assets}Asset{:else}Page{/if}</button>
173
186
  </div>
@@ -186,20 +199,57 @@ $: updateSelected($value);
186
199
  flex-wrap: wrap;
187
200
  margin-bottom: 0.25em;
188
201
  font-size: 0.9em;
202
+ align-items: center;
189
203
  }
190
204
  div.dialog-chooser-container > :global(.dialog-chooser-thumbnail) {
191
205
  width: 8em;
192
206
  padding-top: 0;
193
207
  height: 5em;
194
208
  }
209
+ div.dialog-chooser-container.urlEntry > :global(.dialog-chooser-thumbnail) {
210
+ width: 3em;
211
+ padding-top: 0;
212
+ height: 3em;
213
+ }
214
+ .dialog-chooser-right {
215
+ max-width: calc(100% - 8.5em);
216
+ flex-grow: 1;
217
+ }
195
218
  .dialog-chooser-right button {
196
219
  margin-top: 0.5em;
220
+ border: 0;
221
+ font-weight: bold;
222
+ border-radius: 0.2em;
223
+ cursor: pointer;
224
+ padding: 0.4em;
225
+ background: none;
226
+ }
227
+ .dialog-chooser-right button:hover {
228
+ background-color: #cccccc;
197
229
  }
198
230
  .dialog-chooser-entry {
199
231
  display: flex;
232
+ align-items: flex-start;
233
+ margin-top: 0.2em;
200
234
  }
201
- input {
235
+ .dialog-chooser-entry-input {
236
+ position: relative;
202
237
  flex-grow: 1;
203
238
  }
239
+ .dialog-chooser-entry-input input {
240
+ width: 100%;
241
+ padding-right: 1.4em;
242
+ }
243
+ .dialog-chooser-entry-input button {
244
+ display: block;
245
+ border: 0;
246
+ background: none;
247
+ position: absolute;
248
+ top: 50%;
249
+ right: 0;
250
+ transform: translateY(-50%);
251
+ cursor: pointer;
252
+ line-height: 1;
253
+ }
204
254
 
205
255
  </style>
@@ -1,13 +1,17 @@
1
1
  <script context="module">export const DG_DIALOG_FIELD_MULTIPLE = {};
2
2
  function noOp(..._) { return ''; }
3
3
  </script>
4
- <script>import plusCircleLight from '@iconify-icons/ph/plus-circle-light';
4
+ <script>import caretCircleDown from '@iconify-icons/ph/caret-circle-down';
5
+ import caretCircleUp from '@iconify-icons/ph/caret-circle-up';
6
+ import plusCircleLight from '@iconify-icons/ph/plus-circle-light';
7
+ import xCircle from '@iconify-icons/ph/x-circle';
5
8
  import { AddMore, FORM_CONTEXT, FORM_INHERITED_PATH } from '@txstate-mws/svelte-forms';
6
9
  import { derivedStore } from '@txstate-mws/svelte-store';
7
10
  import { getContext, setContext } from 'svelte';
8
11
  import { isNotNull } from 'txstate-utils';
9
12
  import Button from './Button.svelte';
10
13
  import Container from './Container.svelte';
14
+ import Icon from './Icon.svelte';
11
15
  export let path;
12
16
  export let label;
13
17
  export let initialState = undefined;
@@ -28,11 +32,28 @@ const inheritedPath = getContext(FORM_INHERITED_PATH);
28
32
  const finalPath = [inheritedPath, path].filter(isNotNull).join('.');
29
33
  const store = getContext(FORM_CONTEXT);
30
34
  const messageStore = derivedStore(store, ({ messages }) => messages.all.filter(m => m.path?.startsWith(finalPath)));
31
- const reorderelements = [];
32
- function moveUpAndFocus(onMoveUp, idx) {
35
+ const reorderupelements = [];
36
+ const reorderdownelements = [];
37
+ function moveUpAndFocus(onMove, idx) {
33
38
  return () => {
34
- onMoveUp();
35
- reorderelements[idx - 1]?.focus();
39
+ onMove();
40
+ let newFocus = reorderupelements[idx - 1];
41
+ if (newFocus) {
42
+ if (newFocus.disabled)
43
+ newFocus = reorderdownelements[idx - 1];
44
+ newFocus.focus();
45
+ }
46
+ };
47
+ }
48
+ function moveDownAndFocus(onMove, idx) {
49
+ return () => {
50
+ onMove();
51
+ let newFocus = reorderdownelements[idx + 1];
52
+ if (newFocus) {
53
+ if (newFocus.disabled)
54
+ newFocus = reorderupelements[idx + 1];
55
+ newFocus.focus();
56
+ }
36
57
  };
37
58
  }
38
59
  $: messages = compact ? $messageStore : [];
@@ -40,16 +61,19 @@ $: messages = compact ? $messageStore : [];
40
61
 
41
62
  <Container {label} {messages} {conditional} {related} {helptext} let:helptextid>
42
63
  {noOp(fieldMultipleContext.helptextid = helptextid)}
43
- <AddMore {path} {initialState} {minLength} {maxLength} {conditional} let:path let:currentLength let:maxLength let:index let:minned let:maxed let:value let:onDelete let:onMoveUp>
64
+ <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>
44
65
  {@const showDelete = removable && !minned}
45
- {@const showMove = reorder && index > 0}
46
- <div class="dialog-multiple" class:has-delete-icon={showDelete} class:has-move-icon={showMove}>
66
+ {@const showMove = reorder && currentLength > 1}
67
+ <div class="dialog-multiple" class:has-delete-icon={showDelete} class:has-move-icon={showMove} class:first={index === 0}>
47
68
  <div class="dialog-multiple-content">
48
69
  <slot {path} {index} {value} {maxed} {maxLength} {currentLength}/>
49
70
  </div>
50
71
  {#if showDelete || showMove}<div class="dialog-multiple-buttons">
51
- {#if reorder}<button bind:this={reorderelements[index]} class="dialog-multiple-move" type="button" aria-hidden={!showMove} disabled={!showMove} style:visibility={showMove ? 'visible' : 'hidden'} on:click|preventDefault|stopPropagation={moveUpAndFocus(onMoveUp, index)}>^</button>{/if}
52
- {#if showDelete}<button class="dialog-multiple-delete" type="button" on:click|preventDefault|stopPropagation={onDelete}>X</button>{/if}
72
+ {#if showMove}
73
+ <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
+ <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
+ {/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}
53
77
  </div>{/if}
54
78
  </div>
55
79
  <svelte:fragment slot="addbutton" let:maxed let:onClick>
@@ -61,10 +85,10 @@ $: messages = compact ? $messageStore : [];
61
85
  <style>
62
86
  .dialog-multiple {
63
87
  position: relative;
64
- border: var(--dialog-container-border, 0);
65
- padding: var(--dialog-container-padding, 1em);
88
+ border: var(--dialog-container-border, 1px dashed #CCCCCC);
89
+ padding: var(--dialog-container-padding, 1.5em);
66
90
  }
67
- .dialog-multiple:not(:first-child) {
91
+ .dialog-multiple:not(.first) {
68
92
  border-top: 0;
69
93
  }
70
94
  .dialog-multiple:nth-of-type(even) {
@@ -75,25 +99,18 @@ $: messages = compact ? $messageStore : [];
75
99
  background-color: var(--dialog-field-bg2, transparent);
76
100
  color: var(--dialog-field-text2, inherit);
77
101
  }
78
- :global(.dialog-multiple-button) {
79
- margin-left: var(--dialog-container-padding, 1em);
80
- }
81
- .dialog-multiple.has-delete-icon, .dialog-multiple.has-move-icon {
82
- display: flex;
83
- align-items: center;
84
- justify-content: space-between;
85
- }
86
102
  .dialog-multiple-buttons {
87
- margin-left: 0.75em;
103
+ position: absolute;
104
+ top: 0;
105
+ right: 0.1em;
106
+ display: flex;
88
107
  }
89
108
  .dialog-multiple-buttons button {
90
- margin-left: 0.2em;
91
- }
92
- .dialog-multiple-buttons button:first-child {
93
- margin-left: 0;
94
- }
95
- .dialog-multiple-content {
96
- flex-grow: 1;
109
+ border: 0;
110
+ background: transparent;
111
+ padding: 0.15em;
112
+ cursor: pointer;
113
+ font-size: 1.3em;
97
114
  }
98
115
 
99
116
  </style>
package/dist/Form.svelte CHANGED
@@ -15,20 +15,22 @@ setContext(CHOOSER_API_CONTEXT, chooserClient);
15
15
 
16
16
  <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
17
  <slot {messages} {saved} {validating} {submitting} {valid} {invalid} {data} {allMessages} {showingInlineErrors} />
18
- <div class="form-errors" aria-live='assertive'>
19
- {#if messages.length}
20
- <ul>
21
- {#each messages as message}
22
- <li>{message.message}</li>
23
- {/each}
24
- {#if showingInlineErrors}
25
- <li>More errors. See inline messages for details.</li>
26
- {/if}
27
- </ul>
28
- {:else if showingInlineErrors}
29
- This form contains validation errors. See inline messages for details.
30
- {/if}
31
- </div>
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.
31
+ {/if}
32
+ </div>
33
+ {/if}
32
34
  <slot name="submit" {saved} {validating} {submitting} {valid} {invalid} {allMessages} {showingInlineErrors} />
33
35
  </Form>
34
36
 
package/dist/Tab.svelte CHANGED
@@ -31,7 +31,7 @@ const last = idx === $store.tabs.length - 1;
31
31
  width: 100%;
32
32
  min-height: 50vh;
33
33
  border: var(--tabs-panel-border, var(--tabs-border, 0));
34
- padding: var(--tabs-margin-top, 2em) var(--tabs-padding-hori, 0.7em) 0.5em var(--tabs-padding-hori, 0.7em);
34
+ padding: var(--tabs-margin-top, 1.5em) var(--tabs-padding-hori, 0.7em) 0.5em var(--tabs-padding-hori, 0.7em);
35
35
  }
36
36
  .tabs-panel.accordion {
37
37
  border-left: 0;
@@ -132,7 +132,6 @@ const previewId = randomid();
132
132
  min-width: calc(100% - 21em);
133
133
  height: calc(100% - 4em);
134
134
  background-color: white;
135
- padding: 0.5em;
136
135
  overflow: auto;
137
136
  }
138
137
  .dialog-chooser-preview {
@@ -94,6 +94,7 @@ export interface Asset extends Item {
94
94
  type: 'asset';
95
95
  title?: string;
96
96
  mime: string;
97
+ extension: string;
97
98
  bytes: number;
98
99
  url: string;
99
100
  image?: {
@@ -62,4 +62,5 @@ export declare class ChooserStore<F = any> extends Store<IAssetStore> {
62
62
  setSource(name?: string, init?: boolean): void;
63
63
  }
64
64
  export declare function cleanUrl(url: string): string;
65
+ export declare function humanFileType(mime: string, extension: string): string;
65
66
  export {};
@@ -115,3 +115,12 @@ export function cleanUrl(url) {
115
115
  }
116
116
  }
117
117
  }
118
+ export function humanFileType(mime, extension) {
119
+ if (mime.startsWith('image/') || mime.startsWith('video/'))
120
+ return mime;
121
+ if (mime.startsWith('text/'))
122
+ return 'text - ' + extension;
123
+ if (extension === 'js')
124
+ return 'javascript';
125
+ return extension;
126
+ }
@@ -1,9 +1,9 @@
1
- <script>import { bytesToHuman, cleanUrl } from './ChooserStore';
1
+ <script>import { bytesToHuman, cleanUrl, humanFileType } from './ChooserStore';
2
2
  export let item;
3
3
  export let singleLine = false;
4
4
  </script>
5
5
 
6
- <dl class="dialog-chooser-info" aria-live="polite" class:multiLine={!singleLine}>
6
+ <dl class="dialog-chooser-info" aria-live="polite" class:multiLine={!singleLine} class:asset={item.type === 'asset'}>
7
7
  {#if item.type === 'raw' && item.id}
8
8
  <div class="top-row">
9
9
  <dt>External Link:</dt>
@@ -29,7 +29,7 @@ export let singleLine = false;
29
29
  {/if}
30
30
  <div>
31
31
  <dt>File Type:</dt>
32
- <dd>{item.mime}</dd>
32
+ <dd>{humanFileType(item.mime, item.extension)}</dd>
33
33
  </div>
34
34
  <div>
35
35
  <dt>File Size:</dt>
@@ -58,7 +58,7 @@ export let singleLine = false;
58
58
  display: flex;
59
59
  flex-wrap: wrap;
60
60
  justify-content: flex-start;
61
- gap: 1em 1.5em;
61
+ gap: 1em;
62
62
  padding: 0;
63
63
  margin: 0;
64
64
  list-style: none;
@@ -72,5 +72,13 @@ export let singleLine = false;
72
72
  .multiLine .top-row {
73
73
  width: 100%;
74
74
  }
75
+ .top-row {
76
+ max-width: calc(100% - 9em);
77
+ }
78
+ dl.asset .top-row {
79
+ max-width: 30em;
80
+ white-space: nowrap;
81
+ text-overflow: ellipsis;
82
+ }
75
83
 
76
84
  </style>
@@ -25,6 +25,7 @@ export let larger = false;
25
25
  position: relative;
26
26
  width: 100%;
27
27
  padding-top: 75%;
28
+ margin-right: 0.5em;
28
29
  }
29
30
  .dialog-chooser-thumbnail img {
30
31
  width: 100%;
@@ -32,10 +33,8 @@ export let larger = false;
32
33
  object-fit: scale-down;
33
34
  }
34
35
  .dialog-chooser-thumbnail :global(svg) {
35
- width: 60%;
36
- height: 60%;
37
- min-width: 4.5em;
38
- min-height: 4.5em;
36
+ width: 80%;
37
+ height: 80%;
39
38
  object-fit: scale-down;
40
39
  }
41
40
  .dialog-chooser-thumbnail > :global(*) {
@@ -125,6 +125,7 @@ function onDeleteFile(file) {
125
125
  }
126
126
  input[type="file"] {
127
127
  opacity: 0;
128
+ display: block;
128
129
  }
129
130
  label {
130
131
  display: inline-block;
@@ -203,7 +203,7 @@ export class TreeStore extends ActiveStore {
203
203
  }
204
204
  async openAndRefresh(item, notify = true) {
205
205
  await this.refresh(item, true);
206
- item.open = !!item.children?.length;
206
+ this.value.itemsById[item.id].open = !!item.children?.length;
207
207
  if (notify)
208
208
  this.trigger();
209
209
  }
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": "0.0.69",
4
+ "version": "0.0.71",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",
@@ -27,7 +27,7 @@
27
27
  "@iconify-icons/mdi": "^1.2.22",
28
28
  "@iconify-icons/ph": "^1.2.2",
29
29
  "@txstate-mws/svelte-components": "^1.5.3",
30
- "@txstate-mws/svelte-forms": "^1.3.4",
30
+ "@txstate-mws/svelte-forms": "^1.3.6",
31
31
  "codemirror": "^6.0.1",
32
32
  "txstate-utils": "^1.8.0"
33
33
  },