@kubex/zinc 1.0.17 → 1.0.19

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.
@@ -1,22 +1,23 @@
1
- import { animateTo, stopAnimations } from '../../internal/animate.js';
2
- import { classMap } from "lit/directives/class-map.js";
3
- import { type CSSResultGroup, html, nothing, PropertyValues, type TemplateResult, unsafeCSS } from 'lit';
1
+ import {animateTo, stopAnimations} from '../../internal/animate.js';
2
+ import {classMap} from "lit/directives/class-map.js";
4
3
  import {deepQuerySelectorAll} from "../../utilities/query";
5
- import { FormControlController } from "../../internal/form";
6
- import { getAnimation, setDefaultAnimation } from "../../utilities/animation-registry";
7
- import { HasSlotController } from "../../internal/slot";
8
- import { LocalizeController } from "../../utilities/localize";
9
- import { property, query, state } from 'lit/decorators.js';
10
- import { scrollIntoView } from "../../internal/scroll";
11
- import { unsafeHTML } from "lit/directives/unsafe-html.js";
12
- import { waitForEvent } from "../../internal/event";
13
- import { watch } from '../../internal/watch';
4
+ import {FormControlController} from "../../internal/form";
5
+ import {getAnimation, setDefaultAnimation} from "../../utilities/animation-registry";
6
+ import {HasSlotController} from "../../internal/slot";
7
+ import {html, nothing, unsafeCSS} from 'lit';
8
+ import {LocalizeController} from "../../utilities/localize";
9
+ import {property, query, state} from 'lit/decorators.js';
10
+ import {scrollIntoView} from "../../internal/scroll";
11
+ import {unsafeHTML} from "lit/directives/unsafe-html.js";
12
+ import {waitForEvent} from "../../internal/event";
13
+ import {watch} from '../../internal/watch';
14
14
  import ZincElement from '../../internal/zinc-element';
15
15
  import ZnChip from "../chip";
16
16
  import ZnIcon from "../icon";
17
17
  import ZnPopup from "../popup";
18
- import type { ZincFormControl } from '../../internal/zinc-element';
19
- import type { ZnRemoveEvent } from "../../events/zn-remove";
18
+ import type {CSSResultGroup, PropertyValues, TemplateResult} from 'lit';
19
+ import type {ZincFormControl} from '../../internal/zinc-element';
20
+ import type {ZnRemoveEvent} from "../../events/zn-remove";
20
21
  import type ZnOption from "../option";
21
22
 
22
23
  import styles from './select.scss';
@@ -90,12 +91,14 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
90
91
  @query('.select__display-input') displayInput: HTMLInputElement;
91
92
  @query('.select__value-input') valueInput: HTMLInputElement;
92
93
  @query('.select__listbox') listbox: HTMLSlotElement;
94
+ @query('slot[name="prefix"]') prefixSlot: HTMLSlotElement;
93
95
 
94
96
  @state() private hasFocus = false;
95
97
  @state() displayLabel = '';
96
98
  @state() currentOption: ZnOption;
97
99
  @state() selectedOptions: ZnOption[] = [];
98
100
  @state() private valueHasChanged: boolean = false;
101
+ @state() private inputPrefix: boolean = false;
99
102
 
100
103
  /** The name of the select, submitted as a name/value pair with form data. */
101
104
  @property() name = '';
@@ -142,73 +145,75 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
142
145
  }) defaultValue: string | string[] = '';
143
146
 
144
147
  /** The select's size. */
145
- @property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
148
+ @property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium';
146
149
 
147
150
  /** Placeholder text to show as a hint when the select is empty. */
148
151
  @property() placeholder = '';
149
152
 
150
153
  /** Allows more than one option to be selected. */
151
- @property({ type: Boolean, reflect: true }) multiple = false;
154
+ @property({type: Boolean, reflect: true}) multiple = false;
152
155
 
153
156
  /** Max number of options that can be selected when `multiple` is true. Set to 0 to allow unlimited selections. */
154
- @property({ attribute: 'max-options', type: Number }) maxOptions = 0;
157
+ @property({attribute: 'max-options', type: Number}) maxOptions = 0;
155
158
 
156
159
  /**
157
160
  * The maximum number of selected options to show when `multiple` is true. After the maximum, "+n" will be shown to
158
161
  * indicate the number of additional items that are selected. Set to 0 to remove the limit.
159
162
  */
160
- @property({ attribute: 'max-options-visible', type: Number }) maxOptionsVisible = 3;
163
+ @property({attribute: 'max-options-visible', type: Number}) maxOptionsVisible = 3;
161
164
 
162
165
  /** Disables the select control. */
163
- @property({ type: Boolean, reflect: true }) disabled = false;
166
+ @property({type: Boolean, reflect: true}) disabled = false;
164
167
 
