@thinkpixellab-public/px-vue 4.1.9 → 4.1.13
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.
- package/bases/PxBaseItemsSelect.vue +7 -1
- package/bases/PxBaseUniqueId.vue +2 -2
- package/components/PxContain.vue +1 -1
- package/components/PxIcon.vue +2 -2
- package/components/PxResizer.vue +34 -15
- package/composables/useSettings.js +49 -0
- package/package.json +1 -1
- package/stories/PxContain.stories.js +38 -0
- package/test/PxBaseItemsSelect.test.js +1 -0
- package/test/PxBaseUniqueId.test.js +4 -3
- package/test/utils.test.js +15 -1
- package/utils/drag.js +12 -0
- package/utils/utils.js +36 -0
|
@@ -37,6 +37,12 @@ export default {
|
|
|
37
37
|
* items is updated and no other item is selected
|
|
38
38
|
*/
|
|
39
39
|
autoSelectFirst: { type: Boolean, default: false },
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* If true, the update and change events will only be emitted after if the component is
|
|
43
|
+
* mounted.
|
|
44
|
+
*/
|
|
45
|
+
emitEventsBeforeMounted: { type: Boolean, default: false },
|
|
40
46
|
},
|
|
41
47
|
|
|
42
48
|
data() {
|
|
@@ -179,7 +185,7 @@ export default {
|
|
|
179
185
|
* Emit the change / update:selected event
|
|
180
186
|
*/
|
|
181
187
|
emitUpdateEvent() {
|
|
182
|
-
if (!this.isMounted) {
|
|
188
|
+
if (!this.isMounted && !this.emitEventsBeforeMounted) {
|
|
183
189
|
return;
|
|
184
190
|
}
|
|
185
191
|
|
package/bases/PxBaseUniqueId.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
export default {
|
|
3
3
|
beforeCreate() {
|
|
4
|
-
const id =
|
|
5
|
-
this.$__uniqueId = id;
|
|
4
|
+
const id = this.$.uid || Math.floor(Math.random() * 1000);
|
|
5
|
+
this.$__uniqueId = String(id).padStart(4, '0');
|
|
6
6
|
},
|
|
7
7
|
methods: {
|
|
8
8
|
/**
|
package/components/PxContain.vue
CHANGED
package/components/PxIcon.vue
CHANGED
|
@@ -136,8 +136,8 @@ const PxIcon = {
|
|
|
136
136
|
|
|
137
137
|
if (offset || spacing || this.strokeWidth) {
|
|
138
138
|
return {
|
|
139
|
-
transform: offset ? `translateY(${cssEmFallback(offset)})` : null,
|
|
140
|
-
margin: spacing ? `auto ${cssEmFallback(spacing)}` : null,
|
|
139
|
+
transform: offset ? `translateY(${cssEmFallback(this.offset)})` : null,
|
|
140
|
+
margin: spacing ? `auto ${cssEmFallback(this.spacing)}` : null,
|
|
141
141
|
strokeWidth: cssPx(this.strokeWidth, false),
|
|
142
142
|
};
|
|
143
143
|
}
|
package/components/PxResizer.vue
CHANGED
|
@@ -115,6 +115,7 @@ export default {
|
|
|
115
115
|
|
|
116
116
|
this.updateSize(r.width, r.height, this.downDirection);
|
|
117
117
|
|
|
118
|
+
document.body.style.userSelect = 'none';
|
|
118
119
|
document.addEventListener('mousemove', this.mousemove);
|
|
119
120
|
document.addEventListener('mouseup', this.mouseup);
|
|
120
121
|
}
|
|
@@ -130,6 +131,7 @@ export default {
|
|
|
130
131
|
e.preventDefault();
|
|
131
132
|
},
|
|
132
133
|
mouseup() {
|
|
134
|
+
document.body.style.removeProperty('user-select');
|
|
133
135
|
this.resizing = false;
|
|
134
136
|
this.emitResizeEnd();
|
|
135
137
|
document.removeEventListener('mousemove', this.mousemove);
|
|
@@ -143,6 +145,16 @@ export default {
|
|
|
143
145
|
@use '../styles/px.scss' as *;
|
|
144
146
|
@use 'sass:color';
|
|
145
147
|
|
|
148
|
+
@at-root {
|
|
149
|
+
:root {
|
|
150
|
+
--px-resizer-color: #{accent()};
|
|
151
|
+
--px-resizer-color-hover: #{color.adjust(accent(), $lightness: -10%)};
|
|
152
|
+
--px-resizer-width: 3px;
|
|
153
|
+
--px-resizer-height: 24px;
|
|
154
|
+
--px-resizer-hit-width: 24px;
|
|
155
|
+
--px-resizer-offset: 10px;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
146
158
|
.px-resizer {
|
|
147
159
|
position: relative;
|
|
148
160
|
margin: 0 auto;
|
|
@@ -165,16 +177,16 @@ export default {
|
|
|
165
177
|
|
|
166
178
|
&--show-overlay {
|
|
167
179
|
&::after {
|
|
168
|
-
border: 1px dashed
|
|
180
|
+
border: 1px dashed var(--px-resizer-color);
|
|
169
181
|
border-radius: inherit;
|
|
170
182
|
}
|
|
171
183
|
}
|
|
172
184
|
|
|
173
185
|
&__resize-x,
|
|
174
186
|
&__resize-y {
|
|
175
|
-
color:
|
|
187
|
+
color: var(--px-resizer-color);
|
|
176
188
|
&:hover {
|
|
177
|
-
color:
|
|
189
|
+
color: var(--px-resizer-color-hover);
|
|
178
190
|
}
|
|
179
191
|
&:active {
|
|
180
192
|
opacity: 0.5;
|
|
@@ -184,13 +196,16 @@ export default {
|
|
|
184
196
|
&__resize-x {
|
|
185
197
|
@include center-y();
|
|
186
198
|
cursor: ew-resize;
|
|
187
|
-
width:
|
|
188
|
-
height:
|
|
189
|
-
right: -
|
|
199
|
+
width: var(--px-resizer-hit-width);
|
|
200
|
+
height: var(--px-resizer-height);
|
|
201
|
+
right: calc(var(--px-resizer-hit-width) * -1);
|
|
202
|
+
|
|
190
203
|
@include after() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
204
|
+
position: absolute;
|
|
205
|
+
top: 0;
|
|
206
|
+
left: var(--px-resizer-offset);
|
|
207
|
+
width: var(--px-resizer-width);
|
|
208
|
+
height: var(--px-resizer-height);
|
|
194
209
|
background-color: currentColor;
|
|
195
210
|
}
|
|
196
211
|
}
|
|
@@ -198,13 +213,17 @@ export default {
|
|
|
198
213
|
&__resize-y {
|
|
199
214
|
@include center-x();
|
|
200
215
|
cursor: ns-resize;
|
|
201
|
-
|
|
202
|
-
height:
|
|
203
|
-
|
|
216
|
+
|
|
217
|
+
height: var(--px-resizer-hit-width);
|
|
218
|
+
width: var(--px-resizer-height);
|
|
219
|
+
bottom: calc(var(--px-resizer-hit-width) * -1);
|
|
220
|
+
|
|
204
221
|
@include after() {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
position: absolute;
|
|
223
|
+
left: 0;
|
|
224
|
+
top: var(--px-resizer-offset);
|
|
225
|
+
height: var(--px-resizer-width);
|
|
226
|
+
width: var(--px-resizer-height);
|
|
208
227
|
background-color: currentColor;
|
|
209
228
|
}
|
|
210
229
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ref, watch, onMounted } from 'vue';
|
|
2
|
+
import { deepMerge } from '../utils/utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Provides a reactive settings object that syncs with localStorage. Basic usage:
|
|
6
|
+
*
|
|
7
|
+
* setup() {
|
|
8
|
+
* const settings = useSettings("appSettings", {
|
|
9
|
+
* setting1: true,
|
|
10
|
+
* setting2: 'awesome'
|
|
11
|
+
* }, (loadedSettings) => {
|
|
12
|
+
* console.log("Settings initialized:", loadedSettings);
|
|
13
|
+
* });
|
|
14
|
+
* return { settings }
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export default function useSettings(key, defaultSettings = {}, onInit = null) {
|
|
19
|
+
const settings = ref(defaultSettings);
|
|
20
|
+
|
|
21
|
+
const loadSettings = () => {
|
|
22
|
+
if (process.client) {
|
|
23
|
+
const stored = localStorage.getItem(key);
|
|
24
|
+
const parsed = stored ? JSON.parse(stored) : {};
|
|
25
|
+
|
|
26
|
+
settings.value = deepMerge(settings.value, parsed, true);
|
|
27
|
+
|
|
28
|
+
if (onInit && typeof onInit === 'function') {
|
|
29
|
+
onInit(settings.value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
onMounted(() => {
|
|
35
|
+
loadSettings();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
watch(
|
|
39
|
+
settings,
|
|
40
|
+
nv => {
|
|
41
|
+
if (process.client) {
|
|
42
|
+
localStorage.setItem(key, JSON.stringify(nv));
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{ deep: true },
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return settings;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -37,6 +37,44 @@ export const Basic = {
|
|
|
37
37
|
</PxResizer>
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
`,
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const ScaledContentExample = {
|
|
45
|
+
render: (args, { argTypes }) => ({
|
|
46
|
+
components: { PxResizer, PxContain },
|
|
47
|
+
setup() {
|
|
48
|
+
return { args };
|
|
49
|
+
},
|
|
50
|
+
template: `
|
|
51
|
+
<PxResizer :initialWidth="400" initialHeight="400" class="story-bounds" >
|
|
52
|
+
<PxContain #default="{width, height, left, top, scale}" :naturalWidth="1000" :naturalHeight="1000" :round="true" :to-px="true" v-bind="args" >
|
|
53
|
+
<div
|
|
54
|
+
class="story-fill"
|
|
55
|
+
:style="{
|
|
56
|
+
top,
|
|
57
|
+
left,
|
|
58
|
+
width: '1000px',
|
|
59
|
+
height: '1000px',
|
|
60
|
+
position: 'absolute',
|
|
61
|
+
transform: 'scale(' + scale + ')',
|
|
62
|
+
transformOrigin: '0 0',
|
|
63
|
+
fontSize: '50px',
|
|
64
|
+
}">
|
|
65
|
+
Outer natural size: 1000px x 1000px<br>
|
|
66
|
+
Font-size: 50px<br>
|
|
67
|
+
Scale {{Math.round(1000 * scale) / 1000}}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
<div style="width: 500px; height: 500px; background-color: rgba(0,0,0,0.5); top: 250px; left: 250px; position: absolute;">
|
|
71
|
+
Inner Rectangle Size: 500px x 500px
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</PxContain>
|
|
75
|
+
</PxResizer>
|
|
76
|
+
|
|
77
|
+
|
|
40
78
|
`,
|
|
41
79
|
}),
|
|
42
80
|
};
|
|
@@ -4,9 +4,11 @@ import PxBaseUniqueId from '@/bases/PxBaseUniqueId.vue';
|
|
|
4
4
|
|
|
5
5
|
test('Ensure uniqueId method works', () => {
|
|
6
6
|
const wrapper = shallowMount(MockComponent, {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
global: {
|
|
8
|
+
mixins: [PxBaseUniqueId],
|
|
9
|
+
},
|
|
9
10
|
});
|
|
11
|
+
|
|
10
12
|
expect(wrapper.vm.uniqueId()).toEqual('0001');
|
|
11
13
|
});
|
|
12
14
|
|
|
@@ -18,7 +20,6 @@ test('Ensure uniqueId method works with a prefix', () => {
|
|
|
18
20
|
expect(wrapper.vm.uniqueId('label')).toEqual('label-0003');
|
|
19
21
|
});
|
|
20
22
|
|
|
21
|
-
|
|
22
23
|
test('Ensure uniqueId method trims extra spaces', () => {
|
|
23
24
|
const wrapper = shallowMount(MockComponent, {
|
|
24
25
|
mixins: [PxBaseUniqueId],
|
package/test/utils.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { objectToArray } from '../utils/utils.js';
|
|
1
|
+
import { objectToArray, deepMerge } from '../utils/utils.js';
|
|
2
2
|
|
|
3
3
|
test('objectToArray (defaults with value type value)', () => {
|
|
4
4
|
expect(objectToArray({ one: 1, two: 2, three: 3 })).toEqual([
|
|
@@ -44,3 +44,17 @@ test('objectToArray (expand objects)', () => {
|
|
|
44
44
|
{ key: 'three', number: 3, ordinal: 'third' },
|
|
45
45
|
]);
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
test('deepMerge', () => {
|
|
49
|
+
const target = { a: 1, b: { c: 2, d: 3 } };
|
|
50
|
+
const source = { b: { d: 4, e: 5 } };
|
|
51
|
+
|
|
52
|
+
expect(deepMerge(target, source)).toEqual({ a: 1, b: { c: 2, d: 4, e: 5 } });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('deepMerge (overwrite is false)', () => {
|
|
56
|
+
const target = { a: 1, b: { c: 2, d: 3 } };
|
|
57
|
+
const source = { b: { d: 4, e: 5 } };
|
|
58
|
+
|
|
59
|
+
expect(deepMerge(target, source, false)).toEqual({ a: 1, b: { c: 2, d: 3, e: 5 } });
|
|
60
|
+
});
|
package/utils/drag.js
CHANGED
|
@@ -36,6 +36,9 @@ const defaults = {
|
|
|
36
36
|
|
|
37
37
|
// initial value for the disabled property
|
|
38
38
|
disabled: false,
|
|
39
|
+
|
|
40
|
+
// disable text selection during drag
|
|
41
|
+
disableTextSelection: true,
|
|
39
42
|
};
|
|
40
43
|
|
|
41
44
|
function Drag(element, options) {
|
|
@@ -310,6 +313,11 @@ function Drag(element, options) {
|
|
|
310
313
|
|
|
311
314
|
this.addRuntimeEvents = () => {
|
|
312
315
|
this.removeRuntimeEvents();
|
|
316
|
+
|
|
317
|
+
if (this.ops.disableTextSelection) {
|
|
318
|
+
document.body.style.userSelect = 'none';
|
|
319
|
+
}
|
|
320
|
+
|
|
313
321
|
if (this.supportsTouch) {
|
|
314
322
|
document.addEventListener('touchmove', this.onDrag, { passive: false });
|
|
315
323
|
document.addEventListener('touchend', this.onEnd);
|
|
@@ -320,6 +328,10 @@ function Drag(element, options) {
|
|
|
320
328
|
};
|
|
321
329
|
|
|
322
330
|
this.removeRuntimeEvents = () => {
|
|
331
|
+
if (this.ops.disableTextSelection) {
|
|
332
|
+
document.body.style.removeProperty('user-select');
|
|
333
|
+
}
|
|
334
|
+
|
|
323
335
|
if (this.supportsTouch) {
|
|
324
336
|
document.removeEventListener('touchmove', this.onDrag, { passive: false });
|
|
325
337
|
document.removeEventListener('touchend', this.onEnd);
|
package/utils/utils.js
CHANGED
|
@@ -251,6 +251,42 @@ export function filterObjectByKeys(obj, allowedKeys) {
|
|
|
251
251
|
}, {});
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Performs a deep merge of two objects by merging source into target. This function is similar to
|
|
256
|
+
* Object.assign, but it recursively merges objects.
|
|
257
|
+
*
|
|
258
|
+
* @param {Object} target - The object to merge into.
|
|
259
|
+
* @param {Object} source - The object to merge from.
|
|
260
|
+
* @param {boolean} [overwrite=true] - If true, properties in source will overwrite properties in
|
|
261
|
+
* target with the same key. If false, properties in source will only be added if the key doesn't
|
|
262
|
+
* already exist in target.
|
|
263
|
+
* @returns {Object} The merged object.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* const target = { a: 1, b: { c: 2 } };
|
|
267
|
+
* const source = { b: { d: 3 } };
|
|
268
|
+
* const result = deepMerge(target, source);
|
|
269
|
+
* // result is { a: 1, b: { c: 2, d: 3 } }
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
export function deepMerge(target, source, overwrite = true) {
|
|
273
|
+
if (typeof target !== 'object' || typeof source !== 'object') return source;
|
|
274
|
+
|
|
275
|
+
for (const key in source) {
|
|
276
|
+
if (source.hasOwnProperty(key)) {
|
|
277
|
+
if (
|
|
278
|
+
typeof source[key] === 'object' &&
|
|
279
|
+
!Array.isArray(source[key]) &&
|
|
280
|
+
typeof target[key] === 'object'
|
|
281
|
+
) {
|
|
282
|
+
target[key] = deepMerge(target[key], source[key], overwrite); // Recursively merge objects
|
|
283
|
+
} else if (overwrite || !(key in target)) {
|
|
284
|
+
target[key] = source[key];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return target;
|
|
289
|
+
}
|
|
254
290
|
/**
|
|
255
291
|
* Returns an object representing the current query string parameters.
|
|
256
292
|
*/
|