@thinkpixellab-public/px-vue 3.0.5 → 3.0.7

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,7 +1,7 @@
1
1
  <!--
2
2
  Keeps a child element contained within boundaries.
3
3
 
4
- <px-contain #default="{width, height, left: }" :aspect="16/9" :round="true" :px="true">
4
+ <px-contain #default="{width, height, left: }" :aspect="16/9" :round="true" :to-px="true">
5
5
  <div :style="{position: 'absolute', left: left top: top width: width height: height}" />
6
6
  </px-contain>
7
7
 
@@ -18,9 +18,7 @@
18
18
  :width="contain.width"
19
19
  :height="contain.height"
20
20
  :scale="contain.scale"
21
- >
22
- <!-- fallback -->
23
- </slot>
21
+ ></slot>
24
22
  </div>
25
23
  </template>
26
24
 
@@ -46,6 +44,19 @@ export default {
46
44
  naturalWidth: { type: Number, default: null },
47
45
  naturalHeight: { type: Number, default: null },
48
46
 
47
+ // these are adjustments to the available area inside of the container rectangle; the
48
+ // difference between padding and border is that padding values will also affect the final
49
+ // top/left values but border does not (to visualize this, imagine that you use border if
50
+ // the content has a visible border around and the final values reflect that where padding
51
+ // is empty space so palcement gets shfited); specify as either a number or an arry of up to
52
+ // 4 numbers that follow the css box-model pattern
53
+ padding: { default: null },
54
+ border: { default: null },
55
+
56
+ // max limits fo width/height of the contain rectangle
57
+ maxWidth: { type: Number, default: null },
58
+ maxHeight: { type: Number, default: null },
59
+
49
60
  // the horizontal positioning when there is leftover space
50
61
  alignX: { default: 0.5 }, // number | 'start' | 'center' | 'end'
51
62
 
@@ -53,13 +64,16 @@ export default {
53
64
  alignY: { default: 0.5 }, // number | 'start' | 'center' | 'end'
54
65
 
55
66
  // whether slot values should be returned as strings with 'px'
56
- px: { type: Boolean, default: true },
67
+ toPx: { type: Boolean, default: true },
57
68
 
58
69
  // whether slot values should be rounded to screen pixels
59
70
  round: { type: Boolean, default: true },
60
71
  },
61
72
  data() {
62
73
  return {
74
+ // use setContainer to use a container source other than this.$el
75
+ container: null,
76
+
63
77
  contain: {
64
78
  current: false,
65
79
  width: 0,
@@ -116,6 +130,9 @@ export default {
116
130
  // no width or height or aspect
117
131
  return null;
118
132
  },
133
+ containerValue() {
134
+ return this.container || this.$el;
135
+ },
119
136
  },
120
137
 
121
138
  watch: {
@@ -124,39 +141,127 @@ export default {
124
141
  },
125
142
  },
126
143
  methods: {
144
+ // Use a source for the outer container size other than this.$el. The container argument can
145
+ // be an element or any object that implements getBoundingClientRect; setting a custom
146
+ // container may also require a direct call to resize when the container size updates (since
147
+ // the default resize observer behavior only observes this.$el)
148
+ setContainer(container) {
149
+ this.container = container;
150
+ this.resize();
151
+ },
127
152
  resize() {
128
- let outer = this.$el.getBoundingClientRect();
153
+ let p = this.toBoxModelObject(this.padding);
154
+ let b = this.toBoxModelObject(this.border);
155
+
156
+ let outer = this.containerValue.getBoundingClientRect();
129
157
  let inner = this.aspectRect || outer;
130
158
 
131
- const c = contain(
132
- inner,
133
- outer,
134
- this.alignToDecimal(this.alignX),
135
- this.alignToDecimal(this.alignY)
136
- );
159
+ outer = {
160
+ width: outer.width - (b.w + p.w),
161
+ height: outer.height - (b.h + p.h),
162
+ };
163
+
164
+ // max width/height
165
+
166
+ if (this.maxHeight && outer.height > this.maxHeight) {
167
+ outer.y = (outer.height - this.maxHeight) * this.alignToDecimal(this.alignY);
168
+ outer.height = this.maxHeight;
169
+ }
170
+
171
+ if (this.maxWidth && outer.width > this.maxWidth) {
172
+ outer.x = (outer.width - this.maxWidth) * this.alignToDecimal(this.alignX);
173
+ outer.width = this.maxWidth;
174
+ }
175
+
176
+ // get contain
177
+
178
+ let r;
137
179
 
138
- this.contain = {
180
+ if (this.aspect) {
181
+ r = contain(
182
+ inner,
183
+ outer,
184
+ this.alignToDecimal(this.alignX),
185
+ this.alignToDecimal(this.alignY)
186
+ );
187
+ } else {
188
+ r = {
189
+ x: 0,
190
+ y: 0,
191
+ width: outer.width,
192
+ height: outer.height,
193
+ };
194
+ }
195
+
196
+ let c = {
139
197
  current: true,
140
- width: this.prepValue(c.width),
141
- height: this.prepValue(c.height),
142
- top: this.prepValue(c.y),
143
- right: this.prepValue(c.width - c.x),
144
- bottom: this.prepValue((c.height = c.y)),
145
- left: this.prepValue(c.x),
146
- scale: this.naturalWidth || this.naturalHeight ? c.scale : 1,
198
+ width: r.width + b.w,
199
+ height: r.height + b.h,
200
+ top: r.y + p.top,
201
+ left: r.x + p.left,
202
+ right: r.width - (r.x + b.w),
203
+ bottom: r.height - (r.y + b.h),
204
+ scale: this.naturalWidth || this.naturalHeight ? r.scale : 1,
147
205
  };
206
+
207
+ this.$emit('update', c);
208
+
209
+ c.width = this.prepValue(c.width);
210
+ c.height = this.prepValue(c.height);
211
+ c.left = this.prepValue(c.left);
212
+ c.top = this.prepValue(c.top);
213
+ c.right = this.prepValue(c.right);
214
+ c.bottom = this.prepValue(c.bottom);
215
+
216
+ this.contain = c;
148
217
  },
149
218
 
150
219
  prepValue(n) {
151
220
  if (this.round && typeof window != 'undefined') {
152
221
  n = Math.round(n * window.devicePixelRatio) / window.devicePixelRatio;
153
222
  }
154
- if (this.px) {
223
+ if (this.toPx) {
155
224
  n = this.cssPx(n);
156
225
  }
157
226
  return n;
158
227
  },
159
228
 
229
+ toBoxModelObject(val) {
230
+ const arr = this.toBoxModelArray(val);
231
+ return {
232
+ top: arr[0],
233
+ right: arr[1],
234
+ bottom: arr[2],
235
+ left: arr[3],
236
+ w: arr[1] + arr[3],
237
+ h: arr[0] + arr[2],
238
+ };
239
+ },
240
+
241
+ toBoxModelArray(val) {
242
+ if (!val) {
243
+ return [0, 0, 0, 0];
244
+ }
245
+
246
+ if (Array.isArray(val)) {
247
+ if (val.length == 0) {
248
+ return [0, 0, 0, 0];
249
+ }
250
+ if (val.length == 1) {
251
+ return [val[0], val[0], val[0], val[0]];
252
+ }
253
+ if (val.length == 2) {
254
+ return [val[0], val[1], val[0], val[1]];
255
+ }
256
+ if (val.length == 3) {
257
+ return [val[0], val[1], val[2], val[1]];
258
+ }
259
+ return [val[0], val[1], val[2], val[3]];
260
+ }
261
+
262
+ return [val, val, val, val];
263
+ },
264
+
160
265
  alignToDecimal(align) {
161
266
  if (typeof align == 'number') {
162
267
  return align;
@@ -22,10 +22,10 @@
22
22
  :name="calcTransitionName"
23
23
  @before-enter="transitionVisible = true"
24
24
  @after-leave="transitionVisible = false"
25
- :style="{ zIndex: zStart + currentLayerIndex }"
26
25
  >
27
26
  <div
28
- :class="bem({ ...variantsMap, absolute: strategy == 'absolute' })"
27
+ :class="[rootClass, bem({ ...variantsMap, absolute: strategy == 'absolute' })]"
28
+ :style="{ ...(rootStyle || {}), zIndex: zStart + currentLayerIndex }"
29
29
  v-show="visibleValueNext"
30
30
  >
31
31
  <!-- overlay element -->
@@ -34,6 +34,7 @@
34
34
  :class="[bem('overlay'), overlayClass]"
35
35
  @click="overlayClick"
36
36
  v-bind="overlayAttributes"
37
+ ref="overlay"
37
38
  :style="{
38
39
  pointerEvents: visibleValue ? null : 'none',
39
40
  userSelect: visibleValue ? null : 'none',
@@ -113,11 +114,17 @@ export default {
113
114
  // to ensure that the last opened from the same starting z will be on top of each other)
114
115
  zStart: { type: Number, default: 999999 },
115
116
 
117
+ // class set on the root element (equivalent of setting class if not using a portal)
118
+ rootClass: { type: String, default: null },
119
+
120
+ // style set on the root element (equivalent of setting style if not using a portal)
121
+ rootStyle: { default: null },
122
+
116
123
  // An additional class set on the popup element
117
124
  popupClass: { type: String, default: '' },
118
125
 
119
126
  // Style object that gets merged onto the popup
120
- popupStyle: { type: Object, default: null },
127
+ popupStyle: { default: null },
121
128
 
122
129
  // Whether to render an overlay element
123
130
  overlayEnabled: { type: Boolean, default: true },
@@ -222,6 +229,13 @@ export default {
222
229
  // emit update when visibleValue changes (enables .sync on parent)
223
230
  this.$emit('update:visible', nv);
224
231
 
232
+ if (nv) {
233
+ this.$emit('beforeshow');
234
+ }
235
+ if (!nv) {
236
+ this.$emit('beforehide');
237
+ }
238
+
225
239
  if (this.isServer) {
226
240
  return;
227
241
  }
@@ -273,7 +287,6 @@ export default {
273
287
  }
274
288
  } else {
275
289
  if (this.cleanup) {
276
- console.log('cleanup');
277
290
  this.cleanup();
278
291
  this.cleanup = null;
279
292
  }
@@ -312,6 +325,15 @@ export default {
312
325
  globalClearLayerIndex = 0;
313
326
  }
314
327
  }
328
+
329
+ // show and hide events (also see beforeshow/beforehide in visibleValue watcher)
330
+
331
+ if (nv) {
332
+ this.$emit('show');
333
+ }
334
+ if (!nv) {
335
+ this.$emit('hide');
336
+ }
315
337
  });
316
338
  },
317
339
  },
@@ -481,6 +503,15 @@ export default {
481
503
 
482
504
  return reference;
483
505
  },
506
+
507
+ // returns the default overlay element (will be null if the overlay slot has been overriden)
508
+ getOverlayElement() {
509
+ return this.$refs.overlay;
510
+ },
511
+
512
+ getPopupElement() {
513
+ return this.$refs.popup;
514
+ },
484
515
  },
485
516
  };
486
517
  </script>
@@ -22,7 +22,7 @@
22
22
  </px-test>
23
23
 
24
24
  <px-test name="Width / Height" testId="pxcontain-wh" :size="{ h: 600 }">
25
- <px-contain :naturalWidth="1600" :naturalHeight="900" :px="true">
25
+ <px-contain :naturalWidth="1600" :naturalHeight="900">
26
26
  <template #default="{ top, left, width, height }">
27
27
  <div
28
28
  :style="{
@@ -39,7 +39,7 @@
39
39
  </px-test>
40
40
 
41
41
  <px-test name="Scale" testId="pxcontain-scale" :size="{ h: 600 }">
42
- <px-contain :naturalWidth="1600" :naturalHeight="900" :px="true">
42
+ <px-contain :naturalWidth="1600" :naturalHeight="900">
43
43
  <template #default="{ top, left, scale }">
44
44
  <div
45
45
  :style="{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkpixellab-public/px-vue",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "General purpose Vue components and helpers that can be used across projects.",
5
5
  "author": "Pixel Lab",
6
6
  "license": "MIT",
package/utils/fit.js CHANGED
@@ -50,14 +50,14 @@ export function contain(inner, outer, posX = 0.5, posY = 0.5) {
50
50
  if (aspectInner > aspectOuter) {
51
51
  width = outer.width;
52
52
  height = outer.width / aspectInner;
53
- x = 0;
53
+ x = outer.x;
54
54
  y = outer.height * posY - height * posY;
55
55
  scale = height / inner.height;
56
56
  } else {
57
57
  width = outer.height * aspectInner;
58
58
  height = outer.height;
59
59
  x = outer.width * posX - width * posX;
60
- y = 0;
60
+ y = outer.y;
61
61
  scale = width / inner.width;
62
62
  }
63
63