165
168
  /** Adds a clear button when the select is not empty. */
166
- @property({ type: Boolean }) clearable = false;
169
+ @property({type: Boolean}) clearable = false;
167
170
 
168
171
  /**
169
172
  * Indicates whether or not the select is open. You can toggle this attribute to show and hide the menu, or you can
170
173
  * use the `show()` and `hide()` methods and this attribute will reflect the select's open state.
171
174
  */
172
- @property({ type: Boolean, reflect: true }) open = false;
175
+ @property({type: Boolean, reflect: true}) open = false;
173
176
 
174
177
  /**
175
178
  * Enable this option to prevent the listbox from being clipped when the component is placed inside a container with
176
179
  * `overflow: auto|scroll`. Hoisting uses a fixed positioning strategy that works in many, but not all, scenarios.
177
180
  */
178
- @property({ type: Boolean }) hoist = false;
181
+ @property({type: Boolean}) hoist = false;
179
182
 
180
183
  /** Draws a pill-style select with rounded edges. */
181
- @property({ type: Boolean, reflect: true }) pill = false;
184
+ @property({type: Boolean, reflect: true}) pill = false;
182
185
 
183
186
  /** The select's label. If you need to display HTML, use the `label` slot instead. */
184
187
  @property() label = '';
185
188
 
186
189
  /** Text that appears in a tooltip next to the label. If you need to display HTML in the tooltip, use the `label-tooltip` slot instead. */
187
- @property({ attribute: 'label-tooltip' }) labelTooltip = '';
190
+ @property({attribute: 'label-tooltip'}) labelTooltip = '';
188
191
 
189
192
  /** Text that appears above the input, on the right, to add additional context. If you need to display HTML in this text, use the `context-note` slot instead. */
190
- @property({ attribute: 'context-note' }) contextNote = '';
193
+ @property({attribute: 'context-note'}) contextNote = '';
191
194
 
192
195
  /**
193
196
  * The preferred placement of the selects menu. Note that the actual placement may vary as needed to keep the listbox
194
197
  * inside the viewport.
195
198
  */
196
- @property({ reflect: true }) placement: 'top' | 'bottom' = 'bottom';
199
+ @property({reflect: true}) placement: 'top' | 'bottom' = 'bottom';
197
200
 
198
201
  /** The select's help text. If you need to display HTML, use the `help-text` slot instead. */
199
- @property({ attribute: 'help-text' }) helpText = '';
202
+ @property({attribute: 'help-text'}) helpText = '';
200
203
 
201
204
  /**
202
205
  * By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
203
206
  * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
204
207
  * the same document or shadow root for this to work.
205
208
  */
206
- @property({ reflect: true }) form: string;
209
+ @property({reflect: true}) form: string;
207
210
 
208
211
  /** The select's required attribute. */
209
- @property({ type: Boolean, reflect: true }) required = false;
212
+ @property({type: Boolean, reflect: true}) required = false;
210
213
 
211
- @property({ attribute: 'cache-key' }) cacheKey: string = "";
214
+ @property({attribute: 'cache-key'}) cacheKey: string = "";
215
+
216
+ @property({type: Boolean, attribute: 'trigger-submit'}) triggerSubmit = false;
212
217
 
