@zuzjs/ui 0.3.1 → 0.3.3

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.
@@ -0,0 +1,68 @@
1
+ import typescript from 'rollup-plugin-typescript2';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import scss from "rollup-plugin-scss";
4
+ import postcss from 'postcss'
5
+ import livereload from 'rollup-plugin-livereload'
6
+ import { uglify } from 'rollup-plugin-uglify';
7
+
8
+ const isWatching = process && process.argv.includes('-w') || process.argv.includes('--watch')
9
+
10
+ export default [
11
+ {
12
+ input: ["src/index.tsx"],
13
+ output: [
14
+ {
15
+ dir: "dist",
16
+ entryFileNames: "[name].js",
17
+ format: "es",
18
+ exports: "named",
19
+ // preserveModules: true,
20
+ // preserveModulesRoot: 'src',
21
+ }
22
+ ],
23
+ plugins: [
24
+ typescript(),
25
+ commonjs(),
26
+ scss({
27
+ processor: () => postcss(),
28
+ fileName: 'styles.css',
29
+ failOnError: true
30
+ }),
31
+ !isWatching && uglify()
32
+ // livereload()
33
+ ],
34
+ external: [
35
+ "react","react-children-utilities","js-cookie","axios","nanoid","@reduxjs/toolkit","react-redux","react/jsx-runtime",
36
+ "@emotion/react","@emotion/styled","prop-types","react-hot-toast"
37
+ ]
38
+ }
39
+ // ,
40
+ // {
41
+ // input: ["src/kit/index.tsx"],
42
+ // output: [
43
+ // {
44
+ // dir: "dist",
45
+ // entryFileNames: "kit.js",
46
+ // format: "es",
47
+ // exports: "named",
48
+ // // preserveModules: true,
49
+ // // preserveModulesRoot: 'src',
50
+ // }
51
+ // ],
52
+ // plugins: [
53
+ // typescript(),
54
+ // // commonjs(),
55
+ // // scss({
56
+ // // processor: () => postcss(),
57
+ // // fileName: 'styles.css',
58
+ // // failOnError: true
59
+ // // }),
60
+ // // !isWatching && uglify()
61
+ // // livereload()
62
+ // ],
63
+ // // external: [
64
+ // // "react","react-children-utilities","js-cookie","axios","nanoid","@reduxjs/toolkit","react-redux","react/jsx-runtime",
65
+ // // "@emotion/react","@emotion/styled","prop-types","react-hot-toast"
66
+ // // ]
67
+ // }
68
+ ];
package/src/comps/box.tsx CHANGED
@@ -17,6 +17,7 @@ const Box = forwardRef((props : { [ key: string ] : any }, ref) => {
17
17
  {...cleanProps(props)}
18
18
  title={props.title || undefined}
19
19
  id={props.id || undefined}
20
+ onDoubleClick={props.onDoubleClick || undefined}
20
21
  onClick={props.onClick || _noClick}
21
22
  className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`}
22
23
  ref={props.bref || ref}>{props.html ? <span dangerouslySetInnerHTML={{ __html: props.html }} /> : props.children}</div>}
@@ -30,6 +30,7 @@ const Button = forwardRef((props: { [ key: string ] : any }, ref : LegacyRef<HTM
30
30
  border-radius: 2px;
31
31
  ${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`}
32
32
  ref={ref}
33
+ onDoubleClick={props.onDoubleClick || undefined}
33
34
  onClick={e => {
34
35
  if(props.form && props.type && props.type == 'submit'){
35
36
  props.onSubmit(forms[props.form]);
@@ -3,37 +3,56 @@ import {
3
3
  LegacyRef,
4
4
  forwardRef,
5
5
  useRef,
6
- SyntheticEvent,
6
+ useState,
7
+ useEffect,
7
8
  } from 'react';
8
9
  import Box from './box'
9
10
  import Button from './button'
10
11
 
11
12
  const ContextMenu = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTMLHeadingElement>) => {
12
13
 
13
- const {
14
- as,
15
- size,
16
- color,
17
- hover,
18
- pos,
19
- items
20
- } = props;
14
+ const { ID, p, items, hide } = props;
15
+ // const [p, setP] = useState(p)
16
+ const nodeRef = useRef(null);
17
+
18
+ const checkBoundaries = () => {
19
+
20
+ let x = p.x
21
+ let y = p.y
22
+
23
+ if (nodeRef.current) {
24
+ const { innerWidth, innerHeight } = window;
25
+ const { offsetWidth, offsetHeight } = nodeRef.current;
26
+
27
+ if (x + offsetWidth > innerWidth) x -= x + offsetWidth - innerWidth;
28
+
29
+ if (y + offsetHeight > innerHeight) y -= y + offsetHeight - innerHeight;
30
+ }
31
+ // setP({x: x, y: y})
32
+ }
33
+
34
+ useEffect(() => {
35
+ // checkBoundaries();
36
+ }, [])
21
37
 
22
38
  return (
23
39
  <Box
24
- hover={hover || {}}
40
+ bref={nodeRef}
25
41
  flex dir={`cols`}
26
42
  fixed
27
- top={pos?.y || 0}
28
- left={pos?.x || 0}
29
- bref={ref}
30
- as={`contextmenu-${as}`}
31
- size={size || 24}
32
- color={color || `#111111`}
33
- >
34
- {items && items.map((m, i) => <Button
43
+ top={p.x}
44
+ left={p.y}
45
+ as={`zuz-contextmenu ${ID}`}>
46
+ {items && items.map((m, i) => m.id == `line` ? <Box as={`line`} key={`line-${i}-${m.id}`} /> : <button
35
47
  key={`cm-${i}-${m.id}`}
36
- onClick={m.on ? m.on : () => {}}>{m.label}</Button>)}
48
+ onClick={ev => {
49
+ if(m.onClick){
50
+ m.onClick(ev, m)
51
+ }else{
52
+ console.log(`No onClick eventFound`)
53
+ }
54
+ hide()
55
+ }}>{m.label}</button>)}
37
56
  </Box>
38
57
  )
39
58
  })
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  FC,
3
+ Ref,
3
4
  LegacyRef,
4
5
  forwardRef,
5
6
  useRef,
6
7
  SyntheticEvent,
8
+ useEffect,
9
+ memo,
7
10
  } from 'react';
8
11
  import { ClassNames } from '@emotion/react'
9
12
  import { nanoid } from 'nanoid';
@@ -15,7 +18,102 @@ import {
15
18
  UPDATE_FORM_FIELD
16
19
  } from '../actions'
17
20
 
18
- const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTMLHeadingElement>) => {
21
+ const SIZING_STYLE = [
22
+ 'borderBottomWidth',
23
+ 'borderLeftWidth',
24
+ 'borderRightWidth',
25
+ 'borderTopWidth',
26
+ 'boxSizing',
27
+ 'fontFamily',
28
+ 'fontSize',
29
+ 'fontStyle',
30
+ 'fontWeight',
31
+ 'letterSpacing',
32
+ 'lineHeight',
33
+ 'paddingBottom',
34
+ 'paddingLeft',
35
+ 'paddingRight',
36
+ 'paddingTop',
37
+ // non-standard
38
+ 'tabSize',
39
+ 'textIndent',
40
+ // non-standard
41
+ 'textRendering',
42
+ 'textTransform',
43
+ 'width',
44
+ 'wordBreak',
45
+ ] as const;
46
+ type SizingProps = Extract<
47
+ (typeof SIZING_STYLE)[number],
48
+ keyof CSSStyleDeclaration
49
+ >;
50
+
51
+ export const noop = () => {};
52
+
53
+ const pick = <Obj extends { [key: string]: any }, Key extends keyof Obj>(
54
+ props: Key[],
55
+ obj: Obj,
56
+ ): Pick<Obj, Key> =>
57
+ props.reduce((acc, prop) => {
58
+ acc[prop] = obj[prop];
59
+ return acc;
60
+ }, {} as Pick<Obj, Key>);
61
+
62
+ type SizingStyle = Pick<CSSStyleDeclaration, SizingProps>;
63
+
64
+ export type SizingData = {
65
+ sizingStyle: SizingStyle;
66
+ paddingSize: number;
67
+ borderSize: number;
68
+ };
69
+
70
+ const isIE = typeof window !== "undefined" && !!(document.documentElement as any).currentStyle
71
+
72
+ const getSizingData = (node: HTMLElement): SizingData | null => {
73
+
74
+ const style = window.getComputedStyle(node);
75
+
76
+ if (style === null) {
77
+ return null;
78
+ }
79
+
80
+ const sizingStyle = pick(SIZING_STYLE as unknown as SizingProps[], style);
81
+ const { boxSizing } = sizingStyle;
82
+
83
+ // probably node is detached from DOM, can't read computed dimensions
84
+ if (boxSizing === '') {
85
+ return null;
86
+ }
87
+
88
+ // IE (Edge has already correct behaviour) returns content width as computed width
89
+ // so we need to add manually padding and border widths
90
+ if (isIE && boxSizing === 'border-box') {
91
+ sizingStyle.width =
92
+ parseFloat(sizingStyle.width!) +
93
+ parseFloat(sizingStyle.borderRightWidth!) +
94
+ parseFloat(sizingStyle.borderLeftWidth!) +
95
+ parseFloat(sizingStyle.paddingRight!) +
96
+ parseFloat(sizingStyle.paddingLeft!) +
97
+ 'px';
98
+ }
99
+
100
+ const paddingSize =
101
+ parseFloat(sizingStyle.paddingBottom!) +
102
+ parseFloat(sizingStyle.paddingTop!);
103
+
104
+ const borderSize =
105
+ parseFloat(sizingStyle.borderBottomWidth!) +
106
+ parseFloat(sizingStyle.borderTopWidth!);
107
+
108
+ return {
109
+ sizingStyle,
110
+ paddingSize,
111
+ borderSize,
112
+ };
113
+
114
+ }
115
+
116
+ const Input = forwardRef((props : { [ key: string ] : any }, ref : Ref<HTMLTextAreaElement|HTMLInputElement>) => {
19
117
 
20
118
  const {
21
119
  as,
@@ -24,6 +122,8 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
24
122
  onChange,
25
123
  onKeyUp,
26
124
  onClick,
125
+ onBlur,
126
+ onFocus,
27
127
  readOnly,
28
128
  type,
29
129
  tag,
@@ -35,7 +135,10 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
35
135
  value,
36
136
  defaultValue,
37
137
  fref,
38
- autoComplete
138
+ autoComplete,
139
+ elastic,
140
+ minRows,
141
+ maxRows
39
142
  } = props;
40
143
 
41
144
  const dispatch = useDispatch(STORE_FORM_KEY)
@@ -47,6 +150,29 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
47
150
 
48
151
  const _defaultCSS = `width: 100%;border-radius: var(--radius-base);padding-left: 4px;padding-right: 4px;border-style: solid;border-width: 1px;border-color: var(--colors-gray-200);`;
49
152
 
153
+ const isControlled = props.value !== undefined;
154
+ const measurementsCacheRef = useRef<SizingData>();
155
+
156
+ const handleElastic = () => {
157
+ const node = _ref.current!
158
+ const nodeSizingData = measurementsCacheRef.current
159
+ ? measurementsCacheRef.current : getSizingData(node)
160
+ if (!nodeSizingData) {
161
+ return;
162
+ }
163
+ measurementsCacheRef.current = nodeSizingData;
164
+
165
+ // const [height, rowHeight] = calculateNodeHeight(
166
+ // nodeSizingData,
167
+ // node.value || node.placeholder || 'x',
168
+ // minRows || 6,
169
+ // maxRows || 6,
170
+ // );
171
+
172
+ }
173
+
174
+ useEffect(() => {}, [])
175
+
50
176
  return (
51
177
  <ClassNames>
52
178
  {({ css, cx }) => <El
@@ -63,27 +189,36 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
63
189
  defaultValue={defaultValue || ``}
64
190
  onKeyUp={(e : SyntheticEvent) => {
65
191
  let k = e['keyCode'] || ['which'];
66
- if(El != 'textarea' && k == 13 && form && onSubmit){
192
+ if(form && onSubmit && El != 'textarea' && k == 13){
67
193
  onSubmit(forms[form]);
194
+ }else{
195
+ if(onKeyUp) onKeyUp(e)
68
196
  }
197
+
69
198
  }}
70
199
  onChange={e => {
71
200
  let val = type == 'file' ?
72
201
  e.currentTarget.files
73
202
  : e.currentTarget.value;
74
- dispatch( dispatch( UPDATE_FORM_FIELD( form || 'orphan', name, val == "" ? null : val, forms ) ) );
203
+ if(form) dispatch( dispatch( UPDATE_FORM_FIELD( form || 'orphan', name, val == "" ? null : val, forms ) ) );
204
+ // if(El == `textarea` && elastic) handleElastic()
75
205
  onChange && onChange(val == "" ? null : val)
76
206
  }}
77
207
  onClick={onClick ? onClick : () => {}}
78
208
  readOnly={readOnly || false}
79
209
  onBlur={e => {
210
+ if(onBlur) onBlur(e)
80
211
  if(touched){}
81
212
  }}
82
- onFocus={ e => touched == false && dispatch( UPDATE_FORM_FIELD( form || 'orphan', `touched`, true, forms ) ) }
213
+ onFocus={ e => {
214
+ if(touched == false)dispatch( UPDATE_FORM_FIELD( form || 'orphan', `touched`, true, forms ) )
215
+ if(onFocus) onFocus(e)
216
+ }}
83
217
  />}
84
218
  </ClassNames>
85
219
  )
86
-
220
+
221
+
87
222
  })
88
223
 
89
224
  export default Input
@@ -0,0 +1,12 @@
1
+ import {
2
+ forwardRef,
3
+ LegacyRef
4
+ } from 'react'
5
+ import { ClassNames } from '@emotion/react'
6
+ import { buildCSS } from '../core'
7
+
8
+ const MediaPlayer = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTMLImageElement>) => {
9
+
10
+ })
11
+
12
+ export default MediaPlayer;
@@ -16,12 +16,17 @@ class Toaster {
16
16
  this.#defaultTimeLeft = 4
17
17
  const rootID = config?.root || `toast-container`;
18
18
  this.#root = rootID;
19
- if(window.document.querySelector(`#${rootID}`)) return;
20
- var self = this;
19
+
20
+ this.addRoot();
21
+
22
+ }
23
+
24
+ addRoot(){
25
+ if(window.document.querySelector(`#${this.#root}`)) return;
21
26
  var root = window.document.createElement('div');
22
- root.id = rootID;
23
- window.document.body.appendChild(root);
24
- this.#container = window.document.querySelector(`#${rootID}`)
27
+ root.id = this.#root;
28
+ window.document.body.appendChild(root);
29
+ this.#container = window.document.querySelector(`#${this.#root}`)
25
30
  }
