@kubex/zinc 1.1.57 → 1.1.59

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.
@@ -62,6 +62,39 @@ Use the `label` attribute to add a form control label above the toggle.
62
62
  <zn-toggle label="User Preferences">Enable email notifications</zn-toggle>
63
63
  ```
64
64
 
65
+ ### Label Position
66
+
67
+ Use the `label-position` attribute to control where the label sits relative to the toggle. Available positions are `top` (default), `left`, and `right`.
68
+
69
+ With `left`, the label sits at the start of the row and the toggle is pushed to the far right — useful for settings lists. With `right`, the layout is mirrored.
70
+
71
+ ```html:preview
72
+ <zn-toggle label="Top label (default)"></zn-toggle>
73
+ <br />
74
+ <zn-toggle label="Left label" label-position="left"></zn-toggle>
75
+ <br />
76
+ <zn-toggle label="Right label" label-position="right"></zn-toggle>
77
+ ```
78
+
79
+ Combine `label-position` with the `inline` attribute to keep the label and toggle adjacent instead of spread across the full width.
80
+
81
+ ```html:preview
82
+ <zn-toggle label="Inline left" label-position="left" inline></zn-toggle>
83
+ <br />
84
+ <zn-toggle label="Inline right" label-position="right" inline></zn-toggle>
85
+ ```
86
+
87
+ A settings list using `label-position="left"`:
88
+
89
+ ```html:preview
90
+ <div style="max-width: 320px;">
91
+ <zn-toggle label="Apple" label-position="left"></zn-toggle>
92
+ <zn-toggle label="Windows" label-position="left"></zn-toggle>
93
+ <zn-toggle label="Android" label-position="left"></zn-toggle>
94
+ <zn-toggle label="Blackberry" label-position="left" checked></zn-toggle>
95
+ </div>
96
+ ```
97
+
65
98
  ### On/Off Text with Tooltip
66
99
 
67
100
  Use the `on-text` and `off-text` attributes to display tooltips that show when the toggle is in the on or off state. This provides additional context for the toggle's current state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubex/zinc",
3
- "version": "1.1.57",
3
+ "version": "1.1.59",
4
4
  "description": "A collection of web components for building web applications based off of @shoelace-style/Shoelace",
5
5
  "keywords": [
6
6
  "web components",
@@ -112,6 +112,7 @@
112
112
  /* Border radii */
113
113
  --zn-border-radius: 6px;
114
114
  --zn-border-radius-small: 2px;
115
+ --zn-border-radius-x-large: var(--zn-border-radius); // For backwards compat modal border radius
115
116
  --zn-border-radius-circle: 50%;
116
117
  --zn-border-radius-pill: 9999px;
117
118
 
@@ -74,7 +74,7 @@ export default class ZnCols extends ZincElement {
74
74
  });
75
75
 
76
76
  return html`
77
- <div class="${classMap({
77
+ <div part="base" class="${classMap({
78
78
  'cols': true,
79
79
  'cols--no-gap': this.noGap,
80
80
  'cols--border': this.border,
@@ -83,3 +83,7 @@
83
83
  grid-column: span 5 / span 5;
84
84
  }
85
85
  }
86
+
87
+ zn-cols::part(base) {
88
+ gap: 70px;
89
+ }
@@ -2,6 +2,7 @@ import { classMap } from "lit/directives/class-map.js";
2
2
  import { type CSSResultGroup, html, type PropertyValues, unsafeCSS } from 'lit';
3
3
  import { defaultValue } from "../../internal/default-value";
4
4
  import { FormControlController } from "../../internal/form";
5
+ import { HasSlotController } from "../../internal/slot";
5
6
  import { ifDefined } from "lit/directives/if-defined.js";
6
7
  import { live } from "lit/directives/live.js";
7
8
  import { property, query, state } from 'lit/decorators.js';
@@ -20,6 +21,7 @@ import styles from './toggle.scss';
20
21
  * @event zn-input - Emitted when the toggle receives input.
21
22
  *
22
23
  * @slot - The toggle's label.