213
218
  /**
214
219
  * A function that customizes the tags to be rendered when multiple=true. The first argument is the option, the second
@@ -266,6 +271,11 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
266
271
  }
267
272
  }
268
273
 
274
+ private updateHasInputPrefix() {
275
+ const assigned = this.prefixSlot?.assignedElements({flatten: true}) || [];
276
+ this.inputPrefix = assigned.length > 0;
277
+ }
278
+
269
279
  private addOpenListeners() {
270
280
  const root = this.getRootNode();
271
281
  root.addEventListener('focusin', this.handleDocumentFocusIn);
@@ -278,7 +288,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
278
288
  this.closeWatcher.onclose = () => {
279
289
  if (this.open) {
280
290
  this.hide();
281
- this.displayInput.focus({ preventScroll: true });
291
+ this.displayInput.focus({preventScroll: true});
282
292
  }
283
293
  };
284
294
  }
@@ -330,7 +340,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
330
340
  event.preventDefault();
331
341
  event.stopPropagation();
332
342
  this.hide();
333
- this.displayInput.focus({ preventScroll: true });
343
+ this.displayInput.focus({preventScroll: true});
334
344
  }
335
345
 
336
346
  // Handle enter and space. When pressing space, we allow for type to select behaviors so if there's anything in the
@@ -362,7 +372,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
362
372
 
363
373
  if (!this.multiple) {
364
374
  this.hide();
365
- this.displayInput.focus({ preventScroll: true });
375
+ this.displayInput.focus({preventScroll: true});
366
376
  }
367
377
  }
368
378
 
@@ -463,13 +473,15 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
463
473
 
464
474
  // not the keyboard_arrow_down icon
465
475
  const isExpandIcon = path.some(el => el instanceof Element && el.classList.contains('select__expand-icon'));
476
+ // if click is inside the prefix area (e.g. checkbox), don't toggle the select
477
+ const inPrefix = path.some(el => el instanceof Element && el.classList.contains('select__prefix'));
466
478
  // Ignore disabled controls and clicks on tags (remove buttons)
467
- if ((this.disabled || isIcon) && !isExpandIcon) {
479
+ if (((this.disabled || isIcon) && !isExpandIcon) || inPrefix) {
468
480
  return;
469
481
  }
470
482
 
471
483
  event.preventDefault();
472
- this.displayInput.focus({ preventScroll: true });
484
+ this.displayInput.focus({preventScroll: true});
473
485
  this.open = !this.open;
474
486
  }
475
487
 
@@ -478,8 +490,14 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
478
490
  return;
479
491
  }
480
492
 
481
- event.stopPropagation();
482
- this.handleDocumentKeyDown(event);
493
+ // If keydown originated from within the prefix (e.g. checkbox)
494
+ const path = event.composedPath();
495
+ const inPrefix = path.some(el => el instanceof Element && (el as Element).classList?.contains('select__prefix'));
496
+
497
+ if (!inPrefix) {
498
+ event.stopPropagation();
499
+ this.handleDocumentKeyDown(event);
500
+ }
483
501
  }
484
502
 
485
503
  private handleClearClick(event: MouseEvent) {
@@ -487,7 +505,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
487
505
 
488
506
  if (this.value !== '') {
489
507
  this.setSelectedOptions([]);
490
- this.displayInput.focus({ preventScroll: true });
508
+ this.displayInput.focus({preventScroll: true});
491
509
 
492
510
  // Emit after update
493
511
  this.updateComplete.then(() => {
@@ -526,7 +544,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
526
544
  }
527
545
 
528
546
  // Set focus after updating so the value is announced by screen readers
529
- this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true }));
547
+ this.updateComplete.then(() => this.displayInput.focus({preventScroll: true}));
530
548
 
531
549
  if (this.value !== oldValue) {
532
550
  // Emit after updating
@@ -538,7 +556,11 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
538
556
 
539
557
  if (!this.multiple) {
540
558
  this.hide();
541
- this.displayInput.focus({ preventScroll: true });
559
+ this.displayInput.focus({preventScroll: true});
560
+ }
561
+
562
+ if (this.triggerSubmit) {
563
+ this.formControlController.submit();
542
564
  }
543
565
  }
544
566
  }
@@ -745,9 +767,11 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
745
767
  conditionalSelect.dispatchEvent(new Event('zn-input'));
746
768
  });
747
769
  }
770
+
771
+ this.updateHasInputPrefix();
748
772
  }
749
773
 
750
- @watch('disabled', { waitUntilFirstUpdate: true })
774
+ @watch('disabled', {waitUntilFirstUpdate: true})
751
775
  handleDisabledChange() {
752
776
  // Close the listbox when the control is disabled
753
777
  if (this.disabled) {
@@ -768,7 +792,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
768
792
  }
769
793
  }
770
794
 
771
- @watch(['defaultValue', 'value'], { waitUntilFirstUpdate: true })
795
+ @watch(['defaultValue', 'value'], {waitUntilFirstUpdate: true})
772
796
  handleValueChange() {
773
797
  if (!this.valueHasChanged) {
774
798
  const cachedValueHasChanged = this.valueHasChanged;
@@ -784,7 +808,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
784
808
  this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
785
809
  }
786
810
 
787
- @watch('open', { waitUntilFirstUpdate: true })
811
+ @watch('open', {waitUntilFirstUpdate: true})
788
812
  async handleOpenChange() {
789
813
  if (this.open && !this.disabled) {
790
814
  // Reset the current option
@@ -803,7 +827,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
803
827
  this.setCurrentOption(this.currentOption);
804
828
  });
805
829
 
806
- const { keyframes, options } = getAnimation(this, 'select.show', { dir: this.localize.dir() });
830
+ const {keyframes, options} = getAnimation(this, 'select.show', {dir: this.localize.dir()});
807
831
  await animateTo(this.popup.popup, keyframes, options);
808
832
 
809
833
  // Make sure the current option is scrolled into view (required for Safari)
@@ -818,7 +842,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
818
842
  this.removeOpenListeners();
819
843
 
820
844
  await stopAnimations(this);
821
- const { keyframes, options } = getAnimation(this, 'select.hide', { dir: this.localize.dir() });
845
+ const {keyframes, options} = getAnimation(this, 'select.hide', {dir: this.localize.dir()});
822
846
  await animateTo(this.popup.popup, keyframes, options);
823
847
  this.listbox.hidden = true;
824
848
  this.popup.active = false;
@@ -942,7 +966,8 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
942
966
  'select--bottom': this.placement === 'bottom',
943
967
  'select--small': this.size === 'small',
944
968
  'select--medium': this.size === 'medium',
945
- 'select--large': this.size === 'large'
969
+ 'select--large': this.size === 'large',
970
+ 'select--has-input-prefix': this.inputPrefix
946
971
  })}
947
972
  placement=${this.placement}
948
973
  strategy=${this.hoist ? 'fixed' : 'absolute'}
@@ -957,7 +982,10 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
957
982
  slot="anchor"
958
983
  @keydown=${this.handleComboboxKeyDown}
959
984
  @mousedown=${this.handleComboboxMouseDown}>
960
- <slot part="prefix" name="prefix" class="select__prefix"></slot>
985
+ <slot part="prefix"
986
+ name="prefix"
987
+ class="select__prefix"
988
+ @slotchange=${() => this.updateHasInputPrefix()}></slot>
961
989
 
962
990
  <input
963
991
  part="display-input"
@@ -1049,16 +1077,16 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
1049
1077
 
1050
1078
  setDefaultAnimation('select.show', {
1051
1079
  keyframes: [
1052
- { opacity: 0, scale: 0.9 },
1053
- { opacity: 1, scale: 1 }
1080
+ {opacity: 0, scale: 0.9},
1081
+ {opacity: 1, scale: 1}
1054
1082
  ],
1055
- options: { duration: 100, easing: 'ease' }
1083
+ options: {duration: 100, easing: 'ease'}
1056
1084
  });
1057
1085
 
1058
1086
  setDefaultAnimation('select.hide', {
1059
1087
  keyframes: [
1060
- { opacity: 1, scale: 1 },
1061
- { opacity: 0, scale: 0.9 }
1088
+ {opacity: 1, scale: 1},
1089
+ {opacity: 0, scale: 0.9}
1062
1090
  ],
1063
- options: { duration: 100, easing: 'ease' }
1091
+ options: {duration: 100, easing: 'ease'}
1064
1092
  });
@@ -48,6 +48,14 @@
48
48
  var(--zn-transition-fast) background-color;
49
49
  }
50
50
 
51
+ /* Prefix area: allow a leading control (e.g., checkbox) full-height */
52
+ .select__prefix {
53
+ display: flex;
54
+ align-items: center;
55
+ align-self: stretch;
56
+ height: 100%;
57
+ }
58
+
51
59
  .select__display-input {
52
60
  position: relative;
53
61
  width: 100%;
@@ -175,6 +183,10 @@
175
183
  padding-inline: var(--zn-input-spacing-medium);
176
184
  }
