@thinkpixellab-public/px-vue 4.0.9 → 4.0.10

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.
Files changed (84) hide show
  1. package/.storybook/README.md +7 -0
  2. package/.storybook/main.js +34 -0
  3. package/.storybook/manager-head.html +0 -0
  4. package/.storybook/manager.js +6 -0
  5. package/.storybook/preview-head.html +3 -0
  6. package/.storybook/preview.js +30 -0
  7. package/.storybook/preview.scss +44 -0
  8. package/.storybook/px-vue.storybook.theme.js +34 -0
  9. package/README.md +15 -7
  10. package/bases/PxBaseItemsSelect.vue +4 -4
  11. package/bases/PxBaseMobileAware.vue +25 -60
  12. package/bases/PxBaseResize.vue +22 -3
  13. package/bases/PxBaseVariants.vue +19 -4
  14. package/bases/PxBaseVariantsOverride.vue +1 -1
  15. package/components/PxArrow.vue +127 -0
  16. package/components/PxArrowScroller.vue +41 -9
  17. package/components/PxAutoClamp.vue +8 -1
  18. package/components/PxBalancedText.vue +8 -0
  19. package/components/PxBaseActiveTab.vue +50 -0
  20. package/components/PxBaseMobileProvide.vue +79 -0
  21. package/components/PxCalendar.vue +2 -2
  22. package/components/PxCardGrid.vue +13 -0
  23. package/components/PxClock.vue +1 -1
  24. package/components/PxContain.vue +2 -2
  25. package/components/PxDatePicker.vue +1 -1
  26. package/components/PxFlip.vue +95 -0
  27. package/components/PxFloat.vue +23 -1
  28. package/components/PxGsapScrubber.vue +146 -0
  29. package/components/PxIconButton.vue +7 -0
  30. package/components/PxMeasureContainer.vue +83 -0
  31. package/components/PxMenu.vue +240 -0
  32. package/components/PxMenuItem.vue +143 -0
  33. package/components/PxModal.vue +1 -1
  34. package/components/PxPivot.vue +101 -47
  35. package/components/PxPopup.vue +1 -1
  36. package/components/PxRadioButton.vue +1 -1
  37. package/components/PxScrubber.vue +140 -0
  38. package/components/PxSideDrawer.vue +46 -12
  39. package/components/PxSlideTransition.vue +77 -17
  40. package/components/PxSlides.vue +104 -29
  41. package/components/PxSpinner.vue +18 -9
  42. package/components/PxSticky.vue +112 -0
  43. package/components/PxSvg.vue +32 -1
  44. package/components/PxTable.vue +1 -1
  45. package/components/PxToggle.vue +1 -1
  46. package/components/PxToggleButton.vue +30 -18
  47. package/components/PxTransition.vue +241 -0
  48. package/components/PxTransitionItem.vue +97 -0
  49. package/components/PxValidator.vue +4 -7
  50. package/components/PxVueTest.vue +11 -15
  51. package/dev/demos/Demo-PxCardGrid.vue +0 -4
  52. package/dev/demos/Demo-PxContain.vue +0 -4
  53. package/dev/demos/Demo-PxFlip.vue +104 -0
  54. package/dev/demos/Demo-PxFloat.vue +5 -5
  55. package/dev/demos/Demo-PxMenu.vue +91 -0
  56. package/dev/demos/Demo-PxPivot.vue +0 -3
  57. package/dev/demos/Demo-PxSideDrawer.vue +0 -3
  58. package/dev/demos/Demo-PxSlideTransition.vue +0 -2
  59. package/dev/demos/Demo-PxStylesPreview.vue +0 -3
  60. package/dev/demos/DemoPicker.vue +2 -4
  61. package/dev/dev.vue +5 -1
  62. package/package.json +32 -28
  63. package/plugins/common.js +9 -2
  64. package/stories/PxAspect.stories.js +8 -4
  65. package/stories/PxBalancedText.stories.js +4 -2
  66. package/stories/PxButton.stories.js +9 -5
  67. package/stories/PxDropdown.stories.js +4 -2
  68. package/stories/PxExpander.stories.js +5 -3
  69. package/stories/PxFloat.stories.js +4 -2
  70. package/stories/PxIcon.stories.js +14 -8
  71. package/stories/PxPivot.stories.js +8 -4
  72. package/stories/PxRadioButton.stories.js +4 -2
  73. package/stories/PxSvg.stories.js +4 -2
  74. package/stories/PxToggleButton.stories.js +8 -4
  75. package/stories/PxToggleButtonList.stories.js +9 -4
  76. package/utils/balanceText.js +2 -2
  77. package/utils/bem.js +2 -1
  78. package/utils/cssStr.js +181 -13
  79. package/utils/drag.js +1 -1
  80. package/utils/lethargy.js +11 -2
  81. package/utils/points.js +110 -0
  82. package/utils/textWidth.js +1 -0
  83. package/utils/uniqueId.js +5 -14
  84. package/utils/utils.js +398 -204
