@thinkpixellab-public/px-vue 4.1.13 → 4.1.14

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.
@@ -140,7 +140,7 @@ export default {
140
140
 
141
141
  methods: {
142
142
  combineIconClasses(...classNames) {
143
- return classNames.filter((c) => c).join(' ');
143
+ return classNames.filter(c => c).join(' ');
144
144
  },
145
145
  pxIconProps(attrs) {
146
146
  return propsFilter(PxIcon)(attrs);
@@ -39,6 +39,7 @@
39
39
  ref="popup"
40
40
  :class="[bem('popup'), popupClass]"
41
41
  :style="{
42
+ opacity: !popupPositioned ? 0 : null,
42
43
  pointerEvents: visibleValue ? null : 'none',
43
44
  userSelect: visibleValue ? null : 'none',
44
45
  ...(positionCss || {}),
@@ -274,6 +275,9 @@ export default {
274
275
  // mirrors visibleValue but one tick behind (so that we can trigger actions that require the popup to be in the DOM)
275
276
  visibleValueNext: false,
276
277
 
278
+ // whether the popup has been finally positioned (used to trigger opacity)
279
+ popupPositioned: false,
280
+
277
281
  // mirrors reference
278
282
  referenceValue: null,
279
283
 
@@ -357,6 +361,10 @@ export default {
357
361
 
358
362
  // delayed actions (settimeout seems to work but $nextTick doesn't)
359
363
 
364
+ if (nv) {
365
+ this.popupPositioned = false;
366
+ }
367
+
360
368
  setTimeout(() => {
361
369
  // floating-ui
362
370
 
@@ -412,6 +420,8 @@ export default {
412
420
  // see watcher for transitionVisible
413
421
  }
414
422
 
423
+ this.popupPositioned = true;
424
+
415
425
  // show and hide events (also see beforeshow/beforehide in visibleValue watcher)
416
426
 
417
427
  if (nv) {
@@ -14,6 +14,19 @@
14
14
  :style="{ backgroundColor: checkedValue ? checkedColor : uncheckedColor }"
15
15
  >
16
16
  <span :class="bem('spacer')"></span>
17
+ <transition name="px-toggle-icon">
18
+ <span v-if="$slots['icon-on'] && checkedValue" :class="bem('icon', { on: true })">
19
+ <slot name="icon-on" />
20
+ </span>
21
+ </transition>
22
+ <transition name="px-toggle-icon">
23
+ <span
24
+ v-if="$slots['icon-off'] && !checkedValue"
25
+ :class="bem('icon', { off: true })"
26
+ >
27
+ <slot name="icon-off" />
28
+ </span>
29
+ </transition>
17
30
  <span
18
31
  :class="[bem('thumb'), trackClass]"
19
32
  :style="{ backroundColor: thumbColor }"
@@ -79,10 +92,18 @@ export default {
79
92
  @use '@thinkpixellab-public/px-styles/src/modules/controls' as *;
80
93
 
81
94
  .px-toggle {
82
- --px-toggle-size: 1em;
83
- --px-toggle-unchecked: #{gray(-8, 0.33)};
84
- --px-toggle-checked: #{accent()};
85
- --px-toggle-thumb: white;
95
+ @at-root {
96
+ @include vue-transition-fade(px-toggle-icon);
97
+
98
+ :root {
99
+ --px-toggle-size: 1em;
100
+ --px-toggle-unchecked: #{gray(-8, 0.33)};
101
+ --px-toggle-checked: #{accent()};
102
+ --px-toggle-thumb: white;
103
+ --px-toggle-track-shadow: inset 0 1px 1px 0 rgba(0, 0, 0, 0.2);
104
+ --px-toggle-thumb-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.4);
105
+ }
106
+ }
86
107
 
87
108
  @include toggle-switch(
88
109
  (
@@ -90,6 +111,8 @@ export default {
90
111
  '--thumb.font-size': var(--px-toggle-size),
91
112
  '--track.background-color': var(--px-toggle-unchecked),
92
113
  '--track.font-size': var(--px-toggle-size),
114
+ '--track.box-shadow': var(--px-toggle-track-shadow),
115
+ '--thumb.box-shadow': var(--px-toggle-thumb-shadow),
93
116
  'checked.--track.background-color': var(--px-toggle-checked),
94
117
  ),
95
118
  '.px-toggle__track',
@@ -97,6 +120,26 @@ export default {
97
120
  '.px-toggle__spacer'
98
121
  );
99
122
 
123
+ &__icon {
124
+ position: absolute;
125
+ top: 0;
126
+ height: 100%;
127
+ aspect-ratio: 1;
128
+ border-radius: 50%;
129
+
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+
134
+ &--on {
135
+ left: 0;
136
+ }
137
+
138
+ &--off {
139
+ right: 0;
140
+ }
141
+ }
142
+
100
143
  &--reverse {
101
144
  flex-direction: row-reverse;
102
145
  }
@@ -163,12 +163,16 @@ export default {
163
163
  return this.mode === 'expander' ? 'button' : 'switch';
164
164
  },
165
165
  ariaChecked() {
166
- if (!this.isSwitch) return null;
166
+ if (!this.isSwitch) {
167
+ return null;
168
+ }
167
169
 
168
170
  return this.checked ? 'true' : 'false';
169
171
  },
170
172
  ariaExpanded() {
171
- if (this.isSwitch) return null;
173
+ if (this.isSwitch) {
174
+ return null;
175
+ }
172
176
 
173
177
  return this.checked ? 'true' : 'false';
174
178
  },
@@ -28,7 +28,11 @@
28
28
  v-for="item in items"
29
29
  :key="getItemKey(item)"
30
30
  :is="isVariant('radio') ? 'px-radio-button' : 'px-toggle-button'"
31
- :class="[bem('button'), buttonClass]"
31
+ :class="[
32
+ bem('button'),
33
+ buttonClass,
34
+ isItemSelected(item) ? buttonClass + '--checked' : null,
35
+ ]"
32
36
  :icon="icon"
33
37
  :iconSize="iconSize"
34
38
  :variant="isVariant('radio') ? 'default' : variant"
@@ -79,6 +83,11 @@ export default {
79
83
 
80
84
  //
81
85
  multiSelect: { type: Boolean, default: false },
86
+
87
+ /**
88
+ * When true, ensures that one item is always selected (radio button behavior)
89
+ */
90
+ requireSelection: { type: Boolean, default: true },
82
91
  },
83
92
  computed: {
84
93
  calculatedInputGroup() {
@@ -96,6 +105,15 @@ export default {
96
105
  this.selectItems(item);
97
106
  },
98
107
  itemUncheck(item) {
108
+ // Prevent unchecking if requireSelection is true and this is the last selected item
109
+ if (
110
+ this.requireSelection &&
111
+ !this.multiSelect &&
112
+ this.selectedItems.length === 1 &&
113
+ this.isItemSelected(item)
114
+ ) {
115
+ return;
116
+ }
99
117
  this.unselectItems(item);
100
118
  },
101
119
  },