24
+ * @slot help-text - Text that describes how to use the toggle. Alternatively, you can use the `help-text` attribute.
23
25
  *
24
26
  * @csspart base - The component's base wrapper containing the toggle switch.
25
27
  * @csspart control - The toggle switch control (the circular button that slides).
@@ -29,6 +31,8 @@ import styles from './toggle.scss';
29
31
  export default class ZnToggle extends ZincElement implements ZincFormControl {
30
32
  static styles: CSSResultGroup = unsafeCSS(styles);
31
33
 
34
+ private readonly hasSlotController = new HasSlotController(this, 'help-text');
35
+
32
36
  private readonly formControlController = new FormControlController(this, {
33
37
  value: (control: ZnToggle) => (control.checked ? control.value || 'on' : undefined),
34
38
  defaultValue: (control: ZnToggle) => control.defaultChecked,
@@ -69,6 +73,8 @@ export default class ZnToggle extends ZincElement implements ZincFormControl {
69
73
 
70
74
  @property() label: string = '';
71
75
 
76
+ @property({ attribute: 'label-position', reflect: true }) labelPosition: 'top' | 'left' | 'right' = 'top';
77
+
72
78
  @property({ type: Boolean }) inline: boolean = false;
73
79
 
74
80
  get validity() {
@@ -167,6 +173,7 @@ export default class ZnToggle extends ZincElement implements ZincFormControl {
167
173
 
168
174
  const tooltipContent = this.checked ? this.onText : this.offText;
169
175
  const showTooltip = !!tooltipContent;
176
+ const hasHelpText = this.helpText ? true : this.hasSlotController.test('help-text');
170
177
 
171
178
  const toggle = html`
172
179
  <div class="switch__input-wrapper" part="base">
@@ -195,13 +202,20 @@ export default class ZnToggle extends ZincElement implements ZincFormControl {
195
202
 
196
203
  return html`
197
204
  <div class="${classMap({
205
+ 'form-control': true,
206
+ 'form-control--small': this.size === 'small',
207
+ 'form-control--medium': this.size === 'medium',
208
+ 'form-control--large': this.size === 'large',
209
+ 'form-control--has-help-text': hasHelpText,
198
210
  'switch__wrapper': true,
199
211
  'switch__wrapper--disabled': this.disabled,
200
212
  'switch__wrapper--has-focus': this.hasFocus,
201
213
  'switch__wrapper--small': this.size === 'small',
202
214
  'switch__wrapper--medium': this.size === 'medium',
203
215
  'switch__wrapper--large': this.size === 'large',
204
- 'switch__wrapper--inline': this.inline
216
+ 'switch__wrapper--inline': this.inline,
217
+ 'switch__wrapper--label-left': this.labelPosition === 'left',
218
+ 'switch__wrapper--label-right': this.labelPosition === 'right'
205
219
  })}">
206
220
  <label>
207
221
  ${this.label ? html`<p class="switch-label">${this.label}</p>` : ''}
@@ -211,6 +225,12 @@ export default class ZnToggle extends ZincElement implements ZincFormControl {
211
225
  ${toggle}
212
226
  </zn-tooltip>`}
213
227
  </label>
228
+ <div part="form-control-help-text"
229
+ id="help-text"
230
+ class="form-control__help-text"
231
+ aria-hidden=${hasHelpText ? 'false' : 'true'}>
232
+ <slot name="help-text">${this.helpText}</slot>
233
+ </div>
214
234
  </div>`;
215
235
  }
216
236
  }
@@ -1,11 +1,22 @@
1
1
  @use "../../wc";
2
+ @use "../../form-control";
2
3
 
3
4
  :host {
4
5
  --zn-toggle-margin: 8px 0;
6
+ display: block;
5
7
  }
6
8
 
7
9
  .switch__wrapper {
8
- display: inline-flex;
10
+ display: flex;
11
+ flex-direction: column;
12
+
13
+ label {
14
+ flex: 1;
15
+ display: flex;
16
+ flex-direction: column;
17
+ align-items: flex-start;
18
+ cursor: pointer;
19
+ }
9
20
  }
10
21
 
11
22
  .switch__input-wrapper {
@@ -19,8 +30,8 @@
19
30
  cursor: pointer;
20
31
  border-radius: 12px;
21
32
  align-items: center;
22
- background-color: rgba(0, 0, 0, 0.15);
23
- border: 0.92px solid transparent;
33
+ background-color: rgb(var(--zn-color-tab));
34
+ border: 0.92px solid rgb(var(--zn-color-tab));
24
35
  margin: var(--zn-toggle-margin);
25
36
  transition: color .2s ease-in-out, background-color .4s ease-in-out, border-color .2s ease-in-out;
26
37
  }
@@ -34,7 +45,7 @@
34
45
  border-color: rgb(var(--zn-color-primary));
35
46
 
36
47
  .switch__control {
37
- transform: translate(16px, -50%);
48
+ left: calc(100% - 20.32px - 0.92px);
38
49
  }
39
50
  }
40
51
 
@@ -47,22 +58,34 @@
47
58
  }