177
185
 
186
+ .select--has-input-prefix.select--medium .select__combobox {
187
+ padding-inline-start: 0;
188
+ }
189
+
178
190
  .select--medium .select__clear {
179
191
  margin-inline-start: var(--zn-input-spacing-medium);
180
192
  }
@@ -200,6 +212,10 @@
200
212
  padding-inline: var(--zn-input-spacing-large);
201
213
  }
202
214
 
215
+ .select--has-input-prefix.select--large .select__combobox {
216
+ padding-inline-start: 0;
217
+ }
218
+
203
219
  .select--large .select__clear {
204
220
  margin-inline-start: var(--zn-input-spacing-large);
205
221
  }
@@ -241,6 +257,11 @@
241
257
  color: var(--zn-input-icon-color);
242
258
  }
243
259
 
260
+ /* When a checkbox is used in the prefix, remove left padding so the prefix joins flush */
261
+ .select--has-input-prefix.select--standard .select__combobox {
262
+ padding-inline-start: 0;
263
+ }
264
+
244
265
  /* Clear button */
245
266
  .select__clear {
246
267
  display: inline-flex;
@@ -20,7 +20,7 @@ export default class ZnStyle extends ZincElement {
20
20
  @property({type: Boolean}) primary = false;
21
21
  @property({type: Boolean}) accent = false;
22
22
  @property({type: Boolean}) center = false;
23
- @property({type: String}) display = 'contents';
23
+ @property({type: String}) display = null;
24
24
  @property() font = '';
25
25
  @property() width = '';
26
26
  @property() height = '';
package/src/zinc.ts CHANGED
@@ -82,6 +82,7 @@ export {default as FilterWrapper} from './components/filter-wrapper';
82
82
  export {default as SettingsContainer} from './components/settings-container';
83
83
  export { default as FilterContainer } from './components/filter-container';
84
84
  export { default as Reveal } from './components/reveal';
85
+ export { default as AudioSelect } from './components/audio-select';
85
86
  /* plop:component */
86
87
 
87
88
  // Base Component