26
31
 
27
32
  show = (
@@ -57,6 +62,7 @@ class Toaster {
57
62
  }){
58
63
 
59
64
  var self = this;
65
+ self.addRoot();
60
66
  var tout = null,
61
67
  ID = 'toast-' + nanoid(),
62
68
  toast = window.document.createElement('div'),
@@ -32,11 +32,11 @@ class AppTheme {
32
32
  get = () => {
33
33
  let self = this;
34
34
  if(self.#mode === "auto"){
35
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
35
+ typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
36
36
  self.#mode = event.matches ? "dark" : "light";
37
37
  self.#listen(self.#mode);
38
38
  });
39
- return window.matchMedia &&
39
+ return typeof window !== 'undefined' && window.matchMedia &&
40
40
  window.matchMedia('(prefers-color-scheme: dark)').matches ?
41
41
  self.#darkTheme : self.#lightTheme;
42
42
  }else{
package/src/core/index.ts CHANGED
@@ -45,7 +45,7 @@ const cleanProps = (props : any) => {
45
45
  delete _props[k]
46
46
  }
47
47
  });
48
- let _extras = [`as`, `hover`, `bref`]
48
+ let _extras = [`as`,`hover`,`bref`,`tag`]
49
49
  _extras.map(x => x in _props && delete _props[x])