48
59
 
49
60
  .switch-label {
50
- font-size: 90%;
61
+ margin: 0;
62
+ color: var(--zn-input-label-color);
63
+ font-size: var(--zn-input-label-font-size-medium);
64
+ font-weight: var(--zn-input-label-font-weight);
65
+ line-height: var(--zn-input-label-line-height);
66
+ }
67
+
68
+ .switch__wrapper--small .switch-label {
69
+ font-size: var(--zn-input-label-font-size-small);
70
+ }
71
+
72
+ .switch__wrapper--large .switch-label {
73
+ font-size: var(--zn-input-label-font-size-large);
51
74
  }
52
75
 
53
76
  .switch__control {
54
77
  pointer-events: none;
55
78
  position: absolute;
56
79
  top: 50%;
57
- left: 3px;
58
- width: 20px;
59
- height: 20px;
80
+ left: 0.92px;
81
+ width: 20.32px;
82
+ height: 20.32px;
60
83
  border-radius: 50%;
61
84
  background: #fff;
62
- box-shadow: rgba(0, 0, 0, 0.1) 0 1px 3px 0, rgba(0, 0, 0, 0.1) 0 1px 2px -1px;
85
+ box-shadow: 0 2px 4px 0 rgba(var(--zn-color-text), 0.1);
63
86
 
64
87
  transform: translateY(-50%);
65
- transition: transform .3s ease-in-out, background-color .4s ease-in-out;
88
+ transition: left .3s ease-in-out, background-color .4s ease-in-out;
66
89
  outline: none;
67
90
  }
68
91
 
@@ -87,14 +110,36 @@
87
110
 
88
111
  :host([checked]) .switch__wrapper--small .switch__input-wrapper {
89
112
  .switch__control {
90
- transform: translate(13px, -50%);
113
+ left: calc(100% - 14px - 0.92px);
91
114
  }
92
115
  }
93
116
 
117
+ // Label position
118
+ .switch__wrapper--label-left label,
119
+ .switch__wrapper--label-right label {
120
+ align-items: center;
121
+ justify-content: space-between;
122
+ gap: 8px;
123
+ }
124
+
125
+ .switch__wrapper--label-left label {
126
+ flex-direction: row;
127
+ }
128
+
129
+ .switch__wrapper--label-right label {
130
+ flex-direction: row-reverse;
131
+ }
132
+
94
133
  // Support inline label
134
+ :host([inline]) {
135
+ display: inline-block;
136
+ }
137
+
95
138
  .switch__wrapper--inline label {
139
+ flex: none;
140
+ flex-direction: row;
96
141
  align-items: center;
97
- display: flex;
142
+ justify-content: flex-start;
98
143
  gap: 8px;
99
144
 
100
145
  .switch__input {
@@ -102,3 +147,7 @@
102
147
  margin-inline-end: 1px;
103
148
  }
104
149
  }
150
+
151
+ .switch__wrapper--inline.switch__wrapper--label-right label {
152
+ flex-direction: row-reverse;
153
+ }