@dosgato/dialog 1.1.19 → 1.1.21

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.
@@ -60,10 +60,10 @@ onDestroy(() => {
60
60
  {#if helptext}
61
61
  <!-- svelte-ignore a11y-click-events-have-key-events -->
62
62
  <!-- svelte-ignore a11y-no-static-element-interactions -->
63
- <div use:resize={{ debounce: 10 }} on:resize={setNeedsShowHelp} id={helptextid} class="dialog-field-help" class:needsShowHelp class:expanded={showhelp} on:click={() => { if (needsShowHelp) showhelp = !showhelp }}>
64
- <span bind:this={helpelement}>{@html helptext}</span>
63
+ <div use:resize={{ debounce: 10 }} on:resize={setNeedsShowHelp} class="dialog-field-help" class:needsShowHelp class:expanded={showhelp} on:click={() => { if (needsShowHelp) showhelp = !showhelp }}>
64
+ <span bind:this={helpelement} id={helptextid}>{@html helptext}</span>
65
65
  {#if needsShowHelp}
66
- <button type="button" class="dialog-field-help-expand">Show {#if showhelp}Less{:else}More{/if}<ScreenReaderOnly>, ignore this, the help text it controls will be read to you as input description</ScreenReaderOnly></button>
66
+ <button type="button" class="dialog-field-help-expand">Show {#if showhelp}Less{:else}More{/if}<ScreenReaderOnly>, ignore this, the help text it controls will be fully read to you as input description</ScreenReaderOnly></button>
67
67
  {/if}
68
68
  </div>
69
69
  {/if}
@@ -49,7 +49,7 @@ function onChangeCheckbox(setVal, choice, included) {
49
49
  }
50
50
  const descid = randomid();
51
51
  function reactToChoices(..._) {
52
- const choiceSet = new Set(choices?.map(c => c.value));
52
+ const choiceSet = new Set(choices?.filter(c => !c.disabled).map(c => c.value));
53
53
  const val = get($store, finalPath);
54
54
  const filtered = val?.filter(v => choiceSet.has(v));
55
55
  if (filtered?.length !== val?.length)
@@ -1,6 +1,6 @@
1
1
  <script>import { FORM_CONTEXT, FORM_INHERITED_PATH, Field } from '@txstate-mws/svelte-forms';
2
2
  import { getContext, onMount } from 'svelte';
3
- import { get, isNotBlank } from 'txstate-utils';
3
+ import { equal, get, isNotBlank } from 'txstate-utils';
4
4
  import Switcher from './Switcher.svelte';
5
5
  let className = '';
6
6
  export { className as class };
@@ -36,7 +36,7 @@ async function reactToChoices(..._) {
36
36
  return await store.setField(finalPath, finalDeserialize(''));
37
37
  }
38
38
  const val = get($store.data, finalPath);
39
- if (!choices.some(o => o.value === finalDeserialize(val)))
39
+ if (!choices.some(o => equal(o.value, val)))
40
40
  await store.setField(finalPath, notNull ? defaultValue : finalDeserialize(''));
41
41
  }
42
42
  $: reactToChoices(choices).catch(console.error);
@@ -1,5 +1,5 @@
1
1
  <script>import { resize, ScreenReaderOnly } from '@txstate-mws/svelte-components';
2
- import { onMount } from 'svelte';
2
+ 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';
@@ -16,6 +16,7 @@ const store = new CropperStore({ width: 0, height: 0, minSelection, targetAspect
16
16
  const { output, outputPct, selection } = store;
17
17
  let setVal;
18
18
  let value;
19
+ const initialAspectRatio = selectionAspectRatio;
19
20
  function init(spValue, spSetVal) {
20
21
  setVal = spSetVal;
21
22
  value = spValue;
@@ -114,6 +115,18 @@ $: updateRect(container);
114
115
  const descid = randomid();
115
116
  const movedescid = randomid();
116
117
  let focusWithin = false;
118
+ let arChanged = false;
119
+ async function reactToAspectRatio(ar) {
120
+ if (!ar)
121
+ return;
122
+ store.updateTargetAspect(ar);
123
+ await tick();
124
+ if (ar !== initialAspectRatio || arChanged) {
125
+ store.maximize();
126
+ arChanged = true;
127
+ }
128
+ }
129
+ $: void reactToAspectRatio(selectionAspectRatio);
117
130
  </script>
118
131
 
119
132
  <svelte:window on:mousemove={onMouseMove} on:mouseup={onMouseUp} on:touchend={onMouseUp} on:touchcancel={onMouseUp} />
@@ -49,6 +49,7 @@ export declare class CropperStore extends Store<ICropperStore> {
49
49
  constructor(initialValue: Omit<ICropperStore, 'cursor'>);
50
50
  setOutput(selection?: CropOutput): void;
51
51
  updateDimensions(width: number, height: number): void;
52
+ updateTargetAspect(ar: number): void;
52
53
  /**
53
54
  * The svelte component is responsible for making sure x and y are relative to the drawing area,
54
55
  * e.g. (0, 0) is the top left corner of the drawing area
@@ -18,6 +18,9 @@ export class CropperStore extends Store {
18
18
  updateDimensions(width, height) {
19
19
  this.update(v => ({ ...v, width, height }));
20
20
  }
21
+ updateTargetAspect(ar) {
22
+ this.update(v => ({ ...v, targetAspect: ar }));
23
+ }
21
24
  /**
22
25
  * The svelte component is responsible for making sure x and y are relative to the drawing area,
23
26
  * e.g. (0, 0) is the top left corner of the drawing area
@@ -221,7 +221,7 @@ function onKeyDown(e) {
221
221
  border-radius: 10px;
222
222
  border: 1px solid #999;
223
223
  height: 30px;
224
- padding: 0px 0px 0px 30px;
224
+ padding: 0.5em;
225
225
  font-family: 'Nunito Sans', sans-serif !important;
226
226
  }
227
227
 
@@ -234,18 +234,33 @@ function onKeyDown(e) {
234
234
 
235
235
  /* Styles for the icon picker grid */
236
236
 
237
- .icon-picker-items{
237
+ .icon-picker-items {
238
238
  margin-top: 5px;
239
239
  padding: 5px;
240
+ display: grid;
241
+ grid-template-columns: repeat(7, 1fr);
242
+ row-gap: 1em;
243
+ column-gap: 1em;
244
+ justify-items: center;
245
+ align-items: center;
246
+ }
247
+
248
+ @media (max-width: 800px) {
249
+ .icon-picker-items {
250
+ grid-template-columns: repeat(5, 1fr);
251
+ }
252
+ }
253
+
254
+ @media (max-width: 500px) {
255
+ .icon-picker-items {
256
+ grid-template-columns: repeat(4, 1fr);
257
+ }
240
258
  }
241
259
 
242
260
  .icon-picker-item {
243
- float: left;
244
- margin: 0px 11px 12px 11px;
245
261
  text-align: center;
246
262
  cursor: pointer;
247
263
  color: inherit;
248
- width: 10%;
249
264
  position: relative;
250
265
  font-size: 28px;
251
266
  }
@@ -0,0 +1,193 @@
1
+ <script>import { isNotBlank, randomid } from 'txstate-utils';
2
+ import FieldStandard from '../FieldStandard.svelte';
3
+ import Button from '../Button.svelte';
4
+ import arrowsIn from '@iconify-icons/ph/arrows-in';
5
+ import { Dialog } from '..';
6
+ import { onMount } from 'svelte';
7
+ import { modifierKey, ScreenReaderOnly } from '@txstate-mws/svelte-components';
8
+ export let id = undefined;
9
+ export let path;
10
+ export let imageSrc;
11
+ export let label = '';
12
+ export let required = false;
13
+ export let conditional = undefined;
14
+ export let helptext = undefined;
15
+ export let info = undefined;
16
+ export let defaultValue = { x: 50, y: 50 };
17
+ let initialVal;
18
+ function init(v) {
19
+ if (v) {
20
+ initialVal = v;
21
+ }
22
+ else {
23
+ initialVal = defaultValue;
24
+ }
25
+ }
26
+ const boxes = [];
27
+ const descid = randomid();
28
+ const labelid = randomid();
29
+ let modalOpen = false;
30
+ function showModal() {
31
+ modalOpen = true;
32
+ }
33
+ function hideModal() {
34
+ modalOpen = false;
35
+ }
36
+ let x, y;
37
+ onMount(() => {
38
+ x = initialVal?.x / 25;
39
+ y = initialVal?.y / 25;
40
+ });
41
+ function onSave(setVal) {
42
+ setVal({ x: x * 25, y: y * 25 });
43
+ hideModal();
44
+ }
45
+ function onSelectBox(newX, newY) {
46
+ x = newX;
47
+ y = newY;
48
+ boxes[x + y * 5].focus();
49
+ }
50
+ function onKeyDown(e) {
51
+ if (modifierKey(e))
52
+ return;
53
+ if (e.key === 'ArrowDown') {
54
+ e.preventDefault();
55
+ y = Math.min(y + 1, 4);
56
+ }
57
+ else if (e.key === 'ArrowUp') {
58
+ e.preventDefault();
59
+ y = Math.max(y - 1, 0);
60
+ }
61
+ else if (e.key === 'ArrowRight') {
62
+ e.preventDefault();
63
+ x = Math.min(x + 1, 4);
64
+ }
65
+ else if (e.key === 'ArrowLeft') {
66
+ e.preventDefault();
67
+ x = Math.max(0, x - 1);
68
+ }
69
+ boxes[y + x * 5].focus();
70
+ }
71
+ const positionText = {
72
+ 0: {
73
+ 0: 'Top Left Corner',
74
+ 1: 'Upper Left',
75
+ 2: 'Middle Left',
76
+ 3: 'Lower Left',
77
+ 4: 'Bottom Left Corner'
78
+ },
79
+ 1: {
80
+ 0: 'Top Left of Center',
81
+ 1: 'Upper Left of Center',
82
+ 2: 'Middle Left of Center',
83
+ 3: 'Below Left of Center',
84
+ 4: 'Bottom, Left of Center'
85
+ },
86
+ 2: {
87
+ 0: 'Top Center',
88
+ 1: 'Upper Center',
89
+ 2: 'Dead Center',
90
+ 3: 'Below Center',
91
+ 4: 'Bottom Center'
92
+ },
93
+ 3: {
94
+ 0: 'Top, Right of Center',
95
+ 1: 'Upper Right of Center',
96
+ 2: 'Middle Right of Center',
97
+ 3: 'Below Right of Center',
98
+ 4: 'Bottom, Right of Center'
99
+ },
100
+ 4: {
101
+ 0: 'Top Right Corner',
102
+ 1: 'Upper Right',
103
+ 2: 'Middle Right',
104
+ 3: 'Lower Right',
105
+ 4: 'Bottom Right Corner'
106
+ }
107
+ };
108
+ </script>
109
+
110
+ <FieldStandard bind:id {label} {path} {required} {defaultValue} conditional={conditional && isNotBlank(imageSrc)} {helptext} {descid} let:value let:setVal let:helptextid>
111
+ {@const _ = init(value)}
112
+ {#if isNotBlank(imageSrc)}
113
+ <Button icon={arrowsIn} on:click={showModal}>Adjust Image Position</Button>
114
+ {#if modalOpen}
115
+ <Dialog size="large" title={label} on:escape={hideModal} continueText="Save" cancelText="Cancel" on:continue={() => onSave(setVal)} {labelid}>
116
+ {#if info}
117
+ <section class="info">
118
+ {info}
119
+ </section>
120
+ <section class="position">
121
+ <p>
122
+ Using the grid overlays, select a focal point in your image to determine how Gato will align, position, and scale your image
123
+ in the section. This will help ensure the focal point of your image is always in frame. By default, Gato will
124
+ use the center of the image.
125
+ </p>
126
+ <div class="image-container">
127
+ <img class="crop-image" src={imageSrc} alt="" />
128
+ <div class="overlay" role="radiogroup" aria-labelledby={labelid}>
129
+ {#each Array.from(Array(5).keys()) as col}
130
+ {#each Array.from(Array(5).keys()) as row}
131
+ <div
132
+ bind:this={boxes[col + row * 5]}
133
+ class="box"
134
+ class:side={row === 4}
135
+ class:bottom={col === 4}
136
+ role="radio"
137
+ aria-checked={row === x && col === y}
138
+ tabindex={row === x && col === y ? 0 : -1}
139
+ on:click={() => onSelectBox(row, col)} on:keydown={onKeyDown}><ScreenReaderOnly>{positionText[row][col]}</ScreenReaderOnly></div>
140
+ {/each}
141
+ {/each}
142
+ </div>
143
+ </div>
144
+ </section>
145
+ {/if}
146
+ </Dialog>
147
+ {/if}
148
+ {/if}
149
+ </FieldStandard>
150
+
151
+ <style>
152
+ section.position {
153
+ border: 1px dashed #767676;
154
+ padding: 1em;
155
+ display: flex;
156
+ flex-direction: column;
157
+ align-items: center;
158
+ }
159
+ .image-container {
160
+ width: 75%;
161
+ position: relative;
162
+ }
163
+ .crop-image {
164
+ width: 100%;
165
+ display: block;
166
+ }
167
+ .overlay {
168
+ position: absolute;
169
+ top: 0;
170
+ left: 0;
171
+ width: 100%;
172
+ height: 100%;
173
+ display: grid;
174
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
175
+ grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
176
+ }
177
+ .overlay .box {
178
+ background-color: rgba(0, 0, 0, 0.5);
179
+ border-bottom: 1px dashed white;
180
+ border-right: 1px dashed white;
181
+ }
182
+ .overlay .box[aria-checked="true"] {
183
+ background-color: rgba(255, 255, 0, 0.5);
184
+ }
185
+ .overlay .box.side {
186
+ border-right-width: 0;
187
+ }
188
+ .overlay .box.bottom {
189
+ border-bottom-width: 0;
190
+ }
191
+
192
+ </style>
193
+
@@ -0,0 +1,25 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import { type ImagePositionOutput } from '..';
3
+ declare const __propDef: {
4
+ props: {
5
+ id?: string | undefined;
6
+ path: string;
7
+ imageSrc: string | undefined;
8
+ label?: string | undefined;
9
+ required?: boolean | undefined;
10
+ conditional?: boolean | undefined;
11
+ helptext?: string | undefined;
12
+ info?: string | undefined;
13
+ defaultValue?: ImagePositionOutput | undefined;
14
+ };
15
+ events: {
16
+ [evt: string]: CustomEvent<any>;
17
+ };
18
+ slots: {};
19
+ };
20
+ export type FieldImagePositionProps = typeof __propDef.props;
21
+ export type FieldImagePositionEvents = typeof __propDef.events;
22
+ export type FieldImagePositionSlots = typeof __propDef.slots;
23
+ export default class FieldImagePosition extends SvelteComponentTyped<FieldImagePositionProps, FieldImagePositionEvents, FieldImagePositionSlots> {
24
+ }
25
+ export {};
@@ -0,0 +1,2 @@
1
+ export { default as FieldImagePosition } from './FieldImagePosition.svelte';
2
+ export * from './position.js';
@@ -0,0 +1,2 @@
1
+ export { default as FieldImagePosition } from './FieldImagePosition.svelte';
2
+ export * from './position.js';
@@ -0,0 +1,4 @@
1
+ export interface ImagePositionOutput {
2
+ x: number;
3
+ y: number;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -42,3 +42,4 @@ export * from './helpers.js';
42
42
  export * from './tree/index.js';
43
43
  export * from './cropper/index.js';
44
44
  export * from './code/index.js';
45
+ export * from './imageposition/index.js';
package/dist/index.js CHANGED
@@ -42,3 +42,4 @@ export * from './helpers.js';
42
42
  export * from './tree/index.js';
43
43
  export * from './cropper/index.js';
44
44
  export * from './code/index.js';
45
+ export * from './imageposition/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.19",
4
+ "version": "1.1.21",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",
@@ -26,7 +26,7 @@
26
26
  "@iconify/svelte": "^3.1.6",
27
27
  "@iconify-icons/mdi": "^1.2.22",
28
28
  "@iconify-icons/ph": "^1.2.2",
29
- "@txstate-mws/svelte-components": "^1.5.5",
29
+ "@txstate-mws/svelte-components": "^1.6.2",
30
30
  "@txstate-mws/svelte-forms": "^1.4.2",
31
31
  "codemirror": "^6.0.1",
32
32
  "txstate-utils": "^1.8.0"