@kubex/zinc 1.0.14 → 1.0.16

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.
@@ -5,6 +5,7 @@ import {property, state} from "lit/decorators.js";
5
5
  import ZincElement from '../../internal/zinc-element';
6
6
 
7
7
  import styles from './note.scss';
8
+ import {HasSlotController} from "../../internal/slot";
8
9
 
9
10
  /**
10
11
  * @summary Short summary of the component's intended use.
@@ -32,36 +33,19 @@ export default class ZnNote extends ZincElement {
32
33
  @property({reflect: true}) date: string = '';
33
34
  @property({type: HTMLElement, reflect: true}) body: string = '';
34
35
 
35
- // Number of lines at which the note body collapses; 0 disables collapsing
36
- @property({type: Number, attribute: 'collapse-at-lines', reflect: true}) collapseAtLines: number = 3;
36
+ private readonly hasSlotController = new HasSlotController(this, 'caption', 'date', '[default]', 'snippet', 'footer');
37
37
 
38
- // Internal state: whether the body is currently expanded
39
- @state() private expanded = false;
38
+ @state()
39
+ private expanded: boolean = false;
40
40
 
41
- private _toggleExpand = () => {
41
+ private _toggleExpand(): void {
42
42
  this.expanded = !this.expanded;
43
- this.updateComplete.then(() => this._measureOverflow());
44
- }
45
-
46
- private _measureOverflow() {
47
- const container = this.shadowRoot?.querySelector('.note__body-container') as HTMLElement | null;
48
- if (!container || this.collapseAtLines <= 0) {
49
- return;
50
- }
51
-
52
- container.style.setProperty('--note-collapse-lines', String(this.collapseAtLines));
53
- container.classList.toggle('note__body--clamped', !this.expanded);
54
- }
55
-
56
- protected firstUpdated() {
57
- this._measureOverflow();
58
- }
59
-
60
- protected updated() {
61
- this._measureOverflow();
62
43
  }
63
44
 
64
45
  protected render(): unknown {
46
+ const showExpandableButton: boolean = this.hasSlotController.test('snippet')
47
+ && this.hasSlotController.test('[default]');
48
+
65
49
  return html`
66
50
  <div class="${classMap({
67
51
  'note': true,
@@ -80,15 +64,18 @@ export default class ZnNote extends ZincElement {
80
64
  <slot name="date" class="note__header__date">
81
65
  <small>${this.date}</small>
82
66
  </slot>
83
- <slot name="action"></slot>
84
- </div>
85
- <div class="${classMap({
86
- 'note__body-container': true,
87
- 'note__body--clamped': this.collapseAtLines > 0 && !this.expanded
88
- })}" style="--note-collapse-lines: ${this.collapseAtLines}">
89
- <slot class="note__body"></slot>
90
67
  </div>
91
- ${this.collapseAtLines > 0 ? html`
68
+
69
+ ${this.expanded && showExpandableButton ?
70
+ html`
71
+ <slot class="note__body"></slot>` :
72
+ html`
73
+ <slot name="snippet" class="note__snippet"></slot>`}
74
+
75
+ ${!showExpandableButton ? html`
76
+ <slot class="note__body"></slot>` : ' '}
77
+
78
+ ${!showExpandableButton ? '' : html`
92
79
  <div class="note__toggle">
93
80
  <zn-button color="transparent"
94
81
  size="content"
@@ -97,8 +84,7 @@ export default class ZnNote extends ZincElement {
97
84
  aria-expanded="${String(this.expanded)}">
98
85
  ${this.expanded ? 'Show less' : 'Show more'}
99
86
  </zn-button>
100
- </div>
101
- ` : ''}
87
+ </div>`}
102
88
  <slot name="footer" class="note__footer"></slot>
103
89
  </div>
104
90
  `;
@@ -64,6 +64,14 @@ $colors: (
64
64
  padding: 0;
65
65
  }
66
66
 
67
+ &__body--clamped {
68
+ display: -webkit-box;
69
+ -webkit-box-orient: vertical;
70
+ -webkit-line-clamp: var(--note-collapse-lines, 0);
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ }
74
+
67
75
  @each $name, $color in $colors {
68
76
  &--#{'' + $name} {
69
77
  .note__header__caption {
@@ -1,8 +1,8 @@
1
- import {classMap} from "lit/directives/class-map.js";
2
- import {type CSSResultGroup, html, unsafeCSS} from 'lit';
3
- import {LocalizeController} from "../../utilities/localize";
4
- import {property, query, state} from 'lit/decorators.js';
5
- import {watch} from '../../internal/watch';
1
+ import { classMap } from "lit/directives/class-map.js";
2
+ import { type CSSResultGroup, html, unsafeCSS } from 'lit';
3
+ import { LocalizeController } from "../../utilities/localize";
4
+ import { property, query, state } from 'lit/decorators.js';
5
+ import { watch } from '../../internal/watch';
6
6
  import ZincElement from '../../internal/zinc-element';
7
7
  import ZnIcon from "../icon";
8
8
 
@@ -28,7 +28,7 @@ import styles from './option.scss';
28
28
  */
