@thinkpixellab-public/px-vue 4.0.19 → 4.0.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.
@@ -18,8 +18,67 @@ export default {
18
18
  return this.mediaQuery.currentBreakpoint;
19
19
  },
20
20
  },
21
-
22
21
  methods: {
22
+ /**
23
+ * Returns a value from a map that matches the current breakpoint or the nearest breakpoint.
24
+ * Nearest is determined by the searchDirection parameter.
25
+ *
26
+ * Usage:
27
+ *
28
+ * // returns 240 if mobile or smaller, 320 otherwise
29
+ * <Card :width="responsiveValue({mobile: 240}, 320)" />
30
+ *
31
+ * // returns 240 if mobile or smaller, 300 until lg, 320 otherwise
32
+ * <Card :width="responsiveValue({mobile: 240, lg: 300}, 320)" />
33
+ *
34
+ * // returns 200 if smaller than mobile, 240 if smaller than lg and 300 if bigger than lg
35
+ * <Card :width="responsiveValue({mobile: 240, lg: 300}, 200, 'down')" />
36
+ *
37
+ * // could also be used in a computed property....
38
+ * responsiveColor() {
39
+ * return this.responsiveValue({mobile: 'red', lg: 'blue'}, 'green');
40
+ * }
41
+ */
42
+
43
+ responsiveValue(lookup, fallback, searchDirection = 'up') {
44
+ const breakpoints = this.mediaQuery.breakpoints || {};
45
+ const current = this.currentBreakpoint;
46
+
47
+ if (!lookup || !Object.keys(lookup).length || current == null) {
48
+ return fallback;
49
+ }
50
+
51
+ // if the current breakpoint is in the lookup, just return the value
52
+ if (current in lookup) {
53
+ return lookup[current];
54
+ }
55
+
56
+ // convert keys to numbers so we can sort and find the nearest
57
+ lookup = Object.keys(lookup).reduce((acc, key) => {
58
+ if (key in breakpoints) {
59
+ acc[breakpoints[key]] = lookup[key];
60
+ }
61
+ return acc;
62
+ }, {});
63
+
64
+ const currentNum = breakpoints[current];
65
+
66
+ const keys = Object.keys(lookup)
67
+ .map(Number)
68
+ .sort((a, b) => (searchDirection == 'up' ? a - b : b - a));
69
+
70
+ // find the first key that is smaller than current
71
+ const match = keys.find(key => {
72
+ return searchDirection == 'up' ? key >= currentNum : key <= currentNum;
73
+ });
74
+
75
+ if (match) {
76
+ return lookup[match];
77
+ } else {
78
+ return fallback;
79
+ }
80
+ },
81
+
23
82
  isBreakpointOrSmaller(breakpoint) {
24
83
  if (this.currentBreakpoint === null) {
25
84
  return false;
@@ -69,3 +69,9 @@ export default {
69
69
  },
70
70
  };
71
71
  </script>
72
+
73
+ <style lang="scss">
74
+ @use '../styles/px.scss' as *;
75
+
76
+ @include media-breakpoint-vars;
77
+ </style>
@@ -35,6 +35,7 @@ const PxColorPicker = {
35
35
  name: 'px-color-picker',
36
36
  components: { PxFloat, PxColorPanel, PxColorPaletteButton, PxIcon },
37
37
  mixins: [PxBaseColor],
38
+ emits: ['click'],
38
39
  props: {
39
40
  /**
40
41
  * An icon to display inside the button. If a string is provided, it will be interpreted as
@@ -1,27 +1,23 @@
1
1
  <!--
2
- #VUE3 Todo
3
-
4
- Handle transitions between slides in PxSlide. Uses a hybrid css/javascript transition where
5
- fades are in css and the drag movement is done with gsap.
2
+ Describe this component...
6
3
  -->
7
4
  <template>
8
- <transition
9
- @before-enter="beforeEnter"
10
- @enter="enter"
11
- @leave="leave"
12
- v-bind="$attrs"
13
- name="px-slide-transition-fade"
14
- >
5
+ <div :class="bem({ active: active })">
15
6
  <slot />
16
- </transition>
7
+ </div>
17
8
  </template>
18
9
 
19
10
  <script>
20
11
  import gsap from 'gsap';
21
12
 
22
13
  export default {
23
- name: 'px-slide-transition',
14
+ name: 'px-slides-transition',
15
+ // components: { Component },
16
+ // mixins: [ Mixin ],
17
+
24
18
  props: {
19
+ active: { type: Boolean, default: true },
20
+
25
21
  // selector used to find children of the element that should slide during the transition
26
22
  dragSelector: { type: String, default: '[data-drag]' },
27
23
 
@@ -37,28 +33,53 @@ export default {
37
33
  // vertical or horizontal
38
34
  orientation: { type: String, default: 'horizontal' },
39
35
 
36
+ disableAnimations: { type: Boolean, default: false },
37
+
40
38
  // TODO rtlAdjust: {type: Boolean, default: false }
41
39
 
42
40
  duration: { type: Number, default: 1 },
43
41
  ease: { type: String, default: 'power3.out' },
44
42
  slideAmount: { default: '40vw' },
43
+ },
45
44
 
46
- disableAnimations: { type: Boolean, default: false },
45
+ data() {
46
+ return { initialized: false };
47
47
  },
48
48
  computed: {
49
+ currentDuration() {
50
+ if (this.disableAnimations || !this.initialized) {
51
+ return 0;
52
+ }
53
+ return this.duration;
54
+ },
49
55
  tweenShared() {
50
56
  return {
51
- duration: this.duration,
57
+ duration: this.currentDuration,
52
58
  ease: this.ease,
53
59
  };
54
60
  },
55
61
  },
62
+ watch: {
63
+ active(isActive, wasActive) {
64
+ this.updateActive(isActive, wasActive);
65
+ },
66
+ },
56
67
  mounted() {
57
- if (this.disableAnimations) {
58
- gsap.globalTimeline.timeScale(Number.MAX_VALUE);
59
- }
68
+ this.updateActive(this.active, false);
69
+ setTimeout(() => {
70
+ this.initialized = true;
71
+ }, 0);
60
72
  },
61
73
  methods: {
74
+ updateActive(isActive, wasActive) {
75
+ if (isActive && !wasActive) {
76
+ this.enter(this.$el);
77
+ }
78
+
79
+ if (!isActive && wasActive) {
80
+ this.leave(this.$el);
81
+ }
82
+ },
62
83
  directionAmount(direction, addEquals = false) {
63
84
  let dir;
64
85
  if (addEquals) {
@@ -83,14 +104,14 @@ export default {
83
104
  return element.querySelectorAll(this.dragReverseSelector);
84
105
  },
85
106
 
86
- beforeEnter() {},
87
-
88
- enter(element, done) {
107
+ enter(element) {
89
108
  const slideElements = this.slideElements(element);
90
109
 
91
110
  if (slideElements?.length) {
92
111
  const from = {};
93
- const to = { ...this.tweenShared, onComplete: done };
112
+ const to = {
113
+ ...this.tweenShared,
114
+ };
94
115
 
95
116
  if (this.orientation === 'vertical') {
96
117
  from.y = this.directionAmount(this.direction * -1);
@@ -113,13 +134,25 @@ export default {
113
134
  );
114
135
  }
115
136
  }
137
+
138
+ // fade
139
+ gsap.to(element, {
140
+ opacity: 1,
141
+ duration: this.currentDuration,
142
+ ease: this.ease,
143
+ onStart: () => {
144
+ this.$emit('before-enter');
145
+ },
146
+ onComplete: () => {
147
+ this.$emit('after-enter');
148
+ },
149
+ });
116
150
  },
117
- leave(element, done) {
151
+ leave(element) {
118
152
  const slideElements = this.slideElements(element);
119
153
 
120
154
  const to = {
121
155
  ...this.tweenShared,
122
- onComplete: done,
123
156
  };
124
157
 
125
158
  if (this.orientation === 'vertical') {
@@ -146,6 +179,18 @@ export default {
146
179
  gsap.to(slideElementsReverse, toReverse);
147
180
  }
148
181
  }
182
+
183
+ gsap.to(element, {
184
+ opacity: 0,
185
+ duration: this.currentDuration,
186
+ ease: this.ease,
187
+ onStart: () => {
188
+ this.$emit('before-leave');
189
+ },
190
+ onComplete: () => {
191
+ this.$emit('after-leave');
192
+ },
193
+ });
149
194
  },
150
195
  },
151
196
  };
@@ -153,10 +198,9 @@ export default {
153
198
 
154
199
  <style lang="scss">
155
200
  @use '../styles/px.scss' as *;
156
-
157
- @include vue-transition-fade(px-slide-transition-fade, $dur: 1s);
158
-
159
- .slide-transition {
160
- // add styles here
201
+ .px-slides-transition {
202
+ opacity: 0;
203
+ width: 100%;
204
+ height: 100%;
161
205
  }
162
206
  </style>
@@ -3,7 +3,7 @@
3
3
  -->
4
4
 
5
5
  <template>
6
- <div :class="bem(variantsMap)">
6
+ <div :class="bem({ hasOffset, orientation, ...variantsMap })">
7
7
  <div
8
8
  v-for="(item, index) in items"
9
9
  :key="getItemKey(item)"
@@ -11,14 +11,15 @@
11
11
  :class="bem('slide-wrapper', { selected: isItemSelected(item) })"
12
12
  >
13
13
  <px-slide-transition
14
+ :class="bem('transitioner')"
14
15
  :disable-animations="disableAnimations"
15
16
  :direction="direction"
16
17
  :orientation="orientation"
18
+ :active="isItemSelected(item)"
17
19
  @before-leave="transitionStart"
18
20
  @after-leave="transitionEnd"
19
- @leave-cancelled="transitionEnd"
20
21
  >
21
- <div :class="bem('slide')" v-show="isItemSelected(item)">
22
+ <div :class="bem('slide')">
22
23
  <slot
23
24
  name="slide"
24
25
  :selected="isItemSelected(item)"
@@ -148,7 +149,6 @@ export default {
148
149
  navigator.userAgent.indexOf('Win') != -1 ||
149
150
  this.$route.query?.scroll == 'windows'
150
151
  ) {
151
- //console.log('[Windows scroll values enabled.]');
152
152
  return {
153
153
  stability: stability || 8,
154
154
  sensitivity: sensitivity || 20,
@@ -198,6 +198,11 @@ export default {
198
198
  this.drag.enable();
199
199
  }
200
200
  },
201
+ orientation(nv) {
202
+ if (this.drag) {
203
+ this.drag.updateOptions({ lock: nv === 'horizontal' ? 'x' : 'y' });
204
+ }
205
+ },
201
206
  },
202
207
  mounted() {
203
208
  if (this.scrollEnabled) {
@@ -276,8 +281,10 @@ export default {
276
281
  }
277
282
 
278
283
  if (this.orientation === 'vertical') {
279
- gsap.set(this.dragElements, { y: ev.startDelta.y });
280
- gsap.set(this.dragElementsReverse, { y: -1 * ev.startDelta.y });
284
+ gsap.set(this.dragElements || [], { y: ev.startDelta.y });
285
+ if (this.dragElementsReverse?.length) {
286
+ gsap.set(this.dragElementsReverse, { y: -1 * ev.startDelta.y });
287
+ }
281
288
  } else {
282
289
  gsap.set(this.dragElements, { x: ev.startDelta.x });
283
290
  }
@@ -331,6 +338,7 @@ export default {
331
338
 
332
339
  if (this.scrollExcludeSelector) {
333
340
  const excludeElements = this.$el.querySelectorAll(this.scrollExcludeSelector);
341
+
334
342
  for (let i = 0; i < excludeElements.length; i++) {
335
343
  if (excludeElements[i].contains(e.target)) return;
336
344
  }
@@ -350,7 +358,6 @@ export default {
350
358
  }
351
359
 
352
360
  e.stopPropagation();
353
- e.preventDefault();
354
361
  }
355
362
  }
356
363
 
@@ -30,7 +30,7 @@ export default {
30
30
  multiline: { type: Boolean, default: false },
31
31
 
32
32
  // the value of the textbox
33
- value: { type: String, default: null },
33
+ text: { type: String, default: null },
34
34
 
35
35
  // whether to fire an update / v-model event on every character change ('input') or when the textbox loses focus ('change')
36
36
  updateMode: {
@@ -58,12 +58,12 @@ export default {
58
58
  },
59
59
 
60
60
  watch: {
61
- value(nv) {
61
+ text(nv) {
62
62
  this.setValue(nv);
63
63
  },
64
64
  },
65
65
  mounted() {
66
- this.setValue(this.value);
66
+ this.setValue(this.text);
67
67
  },
68
68
 
69
69
  methods: {
@@ -99,7 +99,7 @@ export default {
99
99
  this.$refs.input.textContent = val;
100
100
 
101
101
  // raise an update event
102
- this.$emit('update:value', val);
102
+ this.$emit('update:text', val);
103
103
 
104
104
  // update the caret position
105
105
  const newPos = startPos + (val.length - prevValue.length);
@@ -129,7 +129,7 @@
129
129
  >
130
130
  <template #default="v">
131
131
  <px-textbox
132
- v-model="validateOne"
132
+ v-model:text="validateOne"
133
133
  :class="[v.class, v.requiredClass, v.violationClasses]"
134
134
  />
135
135
  </template>
@@ -142,7 +142,7 @@
142
142
  >
143
143
  <template #default="v">
144
144
  <px-textbox
145
- v-model="validateTwo"
145
+ v-model:text="validateTwo"
146
146
  :class="[v.class, v.requiredClass, v.violationClasses]"
147
147
  />
148
148
  </template>
@@ -253,7 +253,7 @@
253
253
  <px-flex :vertical="true" :gap="1">
254
254
  <div>currentText: {{ currentText }}</div>
255
255
 
256
- <px-textbox v-model="currentText" placeholder="Say what you want..." />
256
+ <px-textbox v-model:text="currentText" placeholder="Say what you want..." />
257
257
 
258
258
  <px-textbox
259
259
  multiline
@@ -263,7 +263,7 @@
263
263
  />
264
264
 
265
265
  <px-textbox type="password" />
266
- <px-textbox :value="'hello@thinkpixellab.com'" type="email" />
266
+ <px-textbox :text="'hello@thinkpixellab.com'" type="email" />
267
267
  </px-flex>
268
268
  </px-test>
269
269
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkpixellab-public/px-vue",
3
- "version": "4.0.19",
3
+ "version": "4.0.21",
4
4
  "description": "General purpose Vue components and helpers that can be used across projects.",
5
5
  "author": {
6
6
  "name": "Pixel Lab"
@@ -74,6 +74,9 @@ class HeightGroup {
74
74
 
75
75
  if (this.ops.varsParent) {
76
76
  this.ops.varsParent.style.setProperty(this.getVariableName(), max + 'px');
77
+ if (this.ops.debug) {
78
+ console.log(` ${this.getVariableName()}: ${max}px`);
79
+ }
77
80
  }
78
81
 
79
82
  if (this.ops.changeCallback) {
package/utils/drag.js CHANGED
@@ -341,7 +341,8 @@ function Drag(element, options) {
341
341
  };
342
342
 
343
343
  this.updateOptions = options => {
344
- this.ops = { ...this.ops, options };
344
+ this.ops = { ...this.ops, ...options };
345
+ this.reset();
345
346
  };
346
347
 
347
348
  this.disable = () => {
package/utils/utils.js CHANGED
@@ -423,3 +423,27 @@ export function uniqueId() {
423
423
  ('000' + ((Math.random() * 46656) | 0).toString(36)).slice(-3)
424
424
  );
425
425
  }
426
+
427
+ export function findScrollParent(node) {
428
+ const isScrolling = node => {
429
+ let overflow = getComputedStyle(node, null).getPropertyValue('overflow');
430
+
431
+ return overflow.indexOf('scroll') > -1 || overflow.indexOf('auto') > -1;
432
+ };
433
+
434
+ if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
435
+ return undefined;
436
+ }
437
+
438
+ let current = node.parentNode;
439
+
440
+ while (current?.parentNode) {
441
+ if (isScrolling(current)) {
442
+ return current;
443
+ }
444
+
445
+ current = current.parentNode;
446
+ }
447
+
448
+ return document.scrollingElement || document.documentElement;
449
+ }