@@ -0,0 +1,7 @@
1
+ # Storybook Notes
2
+
3
+ Just a place to keep some notes while figuring out how to use / integrate storybook.js.
4
+
5
+ ## Global CSS in All Stories
6
+
7
+ Add to preview.scss and then we import in .storybook/preview.js
@@ -0,0 +1,34 @@
1
+ /** @type { import('@storybook/vue3-vite').StorybookConfig } */
2
+ const config = {
3
+ stories: ['../stories/**/*.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
4
+ addons: [
5
+ '@storybook/addon-links',
6
+ '@storybook/addon-essentials',
7
+ '@storybook/addon-interactions',
8
+ {
9
+ name: '@storybook/addon-docs',
10
+ options: {
11
+ mdxPluginOptions: {
12
+ // mdxCompileOptions: {
13
+ // remarkPlugins: [remarkGfm],
14
+ // },
15
+ },
16
+ },
17
+ },
18
+ '@storybook/addon-mdx-gfm'
19
+ ],
20
+
21
+ framework: {
22
+ name: '@storybook/vue3-vite',
23
+ options: {},
24
+ },
25
+
26
+ docs: {
27
+ autodocs: true,
28
+ defaultName: 'Documentation',
29
+ },
30
+ features: {
31
+ interactionsDebugger: true,
32
+ },
33
+ };
34
+ export default config;
File without changes
@@ -0,0 +1,6 @@
1
+ import { addons } from '@storybook/manager-api';
2
+ import PxVueTheme from './px-vue.storybook.theme';
3
+
4
+ addons.setConfig({
5
+ theme: PxVueTheme,
6
+ });
@@ -0,0 +1,3 @@
1
+ <script>
2
+ window.global = window;
3
+ </script>
@@ -0,0 +1,30 @@
1
+ import { setup } from '@storybook/vue3';
2
+ import common from '../plugins/common.js';
3
+
4
+ import './preview.scss';
5
+
6
+ // px-vue common plugins
7
+ setup(app => {
8
+ app.use(common);
9
+ });
10
+
11
+ const preview = {
12
+ parameters: {
13
+ actions: { argTypesRegex: '^on[A-Z].*' },
14
+ controls: {
15
+ matchers: {
16
+ color: /(background|color)$/i,
17
+ date: /Date$/,
18
+ string: /(itemsKey|itemsDisabled)$/i,
19
+ },
20
+ },
21
+
22
+ // theme
23
+ docs: {
24
+ // requires import { themes } from '@storybook/theming';
25
+ //theme: themes.dark,
26
+ },
27
+ },
28
+ };
29
+
30
+ export default preview;
@@ -0,0 +1,44 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600&family=Public+Sans:wght@300;400;500;600&family=Work+Sans:wght@400;600&display=swap');
2
+
3
+ // .sbdocs-h2,
4
+ // .sbdocs-h3,
5
+ // .sbdocs-h4,
6
+ // .sbdocs-h5,
7
+ // .sbdocs h1:where(:not(.sb-unstyled h1)),
8
+ // .sbdocs h2:where(:not(.sb-unstyled h2)),
9
+ // .sbdocs h3:where(:not(.sb-unstyled h3)),
10
+ // .sbdocs h4:where(:not(.sb-unstyled h4)),
11
+ // .sbdocs h5:where(:not(.sb-unstyled h5)),
12
+ // .sbdocs-title,
13
+ // .sbdocs-content p,
14
+ // .docblock-argstable,
15
+ // .docblock-argstable tr,
16
+ // .docblock-argstable th,
17
+ // .docblock-argstable td,
18
+ // .docblock-argstable td button {
19
+ // font-family: $font !important;
20
+ // }
21
+
22
+ .story-bounds {
23
+ display: inline-block;
24
+ height: auto;
25
+ width: auto;
26
+ padding: 0;
27
+ margin: 0;
28
+ display: block;
29
+ border: 1px dashed rgba(black, 0.33);
30
+ }
31
+
32
+ .story-circle {
33
+ background-color: tomato;
34
+ width: 6em;
35
+ height: 6em;
36
+ border-radius: 100%;
37
+ }
38
+
39
+ .story-fill {
40
+ background-color: tomato;
41
+ width: 100%;
42
+ height: 100%;
43
+ border-radius: 8px;
44
+ }
@@ -0,0 +1,34 @@
1
+ // https://storybook.js.org/docs/7.0/vue/configure/theming
2
+
3
+ import { create } from '@storybook/theming/create';
4
+
5
+ export default create({
6
+ // base: 'light',
7
+ // // Typography
8
+ // fontBase: '"Noto Sans", sans-serif',
9
+ // fontCode: 'monospace',
10
+ // brandTitle: 'px-vue',
11
+ // brandUrl: 'https://example.com',
12
+ // brandImage: 'https://storybook.js.org/images/placeholders/350x150.png',
13
+ // brandTarget: '_self',
14
+ // //
15
+ // colorPrimary: '#3A10E5',
16
+ // colorSecondary: '#585C6D',
17
+ // // UI
18
+ // appBg: '#ffffff',
19
+ // appContentBg: '#ffffff',
20
+ // appBorderColor: '#585C6D',
21
+ // appBorderRadius: 4,
22
+ // // Text colors
23
+ // textColor: '#10162F',
24
+ // textInverseColor: '#ffffff',
25
+ // // Toolbar default and active colors
26
+ // barTextColor: '#9E9E9E',
27
+ // barSelectedColor: '#585C6D',
28
+ // barBg: '#ffffff',
29
+ // // Form colors
30
+ // inputBg: '#ffffff',
31
+ // inputBorder: '#10162F',
32
+ // inputTextColor: '#10162F',
33
+ // inputBorderRadius: 2,
34
+ });
package/README.md CHANGED
@@ -1,40 +1,48 @@
1
1
  # px-vue
2
2
 
3
+ ## Storybook
4
+
5
+ Basic component preview and testing can be done with storybook. Run storybook with `npm run dev-px`.
6
+
7
+ ---
8
+
9
+ ## Default README
10
+
3
11
  This template should help get you started developing with Vue 3 in Vite.
4
12
 
5
- ## Recommended IDE Setup
13
+ ### Recommended IDE Setup
6
14
 
7
15
  [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
8
16
 
9
- ## Customize configuration
17
+ ### Customize configuration
10
18
 
11
19
  See [Vite Configuration Reference](https://vitejs.dev/config/).
12
20
 
13
- ## Project Setup
21
+ ### Project Setup
14
22
 
15
23
  ```sh
16
24
  npm install
17
25
  ```
18
26
 
19
- ### Compile and Hot-Reload for Development
27
+ #### Compile and Hot-Reload for Development
20
28
 
21
29
  ```sh
22
30
  npm run dev
23
31
  ```
24
32
 
25
- ### Compile and Minify for Production
33
+ #### Compile and Minify for Production
26
34
 
27
35
  ```sh
28
36
  npm run build
29
37
  ```
30
38
 
31
- ### Run Unit Tests with [Vitest](https://vitest.dev/)
39
+ #### Run Unit Tests with [Vitest](https://vitest.dev/)
32
40
 
33
41
  ```sh
34
42
  npm run test:unit
35
43
  ```
36
44
 
37
- ### Lint with [ESLint](https://eslint.org/)
45
+ #### Lint with [ESLint](https://eslint.org/)
38
46
 
39
47
  ```sh
40
48
  npm run lint
@@ -4,7 +4,7 @@
4
4
 
5
5
  <script>
6
6
  import PxBaseItems from './PxBaseItems.vue';
7
- import utils from '../utils/utils.js';
7
+ import { arraySafeIndex } from '../utils/utils.js';
8
8
 
9
9
  // helper to define what an "empty" key or set of keys looks like
10
10
  const isKeySetEmpty = keyOrKeys => {
@@ -356,7 +356,7 @@ export default {
356
356
  const replace =
357
357
  replaceSelection == 'auto' ? this.updateMode == 'single' : replaceSelection;
358
358
 
359
- index = utils.arraySafeIndex(this.items, index, 0, wrap);
359
+ index = arraySafeIndex(this.items, index, 0, wrap);
360
360
  this.selectAt(index, replace);
361
361
  },
362
362
 
@@ -364,11 +364,11 @@ export default {
364
364
  if (this.selectedIndex === null) {
365
365
  this.selectAt(0);
366
366
  } else {
367
- const index = utils.arraySafeIndex(
367
+ const index = arraySafeIndex(
368
368
  this.items,
369
369
  this.selectedIndex,
370
370
  offsetFromSelectedIndex,
371
- wrap
371
+ wrap,
372
372
  );
373
373
  this.selectAt(index, true);
374
374
  }
@@ -1,73 +1,38 @@
1
1
  <!--
2
2
  #VUE3 Todo
3
-
4
- Use for global mobile awareness in javascript. This doesn't actually handle mobile detection
5
- but provides a consistent interface for communicating between a single component that does
6
- the detection and other components that subscribe to an eventbus and update a property called
7
- isMobile.
8
-
9
- Sample usage using a css variable for detecting mobile (this keeps css and js in sync):
10
-
11
- ### MyLayout.vue (or other global component)
12
-
13
- // add PxBaseMobileAware as a mixin
14
- import PxBaseMobileAware from '@thinkpixellab-public/px-vue/bases/PxBaseMobileAware.vue';
15
- mixins: [PxBaseMobileAware],
16
-
17
- // add a resize handler and call setGlobalIsMobile
18
- resize() {
19
- if (this.$el) {
20
- const cssMobile = getComputedStyle(this.$el).getPropertyValue('--is-mobile');
21
- this.setGlobalIsMobile(cssMobile.toLowerCase().trim() == 'true');
22
- }
23
- },
24
-
25
- // add a css variable that changes with the viewport
26
-
27
- .my-layout {
28
- --is-mobile: false;
29
- @include media-until-mobile() {
30
- --is-mobile: true;
31
- }
32
- }
33
-
34
- ### MyComponent.vue
35
-
36
- // use PxBaseMobileAware as a mixin
37
- import PxBaseMobileAware from '@thinkpixellab-public/px-vue/bases/PxBaseMobileAware.vue';
38
- mixins: [PxBaseMobileAware]
39
-
40
- // use the isMobile property as needed in a template
41
- <div> This is mobile: {{isMobile ? 'yup' : 'nope' }} </div>
42
-
43
- // or in code
44
- watch: {
45
- isMobile(nv) {
46
- // do something when isMobile changes
47
- }
48
- }
49
-
50
-
3
+ Must be used in conjuction with PxBaseMobileProvide. See notes in that file for details.
51
4
  -->
52
5
 
53
6
  <script>
54
7
  export default {
55
- data() {
56
- return { isMobile: null };
8
+ inject: {
9
+ mediaQuery: {
10
+ default: { isMobile: null, currentBreakpoint: null, breakpoints: {} },
11
+ },
57
12
  },
58
- mounted() {
59
- if (this.$globalOn) {
60
- this.$globalOn('mobile-changed', isMobile => {
61
- this.isMobile = isMobile;
62
- });
63
- }
13
+ computed: {
14
+ isMobile() {
15
+ return this.mediaQuery.isMobile;
16
+ },
17
+ currentBreakpoint() {
18
+ return this.mediaQuery.currentBreakpoint;
19
+ },
64
20
  },
21
+
65
22
  methods: {
66
- setGlobalIsMobile(isMobile) {
67
- if (this.$globalEmit) {
68
- this.$globalEmit('mobile-changed', isMobile);
69
- this.isMobile = isMobile;
23
+ isBreakpointOrSmaller(breakpoint) {
24
+ if (this.currentBreakpoint === null) {
25
+ return false;
70
26
  }
27
+
28
+ if (this.currentBreakpoint === breakpoint) {
29
+ return true;
30
+ }
31
+
32
+ return (
33
+ this.mediaQuery.breakpoints[breakpoint] >=
34
+ this.mediaQuery.breakpoints[this.currentBreakpoint]
35
+ );
71
36
  },
72
37
  },
73
38
  };
@@ -31,7 +31,11 @@ export default {
31
31
  const resizeDebouncedFn =
32
32
  this.resizeDebounceValue && this.resize
33
33
  ? debounce(this.resizeDebounceValue, e => {
34
- this.resize(e);
34
+ try {
35
+ this.resize(e);
36
+ } catch (e) {
37
+ console.error('Resize: ' + e);
38
+ }
35
39
  this.resizing = false;
36
40
  })
37
41
  : null;
@@ -51,13 +55,28 @@ export default {
51
55
  // regular version
52
56
  else {
53
57
  if (this.resize) {
54
- this.resize();
58
+ try {
59
+ this.resize();
60
+ } catch (e) {
61
+ console.error('Resize: ' + e);
62
+ }
55
63
  }
56
64
  }
57
65
  };
58
66
 
59
67
  this.resizeObserver = new ResizeObserver(resizeFn);
60
- this.resizeObserver.observe(this.observeElementValue);
68
+
69
+ if (
70
+ this.observeElementValue &&
71
+ (this.observeElementValue instanceof Element ||
72
+ this.observeElementValue instanceof SVGElement)
73
+ ) {
74
+ this.resizeObserver.observe(this.observeElementValue);
75
+ } else {
76
+ console.warn(
77
+ `Could not call ResizeObserver.observe because observeElementValue is not an Element: ${this.observeElementValue}`
78
+ );
79
+ }
61
80
  },
62
81
  beforeUnmount() {
63
82
  this.resizeObserver.disconnect();
@@ -45,12 +45,27 @@ export default {
45
45
  },
46
46
  },
47
47
  methods: {
48
- // true if the given variant is present
49
- isVariant(variantName) {
50
- return this.variants?.includes(variantName);
48
+ // true if the given variant (string) or variants (array) are present
49
+ isVariant(val) {
50
+ if (this.variants && this.variants.length && val) {
51
+ if (typeof val == 'string') {
52
+ return this.variants?.includes(val);
53
+ }
54
+ if (Array.isArray(val) && val.length) {
55
+ for (let i = 0; i < val.length; i++) {
56
+ if (this.variants?.includes(val[i])) {
57
+ return true;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ return false;
51
63
  },
52
64
 
53
- // find the first matching variant key in a map and return the correspond value
65
+ // Find the first matching variant key in a map and return the correspond value
66
+ // Example:
67
+ // <MyComponent variant="red" />
68
+ // variantsToValue({'red': '#F00', 'blue': '#0F0'}) => '#F00'
54
69
  variantsToValue(valueMap, fallback) {
55
70
  for (let key in valueMap) {
56
71
  if (this.isVariant(key)) {
@@ -20,7 +20,7 @@ export default {
20
20
  variantValue() {
21
21
  return (
22
22
  (this.variantBase ? `${this.variantBase} ` : '') +
23
- (this.variantDefault ? this.variantDefault : this.variant)
23
+ (this.variant && this.variant !== 'default' ? this.variant : this.variantDefault)
24
24
  );
25
25
  },
26
26
  },
@@ -0,0 +1,127 @@
1
+ <!--
2
+ Generates an SVG arrow based on props.
3
+ -->
4
+ <template>
5
+ <svg
6
+ version="1.1"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ xmlns:xlink="http://www.w3.org/1999/xlink"
9
+ overflow="visible"
10
+ :viewBox="svgData.viewBox"
11
+ >
12
+ <g
13
+ :stroke-width="svgData.strokeWidth"
14
+ stroke="currentColor"
15
+ fill="none"
16
+ fill-rule="evenodd"
17
+ stroke-linecap="round"
18
+ >
19
+ <polyline :points="svgData.pointer"></polyline>
20
+ <polyline v-if="stemVisible" :points="svgData.stem"></polyline>
21
+ </g>
22
+ </svg>
23
+ </template>
24
+
25
+ <script>
26
+ import PxBaseResize from '@thinkpixellab-public/px-vue/bases/PxBaseResize.vue';
27
+ import { getBoundingBox, rotatePoints } from '../utils/points.js';
28
+ import { contain } from '@thinkpixellab-public/px-vue/utils/fit.js';
29
+
30
+ export default {
31
+ name: 'px-arrow',
32
+ mixins: [PxBaseResize],
33
+ props: {
34
+ // the aspect of the arrow "rectangle"
35
+ pointerAspect: { type: [Number, String], default: 2 },
36
+
37
+ // the relative height of the arrow
38
+ pointerSize: { type: Number, default: 0.4 },
39
+
40
+ // the relative size of the stem
41
+ stemVisible: { type: Boolean, default: true },
42
+
43
+ // the angle in which the arrow is pointing (in degrees)
44
+ angle: { type: Number, default: 0 },
45
+
46
+ // the stroke thickness of the arrow
47
+ strokeWidth: { type: Number, default: 1 },
48
+
49
+ // when true, we scale the strokeWidth in the internal SVG to keep it at the specified value
50
+ // regardless of render size
51
+ scaleStrokeWidth: { type: Boolean, default: true },
52
+ },
53
+ data() {
54
+ return {
55
+ elementSize: { width: 100, height: 100 },
56
+ };
57
+ },
58
+ computed: {
59
+ svgData() {
60
+ const pointerHeight = this.pointerSize;
61
+ const pointerWidth = this.pointerAspect * pointerHeight;
62
+
63
+ // basic arrow (bottom left corner anchored to origin)
64
+ let points = [
65
+ // pointer
66
+ { x: 0, y: pointerHeight },
67
+ { x: pointerWidth / 2, y: 0 },
68
+ { x: pointerWidth, y: pointerHeight },
69
+
70
+ // stem
71
+ { x: pointerWidth / 2, y: 0 },
72
+ { x: pointerWidth / 2, y: 1 },
73
+ ];
74
+
75
+ // rotate
76
+ points = rotatePoints(points, this.angle);
77
+
78
+ // scale by 100 so the viewbox isn't ridiculous
79
+ points = points.map(p => {
80
+ return {
81
+ x: p.x * 100,
82
+ y: p.y * 100,
83
+ };
84
+ });
85
+
86
+ // get viewbox bounds
87
+ const bounds = getBoundingBox(points);
88
+
89
+ // get stroke scale
90
+ const strokeScale = this.scaleStrokeWidth ? contain(this.elementSize, bounds).scale : 1;
91
+
92
+ // convert points to a string
93
+ points = points.map(p => {
94
+ return `${p.x} ${p.y}`;
95
+ });
96
+
97
+ const svgData = {
98
+ viewBox: [bounds.x, bounds.y, bounds.width, bounds.height]
99
+ .map(n => Math.round(n))
100
+ .join(' '),
101
+ pointer: [points[0], points[1], points[2]].join(' '),
102
+ stem: [points[3], points[4]].join(' '),
103
+ strokeWidth: this.strokeWidth * strokeScale,
104
+ };
105
+
106
+ return svgData;
107
+ },
108
+ },
109
+ methods: {
110
+ resize() {
111
+ if (this.$el) {
112
+ const r = this.$el.getBoundingClientRect();
113
+ this.elementSize = {
114
+ width: r.width,
115
+ height: r.height,
116
+ };
117
+ }
118
+ },
119
+ },
120
+ };
121
+ </script>
122
+
123
+ <style lang="scss">
124
+ .px-arrow {
125
+ position: relative;
126
+ }
127
+ </style>