29
29
  export default class ZnOption extends ZincElement {
30
30
  static styles: CSSResultGroup = unsafeCSS(styles);
31
- static dependencies = {'zn-icon': ZnIcon};
31
+ static dependencies = { 'zn-icon': ZnIcon };
32
32
 
33
33
  private cachedTextLabel: string;
34
34
  // @ts-expect-error - Controller is currently unused
@@ -39,7 +39,6 @@ export default class ZnOption extends ZincElement {
39
39
  @query('.option__label') defaultSlot: HTMLSlotElement;
40
40
 
41
41
  @state() current = false; // the user has keyed into the option, but hasn't selected it yet (shows a highlight)
42
- @state() selected = false; // the option is selected and has aria-selected="true"
43
42
  @state() hasHover = false; // we need this because Safari doesn't honor :hover styles while dragging
44
43
 
45
44
  /**
@@ -47,10 +46,12 @@ export default class ZnOption extends ZincElement {
47
46
  * from other options in the same group. Values may not contain spaces, as spaces are used as delimiters when listing
48
47
  * multiple values.
49
48
  */
50
- @property({reflect: true}) value = '';
49
+ @property({ reflect: true }) value = '';
51
50
 
52
51
  /** Draws the option in a disabled state, preventing selection. */
53
- @property({type: Boolean, reflect: true}) disabled = false;
52
+ @property({ type: Boolean, reflect: true }) disabled = false;
53
+
54
+ @property({ type: Boolean, reflect: true }) selected = false;
54
55
 
55
56
  connectedCallback() {
56
57
  super.connectedCallback();
@@ -70,7 +71,7 @@ export default class ZnOption extends ZincElement {
70
71
  // When the label changes, emit a slotchange event so parent controls see it
71
72
  if (textLabel !== this.cachedTextLabel) {
72
73
  this.cachedTextLabel = textLabel;
73
- this.emit('slotchange', {bubbles: true, composed: false, cancelable: false});
74
+ this.emit('slotchange', { bubbles: true, composed: false, cancelable: false });
74
75
  }
75
76
  }
76
77
 
@@ -68,7 +68,6 @@
68
68
 
69
69
  .row {
70
70
  background-color: rgba(var(--zn-panel-highlight), var(--zn-panel-highlight-opacity, 1));
71
- margin-top: 20px;
72
71
  }
73
72
 
74
73
  .header-item {
@@ -101,6 +100,10 @@
101
100
  @include wc.container-query(md) {
102
101
  border-bottom: none;
103
102
  }
103
+
104
+ &:first-of-type {
105
+ margin-top: 0;
106
+ }
104
107
  }
105
108
 
106
109
  .sub {
@@ -1,21 +1,22 @@
1
- import {animateTo, stopAnimations} from '../../internal/animate.js';
2
- import {classMap} from "lit/directives/class-map.js";
3
- import {type CSSResultGroup, html, nothing, type TemplateResult, unsafeCSS, PropertyValues} from 'lit';
4
- import {FormControlController} from "../../internal/form";
5
- import {getAnimation, setDefaultAnimation} from "../../utilities/animation-registry";
6
- import {HasSlotController} from "../../internal/slot";
7
- import {LocalizeController} from "../../utilities/localize";
8
- import {property, query, state} from 'lit/decorators.js';
9
- import {scrollIntoView} from "../../internal/scroll";
10
- import {unsafeHTML} from "lit/directives/unsafe-html.js";
11
- import {waitForEvent} from "../../internal/event";
12
- import {watch} from '../../internal/watch';
13
- import type {ZincFormControl} from '../../internal/zinc-element';
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';
4
+ 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';
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 {ZnRemoveEvent} from "../../events/zn-remove";
18
+ import type { ZincFormControl } from '../../internal/zinc-element';
19
+ import type { ZnRemoveEvent } from "../../events/zn-remove";
19
20
  import type ZnOption from "../option";
20
21
 
21
22
  import styles from './select.scss';
@@ -141,73 +142,73 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
141
142
  }) defaultValue: string | string[] = '';
142
143
 
143
144
  /** The select's size. */
144
- @property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium';
145
+ @property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
145
146
 
146
147
  /** Placeholder text to show as a hint when the select is empty. */
147
148
  @property() placeholder = '';
148
149
 
149
150
  /** Allows more than one option to be selected. */
150
- @property({type: Boolean, reflect: true}) multiple = false;
151
+ @property({ type: Boolean, reflect: true }) multiple = false;
151
152
 
152
153
  /** Max number of options that can be selected when `multiple` is true. Set to 0 to allow unlimited selections. */
153
- @property({attribute: 'max-options', type: Number}) maxOptions = 0;
154
+ @property({ attribute: 'max-options', type: Number }) maxOptions = 0;
154
155
 
155
156
  /**
156
157
  * The maximum number of selected options to show when `multiple` is true. After the maximum, "+n" will be shown to
157
158
  * indicate the number of additional items that are selected. Set to 0 to remove the limit.
158
159
  */
159
- @property({attribute: 'max-options-visible', type: Number}) maxOptionsVisible = 3;
160
+ @property({ attribute: 'max-options-visible', type: Number }) maxOptionsVisible = 3;
160
161
 
161
162
  /** Disables the select control. */
162
- @property({type: Boolean, reflect: true}) disabled = false;
163
+ @property({ type: Boolean, reflect: true }) disabled = false;
163
164
 
164
165
  /** Adds a clear button when the select is not empty. */
165
- @property({type: Boolean}) clearable = false;
166
+ @property({ type: Boolean }) clearable = false;
166
167
 
167
168
  /**
168
169
  * Indicates whether or not the select is open. You can toggle this attribute to show and hide the menu, or you can
169
170
  * use the `show()` and `hide()` methods and this attribute will reflect the select's open state.
170
171
  */
171
- @property({type: Boolean, reflect: true}) open = false;
172
+ @property({ type: Boolean, reflect: true }) open = false;
172
173
 
173
174
  /**
174
175
  * Enable this option to prevent the listbox from being clipped when the component is placed inside a container with
175
176
  * `overflow: auto|scroll`. Hoisting uses a fixed positioning strategy that works in many, but not all, scenarios.
176
177
  */
177
- @property({type: Boolean}) hoist = false;
178
+ @property({ type: Boolean }) hoist = false;
178
179
 
179
180
  /** Draws a pill-style select with rounded edges. */
180
- @property({type: Boolean, reflect: true}) pill = false;
181
+ @property({ type: Boolean, reflect: true }) pill = false;
181
182
 
182
183
  /** The select's label. If you need to display HTML, use the `label` slot instead. */
183
184
  @property() label = '';
184
185
 
185
186
  /** 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. */
186
- @property({attribute: 'label-tooltip'}) labelTooltip = '';
187
+ @property({ attribute: 'label-tooltip' }) labelTooltip = '';
187
188
 
188
189
  /** 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. */
189
- @property({attribute: 'context-note'}) contextNote = '';
190
+ @property({ attribute: 'context-note' }) contextNote = '';
190
191
 
191
192
  /**
192
193
  * The preferred placement of the selects menu. Note that the actual placement may vary as needed to keep the listbox
193
194
  * inside the viewport.
194
195
  */
195
- @property({reflect: true}) placement: 'top' | 'bottom' = 'bottom';
196
+ @property({ reflect: true }) placement: 'top' | 'bottom' = 'bottom';
196
197
 
197
198
  /** The select's help text. If you need to display HTML, use the `help-text` slot instead. */
198
- @property({attribute: 'help-text'}) helpText = '';
199
+ @property({ attribute: 'help-text' }) helpText = '';
199
200
 
200
201
  /**
201
202
  * By default, form controls are associated with the nearest containing `<form>` element. This attribute allows you
202
203
  * to place the form control outside of a form and associate it with the form that has this `id`. The form must be in
203
204
  * the same document or shadow root for this to work.
204
205
  */
205
- @property({reflect: true}) form: string;
206
+ @property({ reflect: true }) form: string;
206
207
 
207
208
  /** The select's required attribute. */
208
- @property({type: Boolean, reflect: true}) required = false;
209
+ @property({ type: Boolean, reflect: true }) required = false;
209
210
 
210
- @property({attribute: 'cache-key'}) cacheKey: string = "";
211
+ @property({ attribute: 'cache-key' }) cacheKey: string = "";
211
212
 
212
213
  /**
213
214
  * A function that customizes the tags to be rendered when multiple=true. The first argument is the option, the second
@@ -277,7 +278,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
277
278
  this.closeWatcher.onclose = () => {
278
279
  if (this.open) {
279
280
  this.hide();
280
- this.displayInput.focus({preventScroll: true});
281
+ this.displayInput.focus({ preventScroll: true });
281
282
  }
282
283
  };
283
284
  }
@@ -329,7 +330,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
329
330
  event.preventDefault();
330
331
  event.stopPropagation();
331
332
  this.hide();
332
- this.displayInput.focus({preventScroll: true});
333
+ this.displayInput.focus({ preventScroll: true });
333
334
  }
334
335
 
335
336
  // Handle enter and space. When pressing space, we allow for type to select behaviors so if there's anything in the
@@ -361,7 +362,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
361
362
 
362
363
  if (!this.multiple) {
363
364
  this.hide();
364
- this.displayInput.focus({preventScroll: true});
365
+ this.displayInput.focus({ preventScroll: true });
365
366
  }
366
367
  }
367
368
 
@@ -468,7 +469,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
468
469
  }
469
470
 
470
471
  event.preventDefault();
471
- this.displayInput.focus({preventScroll: true});
472
+ this.displayInput.focus({ preventScroll: true });
472
473
  this.open = !this.open;
473
474
  }
474
475
 
@@ -486,7 +487,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
486
487
 
487
488
  if (this.value !== '') {
488
489
  this.setSelectedOptions([]);
489
- this.displayInput.focus({preventScroll: true});
490
+ this.displayInput.focus({ preventScroll: true });
490
491
 
491
492
  // Emit after update
492
493
  this.updateComplete.then(() => {
@@ -525,7 +526,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
525
526
  }
526
527
 
527
528
  // Set focus after updating so the value is announced by screen readers
528
- this.updateComplete.then(() => this.displayInput.focus({preventScroll: true}));
529
+ this.updateComplete.then(() => this.displayInput.focus({ preventScroll: true }));
529
530
 
530
531
  if (this.value !== oldValue) {
531
532
  // Emit after updating
@@ -537,7 +538,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
537
538
 
538
539
  if (!this.multiple) {
539
540
  this.hide();
540
- this.displayInput.focus({preventScroll: true});
541
+ this.displayInput.focus({ preventScroll: true });
541
542
  }
542
543
  }
543
544
  }
@@ -558,6 +559,18 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
558
559
 
559
560
  // Select only the options that match the new value
560
561
  this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
562
+
563
+ // of check if an option has selected attribute set initially
564
+ if (!this.valueHasChanged) {
565
+ const initiallySelectedOptions = allOptions.filter(el => el.hasAttribute('selected'));
566
+ if (initiallySelectedOptions.length) {
567
+ if (this.multiple) {
568
+ this.setSelectedOptions(initiallySelectedOptions);
569
+ } else {
570
+ this.setSelectedOptions(initiallySelectedOptions[0]);
571
+ }
572
+ }
573
+ }
561
574
  }
562
575
 
563
576
  private handleTagRemove(event: ZnRemoveEvent, option: ZnOption) {
@@ -718,9 +731,8 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
718
731
  }
719
732
 
720
733
  if (this.conditional !== "") {
721
- const conditionalSelect = document.querySelector(`zn-select[id="${this.conditional}"]`) as ZnSelect;
722
- console.log('conditionalSelect', conditionalSelect);
723
- if (conditionalSelect) {
734
+ const conditionalSelectList = deepQuerySelectorAll(`#${this.conditional}`, document.documentElement, '') as ZnSelect[];
735
+ conditionalSelectList.forEach((conditionalSelect) => {
724
736
  // disable if the other has any options selected
725
737
  conditionalSelect.addEventListener('zn-change', () => {
726
738
  let linkedValues = Array.isArray(conditionalSelect.value) ? conditionalSelect.value : [conditionalSelect.value];
@@ -731,11 +743,11 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
731
743
 
732
744
  // trigger the event once to initialize
733
745
  conditionalSelect.dispatchEvent(new Event('zn-input'));
734
- }
746
+ });
735
747
  }
736
748
  }
737
749
 
738
- @watch('disabled', {waitUntilFirstUpdate: true})
750
+ @watch('disabled', { waitUntilFirstUpdate: true })
739
751
  handleDisabledChange() {
740
752
  // Close the listbox when the control is disabled
741
753
  if (this.disabled) {
@@ -756,7 +768,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
756
768
  }
757
769
  }
758
770
 
759
- @watch(['defaultValue', 'value'], {waitUntilFirstUpdate: true})
771
+ @watch(['defaultValue', 'value'], { waitUntilFirstUpdate: true })
760
772
  handleValueChange() {
761
773
  if (!this.valueHasChanged) {
762
774
  const cachedValueHasChanged = this.valueHasChanged;
@@ -772,7 +784,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
772
784
  this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
773
785
  }
774
786
 
775
- @watch('open', {waitUntilFirstUpdate: true})
787
+ @watch('open', { waitUntilFirstUpdate: true })
776
788
  async handleOpenChange() {
777
789
  if (this.open && !this.disabled) {
778
790
  // Reset the current option
@@ -791,7 +803,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
791
803
  this.setCurrentOption(this.currentOption);
792
804
  });
793
805
 
794
- const {keyframes, options} = getAnimation(this, 'select.show', {dir: this.localize.dir()});
806
+ const { keyframes, options } = getAnimation(this, 'select.show', { dir: this.localize.dir() });
795
807
  await animateTo(this.popup.popup, keyframes, options);
796
808
 
797
809
  // Make sure the current option is scrolled into view (required for Safari)
@@ -806,7 +818,7 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
806
818
  this.removeOpenListeners();
807
819
 
808
820
  await stopAnimations(this);
809
- const {keyframes, options} = getAnimation(this, 'select.hide', {dir: this.localize.dir()});
821
+ const { keyframes, options } = getAnimation(this, 'select.hide', { dir: this.localize.dir() });
810
822
  await animateTo(this.popup.popup, keyframes, options);
811
823
  this.listbox.hidden = true;
812
824
  this.popup.active = false;
@@ -1037,16 +1049,16 @@ export default class ZnSelect extends ZincElement implements ZincFormControl {
1037
1049
 
1038
1050
  setDefaultAnimation('select.show', {
1039
1051
  keyframes: [
1040
- {opacity: 0, scale: 0.9},
1041
- {opacity: 1, scale: 1}
1052
+ { opacity: 0, scale: 0.9 },
1053
+ { opacity: 1, scale: 1 }
1042
1054
  ],
1043
- options: {duration: 100, easing: 'ease'}
1055
+ options: { duration: 100, easing: 'ease' }
1044
1056
  });
1045
1057
 
1046
1058
  setDefaultAnimation('select.hide', {
1047
1059
  keyframes: [
1048
- {opacity: 1, scale: 1},
1049
- {opacity: 0, scale: 0.9}
1060
+ { opacity: 1, scale: 1 },
1061
+ { opacity: 0, scale: 0.9 }
1050
1062
  ],
1051
- options: {duration: 100, easing: 'ease'}
1063
+ options: { duration: 100, easing: 'ease' }
1052
1064
  });
@@ -20,6 +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
24
  @property() font = '';
24
25
  @property() width = '';
25
26
  @property() height = '';
@@ -30,7 +31,7 @@ export default class ZnStyle extends ZincElement {
30
31
  connectedCallback() {
31
32
  super.connectedCallback();
32
33
 
33
- let display = 'contents';
34
+ let display = this.display || 'contents';
34
35
 
35
36
  if (this.color === '') {
36
37
  if (this.error) {
@@ -165,6 +166,11 @@ export default class ZnStyle extends ZincElement {
165
166
  }
166
167
  }
167
168
 
169
+ if (this.display) {
170
+ // Force attribute display
171
+ display = this.display
172
+ }
173
+
168
174
  this.style.display = display;
169
175
  }
170
176
 
@@ -43,6 +43,8 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
43
43
  });
44
44
  private readonly hasSlotController = new HasSlotController(this, 'help-text', 'label');
45
45
  private resizeObserver: ResizeObserver;
46
+ /** Ensures we only attempt to derive the initial value from light DOM content once */
47
+ private _didInitFromContent = false;
46
48
 
47
49
  @query('.form-control-input') formControl: HTMLElement;
48
50
  @query('.textarea__control') input: HTMLTextAreaElement;
@@ -94,6 +96,8 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
94
96
  */
95
97
  @property({reflect: true}) form = '';
96
98
 
99
+ @property({type: Boolean, reflect: true}) flush = false;
100
+
97
101
  /** Makes the textarea a required field. */
98
102
  @property({type: Boolean, reflect: true}) required = false;
99
103
 
@@ -155,6 +159,34 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
155
159
 
156
160
  connectedCallback() {
157
161
  super.connectedCallback();
162
+ // Initialize the value from the element's light DOM text content when no value attribute/property is set.
163
+ // This allows usage like: <zn-textarea>Initial content</zn-textarea>
164
+ if (!this._didInitFromContent) {
165
+ this._didInitFromContent = true;
166
+
167
+ const hasValueAttribute = this.hasAttribute('value');
168
+ const hasProgrammaticValue = typeof this.value === 'string' && this.value.length > 0;
169
+
170
+ if (!hasValueAttribute && !hasProgrammaticValue) {
171
+ // Collect only top-level text nodes to avoid pulling in slotted element text (e.g., label/help-text)
172
+ const textNodes = Array.from(this.childNodes).filter(n => n.nodeType === Node.TEXT_NODE);
173
+ const raw = textNodes.map(n => n.textContent ?? '').join('');
174
+ const content = raw.replace(/\r\n/g, '\n').trim();
175
+
176
+ if (content.length > 0) {
177
+ this.value = content;
178
+ this.defaultValue = content;
179
+ // Remove the consumed text nodes to prevent stray light DOM text
180
+ textNodes.forEach(n => {
181
+ // Only remove nodes that actually contributed non-whitespace content
182
+ if ((n.textContent ?? '').trim().length > 0) {
183
+ n.parentNode?.removeChild(n);
184
+ }
185
+ });
186
+ }
187
+ }
188
+ }
189
+
158
190
  this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
159
191
 
160
192
  this.updateComplete.then(() => {
@@ -360,7 +392,10 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
360
392
  name="context-note">${this.contextNote}</slot></span>`
361
393
  : ''}
362
394
 
363
- <div part="form-control-input" class="form-control-input">
395
+ <div part="form-control-input" class=${classMap({
396
+ "form-control-input": true,
397
+ "form-control-input--flush": this.flush
398
+ })}>
364
399
  <div
365
400
  part="base"
366
401
  class=${classMap({
@@ -121,7 +121,7 @@
121
121
  }
122
122
 
123
123
  .textarea--medium .textarea__control {
124
- padding: 0.75em var(--zn-input-spacing-medium);
124
+ padding: var(--zn-input-spacing-small);
125
125
  }
126
126
 
127
127
  .textarea--large {
@@ -151,7 +151,7 @@
151
151
  overflow-y: hidden;
152
152
  }
153
153
 
154
- .form-control-input {
154
+ .form-control-input--flush {
155
155
  margin: 0 !important;
156
156
  }
157
157