50
50
  return _props
51
51
  }
@@ -105,7 +105,7 @@ const buildFormData = (data : object) : FormData => {
105
105
  return formData;
106
106
  }
107
107
 
108
- const grab = async (uri : string, data : object, timeout : number = 60, fd : object = null, progress? : Function, bearer : string = `__ha`) => {
108
+ const withRest = async (uri : string, data : object, timeout : number = 60, fd : object = null, progress? : Function, bearer : string = `__ha`) => {
109
109
  var Bearer = getCookie(bearer) || `${randstr(8)}^${randstr(8)}`;
110
110
  window['__grabToken'] = axios.CancelToken.source();
111
111
  if(fd){
@@ -228,7 +228,7 @@ const filterHTMLProps = (props : any) => {
228
228
  return filter;
229
229
  }
230
230
 
231
- const uuid = () => nanoid()
231
+ const uuid = (len=21) => nanoid(len)
232
232
 
233
233
  const addScript = (src: string, callback: () => any) => {
234
234
  var s = document.createElement('script')
@@ -320,6 +320,77 @@ const getMousePosition = e => {
320
320
  return pos;
321
321
  }
322
322
 
323
+ const copyToClipboard = (str : string, callback: Function) => {
324
+ const el = document.createElement('textarea');
325
+ let storeContentEditable = el.contentEditable;
326
+ let storeReadOnly = el.readOnly;
327
+ el.value = str;
328
+ el.contentEditable = `true`;
329
+ el.readOnly = false;
330
+ el.setAttribute('readonly', `false`);
331
+ el.setAttribute('contenteditable', `true`);
332
+ el.style.position = 'absolute';
333
+ el.style.left = '-999999999px';
334
+ document.body.appendChild(el);
335
+ const selected =
336
+ document.getSelection().rangeCount > 0
337
+ ? document.getSelection().getRangeAt(0)
338
+ : false;
339
+ el.select();
340
+ el.setSelectionRange(0, 999999);
341
+ document.execCommand('copy');
342
+ document.body.removeChild(el);
343
+ if (selected) {
344
+ document.getSelection().removeAllRanges();
345
+ document.getSelection().addRange(selected);
346
+ }
347
+ el.contentEditable = storeContentEditable;
348
+ el.readOnly = storeReadOnly;
349
+ if(callback) callback()
350
+ }
351
+
352
+ /**
353
+ * Format bytes as human-readable text.
354
+ *
355
+ * @param bytes Number of bytes.
356
+ * @param si True to use metric (SI) units, aka powers of 1000. False to use
357
+ * binary (IEC), aka powers of 1024.
358
+ * @param dp Number of decimal places to display.
359
+ *
360
+ * @return Formatted string.
361
+ */
362
+ const formatSize = (bytes, si=false, dp=1) => {
363
+ const thresh = si ? 1000 : 1024;
364
+
365
+ if (Math.abs(bytes) < thresh) {
366
+ return bytes + ' B';
367
+ }
368
+
369
+ const units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
370
+ let u = -1;
371
+ const r = 10**dp;
372
+
373
+ do {
374
+ bytes /= thresh;
375
+ ++u;
376
+ } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
377
+
378
+
379
+ return bytes.toFixed(dp) + ' ' + units[u];
380
+ }
381
+
382
+ const slugify = (...args: (string | number)[]): string => {
383
+ const value = args.join(' ')
384
+
385
+ return value
386
+ .normalize('NFD') // split an accented letter in the base letter and the acent
387
+ .replace(/[\u0300-\u036f]/g, '') // remove all previously split accents
388
+ .toLowerCase()
389
+ .trim()
390
+ .replace(/[^a-z0-9 ]/g, '') // remove all chars not letters, numbers and spaces (to be replaced)
391
+ .replace(/\s+/g, '-') // separator
392
+ }
393
+
323
394
  export {
324
395
  addProps,
325
396
  addScript,
@@ -329,7 +400,7 @@ export {
329
400
  byName,
330
401
  byId,
331
402
  el,
332
- grab,
403
+ withRest,
333
404
  isEmail,
334
405
  isIPv4,
335
406
  isUrl,
@@ -352,6 +423,9 @@ export {
352
423
  getHostname,
353
424
  parseFilename,
354
425
  camelCase,
355
- getMousePosition
426
+ getMousePosition,
427
+ formatSize,
428
+ copyToClipboard,
429
+ slugify
356
430
  }
357
431
 
@@ -3,6 +3,10 @@ const cssProps : { [key: string] : any } = {
3
3
  "alignContent": "align-content",
4
4
 
5
5
  "aic": "aic",
6
+ "ais": "ais",
7
+ "aie": "aie",
8
+ "nous": "nous",
9
+ "nope": "nope",
6
10
  "ai": "align-items",
7
11
  "alignItems": "align-items",
8
12
 
@@ -278,6 +282,7 @@ const cssProps : { [key: string] : any } = {
278
282
  "wordBreak": "word-break",
279
283
  "wordSpacing": "word-spacing",
280
284
  "wrap": "wrap",
285
+ "textWrap": "textWrap",
281
286
  "wordWrap": "word-wrap",
282
287
  "writingMode": "writing-mode",
283
288
  "zIndex": "z-index",
@@ -317,6 +322,8 @@ const cssPropsDirect : { [key : string] : any } = {
317
322
  'flex' : 'display: flex;',
318
323
  'fwrap' : 'flex-wrap: wrap;',
319
324
  'aic' : 'align-items: center;',
325
+ 'ais' : 'align-items: flex-start;',
326
+ 'aie' : 'align-items: flex-end;',
320
327
  'ass' : 'align-self: flex-start;',
321
328
  'asc' : 'align-self: center;',
322
329
  'ase' : 'align-self: flex-end;',
@@ -329,13 +336,17 @@ const cssPropsDirect : { [key : string] : any } = {
329
336
  'block' : 'display: block;',
330
337
  'bold' : "font-weight: bold;",
331
338
  'wrap' : "word-wrap: break-word;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 99%;",
339
+ 'textWrap' : "word-wrap: break-word;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 99%;",
332
340
  'pointer' : "cursor: pointer;",
333
341
  'fb' : 'font-family: var(--primary-font-bold);',
334
342
  'ph' : 'padding-left: __VALUE__;padding-right: __VALUE__;',
335
343
  'pv' : 'padding-bottom: __VALUE__;padding-top: __VALUE__;',
336
344
  'mv' : 'margin-bottom: __VALUE__;margin-top: __VALUE__;',
337
345
  'mh' : 'margin-left: __VALUE__;margin-right: __VALUE__;',
338
- 'anim' : 'transition:all __VALUE__s linear 0s;'
346
+ 'anim' : 'transition:all __VALUE__s linear 0s;',
347
+ 'nous' : 'user-select: none;',
348
+ 'nope' : 'pointer-events: none;',
349
+ 'tdn' : 'text-decoration: none;',
339
350
  }
340
351
 
341
352
  const cssPropsIgnore : string[] = [
@@ -6,4 +6,5 @@ export { default as useResizeObserver } from './useResizeObserver'
6
6
  export { default as useDevice } from './useDevice'
7
7
  export { default as useToast } from './useToast'
8
8
  export { default as useLang } from './useLang'
9
- export { default as useContextMenu } from './useContextMenu'
9
+ export { default as useContextMenu } from './useContextMenu'
10
+ export { default as useRender } from './useRender'