@zuzjs/ui 0.3.2 → 0.3.4

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 (88) hide show
  1. package/README.md +0 -0
  2. package/dist/hooks.js +89 -0
  3. package/dist/styles.css +37 -62
  4. package/dist/ui.js +665 -0
  5. package/jest.config.js +0 -0
  6. package/package.json +16 -18
  7. package/rollup.config.js +30 -47
  8. package/src/comps/box.tsx +24 -28
  9. package/src/comps/button.tsx +23 -47
  10. package/src/comps/crumb.tsx +9 -0
  11. package/src/comps/form.tsx +57 -88
  12. package/src/comps/heading.tsx +25 -31
  13. package/src/comps/icon.tsx +24 -36
  14. package/src/comps/input.tsx +24 -224
  15. package/src/comps/select.tsx +23 -63
  16. package/src/comps/spinner.tsx +23 -35
  17. package/src/comps/stylesheet.tsx +5 -0
  18. package/src/context/AppContext.tsx +2 -2
  19. package/src/context/AppProvider.tsx +68 -105
  20. package/src/context/createSlice.tsx +15 -39
  21. package/src/context/index.tsx +4 -5
  22. package/src/core/css.ts +1 -0
  23. package/src/core/index.tsx +241 -0
  24. package/src/core/styles.ts +378 -371
  25. package/src/hooks/index.tsx +2 -10
  26. package/src/hooks/useDispatch.tsx +36 -36
  27. package/src/hooks/useStore.tsx +24 -26
  28. package/src/hooks.tsx +8 -0
  29. package/src/scss/mixins.scss +2 -2
  30. package/src/scss/props.scss +91 -69
  31. package/src/scss/{style.scss → styles.scss} +102 -132
  32. package/src/ui.tsx +13 -0
  33. package/tsconfig.json +0 -0
  34. package/tsconfig.lib.json +0 -0
  35. package/tsconfig.spec.json +0 -0
  36. package/dist/index.js +0 -1868
  37. package/src/actions/addForm.tsx +0 -0
  38. package/src/actions/index.tsx +0 -29
  39. package/src/actions/redo.tsx +0 -1
  40. package/src/actions/reset.tsx +0 -1
  41. package/src/actions/undo.tsx +0 -1
  42. package/src/comps/app.tsx +0 -34
  43. package/src/comps/checkbox.tsx +0 -74
  44. package/src/comps/component.tsx +0 -32
  45. package/src/comps/contextmenu.tsx +0 -60
  46. package/src/comps/cover.tsx +0 -34
  47. package/src/comps/image.tsx +0 -34
  48. package/src/comps/masonry.tsx +0 -192
  49. package/src/comps/mediaplayer.tsx +0 -12
  50. package/src/comps/placeholder.tsx +0 -58
  51. package/src/comps/root.tsx +0 -32
  52. package/src/comps/spacer.tsx +0 -20
  53. package/src/comps/text.tsx +0 -27
  54. package/src/comps/toaster.tsx +0 -117
  55. package/src/comps/tweet.tsx +0 -48
  56. package/src/context/_AppProvider.tsx +0 -116
  57. package/src/context/combineReducers.tsx +0 -47
  58. package/src/context/combineState.tsx +0 -14
  59. package/src/context/reduceReducers.tsx +0 -6
  60. package/src/context/store/appbase.tsx +0 -19
  61. package/src/context/store/lang.tsx +0 -26
  62. package/src/context/store/theme.tsx +0 -54
  63. package/src/core/defaultTheme.ts +0 -90
  64. package/src/core/extractCurrentDesignState.tsx +0 -0
  65. package/src/core/index.ts +0 -431
  66. package/src/core/router.ts +0 -86
  67. package/src/hooks/useAppReducer.tsx +0 -40
  68. package/src/hooks/useChooseEffect.tsx +0 -6
  69. package/src/hooks/useContextMenu.tsx +0 -123
  70. package/src/hooks/useDevice.tsx +0 -164
  71. package/src/hooks/useImage.tsx +0 -84
  72. package/src/hooks/useLang.tsx +0 -9
  73. package/src/hooks/useMediaPlayer.tsx +0 -27
  74. package/src/hooks/useNavigator.tsx +0 -6
  75. package/src/hooks/useRender.tsx +0 -29
  76. package/src/hooks/useResizeObserver.tsx +0 -84
  77. package/src/hooks/useRouter.tsx +0 -45
  78. package/src/hooks/useSelector.tsx +0 -9
  79. package/src/hooks/useTheme.tsx +0 -9
  80. package/src/hooks/useToast.tsx +0 -11
  81. package/src/index.tsx +0 -35
  82. package/src/kit/Builder.tsx +0 -18
  83. package/src/kit/Component.tsx +0 -32
  84. package/src/kit/Header.tsx +0 -21
  85. package/src/redux/slices/app.js +0 -26
  86. package/src/redux/slices/form.js +0 -46
  87. package/src/redux/store.js +0 -33
  88. package/src/scss/constants.scss +0 -4
package/src/core/index.ts DELETED
@@ -1,431 +0,0 @@
1
- import {
2
- cloneElement,
3
- isValidElement
4
- } from 'react'
5
- import type { ReactNode, ReactElement } from 'react'
6
- import Children from 'react-children-utilities';
7
- import Cookies from 'js-cookie'
8
- import axios from 'axios'
9
- import {
10
- cssPropsDirect,
11
- cssProps,
12
- cssPropsVals,
13
- cssPropsIgnore,
14
- cssColors
15
- } from './styles'
16
- import { nanoid } from 'nanoid'
17
- import Input from '../comps/input'
18
- import Select from '../comps/select'
19
- import Button from '../comps/button'
20
- import { generateModalRoutes, generatePreservedRoutes, generateRegularRoutes } from './router'
21
-
22
- const makeCSSValue = (k : any, v : any, o : any) => {
23
- let ignore = cssPropsIgnore.indexOf(o) == -1;
24
- if(k in cssPropsDirect && ignore == true){
25
- // return cssPropsDirect[k];
26
- return cssPropsDirect[k].indexOf(`__VALUE__`) > - 1 ?
27
- cssPropsDirect[k].replaceAll(`__VALUE__`, `${v}${"number" == typeof v ? `px` : ``}`)
28
- : cssPropsDirect[k];
29
- }
30
- let unit = "number" == typeof v && ignore ? `px` : ``;
31
- if(cssColors.indexOf(v) > -1){
32
- v = `var(--colors-${v.replaceAll(`.`, `-`)})`;
33
- unit = ``;
34
- }
35
- else if(v in cssPropsVals){
36
- v = cssPropsVals[v];
37
- }
38
- return `${k}:${v}${unit};`;
39
- }
40
-
41
- const cleanProps = (props : any) => {
42
- let _props = { ...props }
43
- Object.keys(_props).map(k => {
44
- if(k in cssProps){
45
- delete _props[k]
46
- }
47
- });
48
- let _extras = [`as`,`hover`,`bref`,`tag`]
49
- _extras.map(x => x in _props && delete _props[x])
50
- return _props
51
- }
52
-
53
- const buildCSS = (props : any) => {
54
- let css = ``;
55
- Object.keys(props).map(k => {
56
- css += k in cssProps ? makeCSSValue(cssProps[k], props[k], k) : '';
57
- });
58
- return css;
59
- }
60
-
61
- const setCSSVar = ( key : string, val : string ) => {
62
- document.documentElement.style.setProperty(`--${key}`, val);
63
- }
64
-
65
- const isUrl = (u : string) => {
66
- let url;
67
- try{
68
- url = new URL(u);
69
- }catch(_){
70
- return false;
71
- }
72
- return url.protocol === 'http:' || url.protocol === 'https:'
73
- }
74
-
75
- const isEmail = (e : string) => {
76
- let reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
77
- return reg.test(e);
78
- }
79
-
80
- const isIPv4 = (ipaddress : string) => {
81
- return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress);
82
- }
83
-
84
- const randstr = function(len? : number){
85
- var text = ""; var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
86
- len = len || 10;
87
- for (var i = 0; i < len; i++){
88
- text += possible.charAt(Math.floor(Math.random() * possible.length));
89
- }
90
- return text;
91
- }
92
-
93
- const setCookie = (key : string, value : any, expiry? : number, host? : string) => Cookies.set(key, value, { expires: expiry || 7, domain: host || window.location.host })
94
-
95
- const getCookie = (key : string) => Cookies.get(key) || null;
96
-
97
- const removeCookie = (key : string) => Cookies.remove(key)
98
-
99
- const buildFormData = (data : object) : FormData => {
100
- var formData = new FormData();
101
- var _data = Cookies.get();
102
- Object.keys(_data).map(k => formData.append(k, _data[k]));
103
- Object.keys(data).filter(x => x != 'files').map(k => formData.append(k, data[k]));
104
- if('files' in data) [data['files']].map((f: any) => formData.append('files[]', f));
105
- return formData;
106
- }
107
-
108
- const grab = async (uri : string, data : object, timeout : number = 60, fd : object = null, progress? : Function, bearer : string = `__ha`) => {
109
- var Bearer = getCookie(bearer) || `${randstr(8)}^${randstr(8)}`;
110
- window['__grabToken'] = axios.CancelToken.source();
111
- if(fd){
112
- return new Promise((resolve, reject) => {
113
- axios({
114
- method: "post",
115
- url: uri,
116
- data: buildFormData(data),
117
- timeout: 1000 * timeout,
118
- cancelToken: window['__grabToken'].token,
119
- headers: {
120
- 'Content-Type': 'multipart/form-data',
121
- 'Authorization': `Bearer ${Bearer}`
122
- },
123
- onUploadProgress: ev => {
124
- //TODO: Add progress
125
- // if(progress) progress(ev.)
126
- }
127
- })
128
- .then(resp => {
129
- if(resp.data && "kind" in resp.data){
130
- resolve(resp.data)
131
- }else{
132
- reject(resp.data)
133
- }
134
- })
135
- .catch(err => reject(err));
136
- })
137
- }
138
- return new Promise((resolve, reject) => {
139
- axios.post(
140
- uri,
141
- {
142
- ...Cookies.get(),
143
- ...data,
144
- __ustmp: new Date().getTime() / 1000
145
- },
146
- {
147
- timeout: 1000 * timeout,
148
- headers: {
149
- 'Content-Type': 'application/json',
150
- 'Authorization': `Bearer ${Bearer}`
151
- },
152
- cancelToken: window['__grabToken'].token
153
- }
154
- )
155
- .then(resp => {
156
- if(resp.data && "kind" in resp.data){
157
- resolve(resp.data)
158
- }else{
159
- reject(resp.data)
160
- }
161
- })
162
- .catch(err => reject(err));
163
- })
164
- }
165
-
166
- const el = (e : string) => document.querySelector(e);
167
-
168
- const byName = (e : string) => document.querySelector(`*[name="${e}"]`);
169
-
170
- const byId = (e : string) => document.getElementById(e);
171
-
172
- const addProps = (children : any, prop : any) => {
173
- let form = {}
174
- let allowedFields = [Input, Select, Button];
175
- let child = Children.deepMap(children, c => {
176
- if(allowedFields.indexOf(c['type']) > -1 && c['props'] && !form[c['props'].name]){
177
- form[c['props'].name] = c['props'].value || null;
178
- }
179
- let Clone = cloneElement(c as ReactElement<any>, prop);
180
- return Clone;
181
- });
182
-
183
- return {
184
- children : child,
185
- fields: form
186
- }
187
- }
188
-
189
- const ucfirst = ( str : string ) => {
190
- return str.charAt(0).toUpperCase() + str.slice(1);
191
- }
192
-
193
- const filterStyleProps = (props : any) => {
194
-
195
- const pks = Object.keys(props);
196
- const css = Object.keys(cssProps);
197
- let filter = {}
198
- css.filter(x => pks.includes(x))
199
-
200
- // .map(k => filter[k] = props[k].toString());
201
- return filter;
202
- // const allowed = Object.keys(props);
203
- // let allowed = Object.keys(cssProps);
204
-
205
- // return Object.keys(props)
206
- // .filter( k => allowed.includes(k))
207
- // .reduce( (o, k) => {
208
- // // console.log(o)
209
- // console.log(k, props[k])
210
- // // o[k] = props[k].toString();
211
- // // return o;
212
- // })
213
- // return [props].filter( row => (
214
- // Object.keys(row)
215
- // .map(key => css.includes(key))
216
- // ));
217
- }
218
-
219
- const filterHTMLProps = (props : any) => {
220
- const pks = Object.keys(props);
221
- const css = Object.keys(cssProps);
222
- let filter = {}
223
- pks.filter(x => {
224
- if(x!= `for` && !css.includes(x)){
225
- filter[x] = props[x]
226
- }
227
- })
228
- return filter;
229
- }
230
-
231
- const uuid = (len=21) => nanoid(len)
232
-
233
- const addScript = (src: string, callback: () => any) => {
234
- var s = document.createElement('script')
235
- s.setAttribute('src', src)
236
- s.addEventListener('load', callback, false)
237
- document.body.appendChild(s)
238
- }
239
-
240
- const shuffleArray = array => {
241
- for (let i = array.length - 1; i > 0; i--) {
242
- const j = Math.floor(Math.random() * (i + 1));
243
- [array[i], array[j]] = [array[j], array[i]];
244
- }
245
- return array
246
- }
247
-
248
- const getUriParams = () => {
249
- var search = location.search.substring(1);
250
- if(search=='') return JSON.parse('{}');
251
- var xny = {};
252
- if("URLSearchParams" in window){
253
- var items = new URLSearchParams(search);
254
- for(const [k, v] of items){
255
- xny[k] = v || ``;
256
- }
257
- }else{
258
- try{
259
- xny = JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}');
260
- }catch(e){
261
- xny = {};
262
- }
263
- }
264
- return xny;
265
- }
266
-
267
- const rgb2hex = rgb => `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(n => parseInt(n, 10).toString(16).padStart(2, '0')).join('')}`
268
-
269
- const getHostname = url => {
270
- if(window.URL){
271
- let u = new window.URL(url);
272
- return u.hostname
273
- }else{
274
- var a = document.createElement(`a`)
275
- a.href = url
276
- return a.hostname.replace("www.", "")
277
- }
278
- }
279
-
280
- const imgPromiseFactory = ({decode = true, crossOrigin = ''}) =>
281
- (src): Promise<void> => {
282
- return new Promise((resolve, reject) => {
283
- const i = new Image()
284
- if (crossOrigin) i.crossOrigin = crossOrigin
285
- i.onload = () => {
286
- decode && i.decode ? i.decode().then(resolve).catch(reject) : resolve()
287
- }
288
- i.onerror = reject
289
- i.src = src
290
- })
291
- }
292
-
293
- const parseFilename = nm => {
294
- var re = /(?:\.([^.]+))?$/;
295
- return {
296
- name: nm.split('.').slice(0, -1).join('.'),
297
- ext: re.exec(nm)[1]
298
- }
299
- }
300
-
301
- const camelCase = str => str.replace(":", "-").replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
302
-
303
- const getMousePosition = e => {
304
- const pos = {
305
- x: (e as MouseEvent).clientX,
306
- y: (e as MouseEvent).clientY,
307
- };
308
-
309
- const touch = (e as TouchEvent).changedTouches;
310
-
311
- if (touch) {
312
- pos.x = touch[0].clientX;
313
- pos.y = touch[0].clientY;
314
- }
315
-
316
- if (!pos.x || pos.x < 0) pos.x = 0;
317
-
318
- if (!pos.y || pos.y < 0) pos.y = 0;
319
-
320
- return pos;
321
- }
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
-
394
- export {
395
- addProps,
396
- addScript,
397
- buildCSS,
398
- cleanProps,
399
- buildFormData,
400
- byName,
401
- byId,
402
- el,
403
- grab,
404
- isEmail,
405
- isIPv4,
406
- isUrl,
407
- imgPromiseFactory,
408
- randstr,
409
- setCSSVar,
410
- getCookie,
411
- setCookie,
412
- removeCookie,
413
- ucfirst,
414
- filterStyleProps,
415
- filterHTMLProps,
416
- uuid,
417
- shuffleArray,
418
- getUriParams,
419
- rgb2hex,
420
- generateModalRoutes,
421
- generatePreservedRoutes,
422
- generateRegularRoutes,
423
- getHostname,
424
- parseFilename,
425
- camelCase,
426
- getMousePosition,
427
- formatSize,
428
- copyToClipboard,
429
- slugify
430
- }
431
-
@@ -1,86 +0,0 @@
1
- export const patterns = {
2
- route: [/^.*\/src\/pages\/|\.(jsx|tsx)$/g, ''],
3
- splat: [/\[\.{3}\w+\]/g, '*'],
4
- param: [/\[([^\]]+)\]/g, ':$1'],
5
- slash: [/^index$|\./g, '/'],
6
- optional: [/^-(:?[\w-]+)/, '$1?'],
7
- } as const
8
-
9
- type PreservedKey = '_app' | '404'
10
- type BaseRoute = { id?: string; path?: string; children?: BaseRoute[] } & Record<string, any>
11
-
12
- export const generatePreservedRoutes = <T>(files: Record<string, T | any>): Partial<Record<PreservedKey, T>> => {
13
- return Object.keys(files).reduce((routes, key) => {
14
- const path = key.replace(...patterns.route)
15
- return { ...routes, [path]: files[key] }
16
- }, {})
17
- }
18
-
19
- export const generateRegularRoutes = <T extends BaseRoute, M>(
20
- files: Record<string, any>,
21
- buildRoute: (module: M, key: string) => T
22
- ) => {
23
- const filteredRoutes = Object.keys(files).filter((key) => !key.includes('/_') || /_layout\.(jsx|tsx)$/.test(key))
24
- return filteredRoutes.reduce<T[]>((routes, key) => {
25
- const module = files[key]
26
- const route = { id: key.replace(...patterns.route), ...buildRoute(module, key) }
27
-
28
- const segments = key
29
- .replace(...patterns.route)
30
- .replace(...patterns.splat)
31
- .replace(...patterns.param)
32
- .split('/')
33
- .filter(Boolean)
34
-
35
- segments.reduce((parent, segment, index) => {
36
- const path = segment.replace(...patterns.slash).replace(...patterns.optional)
37
- const root = index === 0
38
- const leaf = index === segments.length - 1 && segments.length > 1
39
- const node = !root && !leaf
40
- const layout = segment === '_layout'
41
- const group = /\(\w+\)/.test(path)
42
- const insert = /^\w|\//.test(path) ? 'unshift' : 'push'
43
-
44
- if (root) {
45
- const last = segments.length === 1
46
- if (last) {
47
- routes.push({ path, ...route })
48
- return parent
49
- }
50
- }
51
-
52
- if (root || node) {
53
- const current = root ? routes : parent.children
54
- const found = current?.find((route) => route.path === path || route.id?.split('@')?.[0] === path)
55
- const props = group ? (route?.component ? { id: path, path: '/' } : { id: path }) : { path }
56
- if (found) found.children ??= []
57
- else current?.[insert]({ ...props, children: [] })
58
- return found || (current?.[insert === 'unshift' ? 0 : current.length - 1] as BaseRoute)
59
- }
60
-
61
- if (layout) {
62
- return Object.assign(parent, { ...route, id: `${parent.id || parent.path}@${route.id}` })
63
- }
64
-
65
- if (leaf) {
66
- parent?.children?.[insert](route?.index ? route : { path, ...route })
67
- }
68
-
69
- return parent
70
- }, {} as BaseRoute)
71
-
72
- return routes
73
- }, [])
74
- }
75
-
76
- export const generateModalRoutes = <T>(files: Record<string, T | any>): Record<string, T> => {
77
- return Object.keys(files).reduce((modals, key) => {
78
- const path = key
79
- .replace(...patterns.route)
80
- .replace(/\+|\(\w+\)\//g, '')
81
- .replace(/(\/)?index/g, '')
82
- .replace(/\./g, '/')
83
-
84
- return { ...modals, [`/${path}`]: files[key]?.default }
85
- }, {})
86
- }
@@ -1,40 +0,0 @@
1
- import { useReducer } from 'react'
2
- import { UNDO, REDO, RESET } from '../actions'
3
-
4
- const useAppReducer = (reducer, inititalState : Object, passedConfig = {}) => {
5
-
6
- const initialStateWithUndoRedo = {
7
- ...inititalState,
8
- pastDesignStates: [],
9
- futureDesignStates: [],
10
- hasRedo: false,
11
- hasUndo: false,
12
- }
13
-
14
- const undoRedoResetReducer = (state, action) => {
15
-
16
- const newPresetState = reducer(state, action) || initialStateWithUndoRedo
17
-
18
- if([UNDO,REDO,RESET].includes(action.type)){
19
- return newPresetState;
20
- }
21
-
22
- if(newPresetState.isDesignState){
23
-
24
- const newState = {
25
-
26
- }
27
-
28
- return newState;
29
-
30
- }
31
-
32
- return newPresetState;
33
-
34
- }
35
-
36
- return useReducer(undoRedoResetReducer, initialStateWithUndoRedo)
37
-
38
- }
39
-
40
- export default useAppReducer;
@@ -1,6 +0,0 @@
1
- import { useEffect, useLayoutEffect } from 'react'
2
-
3
- const useChooseEffect =
4
- typeof window === 'undefined' ? useEffect : useLayoutEffect
5
-
6
- export default useChooseEffect
@@ -1,123 +0,0 @@
1
- import { SyntheticEvent, useEffect, useState, useRef } from "react"
2
- import ReactDOM from 'react-dom/client'
3
- import Box from '../comps/box'
4
- import { getMousePosition } from "../core"
5
-
6
- const Menu = ({ ID, hide, e, items, width }) => {
7
-
8
- const nodeRef = useRef(null);
9
- const [p, setP] = useState(getMousePosition(e as MouseEvent));
10
- const [visible, setVisible] = useState(false);
11
-
12
-
13
- const checkBoundaries = (x: number, y: number) => {
14
- if (nodeRef.current) {
15
- const { innerWidth, innerHeight } = window;
16
- const { offsetWidth, offsetHeight } = nodeRef.current;
17
- if(x + offsetWidth > innerWidth) x -= offsetWidth //x + offsetWidth - innerWidth;
18
- if (y + offsetHeight > innerHeight) y -= offsetHeight;
19
- }
20
- setP({ x, y })
21
- setVisible(true)
22
- }
23
-
24
- useEffect(() => {
25
- checkBoundaries(p.x, p.y);
26
- }, [e])
27
-
28
- return (
29
- <Box
30
- bref={nodeRef}
31
- flex dir={`cols`}
32
- fixed
33
- top={p.y}
34
- left={p.x}
35
- w={width || 220}
36
- opacity={visible ? 1 : 0}
37
- as={`zuz-contextmenu ${ID}`}>
38
- {(items as Array<any>).map((m, i) => m.id == `line` ? <Box as={`line`} key={`line-${i}-${m.id}`} /> : <button
39
- key={`cm-${i}-${m.id}`}
40
- onClick={ev => {
41
- if(m.onClick){
42
- m.onClick(ev, m)
43
- }else{
44
- console.log(`No onClick eventFound`)
45
- }
46
- hide()
47
- }}>{m.label}</button>)}
48
- </Box>
49
- )
50
- }
51
-
52
- const useContextMenu = (
53
- contextID : string,
54
- contextWidth: number,
55
- contextToken = `____uchides`
56
- ) => {
57
-
58
- const ID = `contextmenu-${contextID}`
59
- const [visible, setVisible] = useState(false)
60
- const [root, setRoot] = useState(null)
61
-
62
- const el = (e : string) => window.document.createElement(e)
63
-
64
-
65
- const createRoot = () => {
66
- if(!window.document.querySelector(`#${ID}`)){
67
- let div = el(`div`)
68
- div.id = ID
69
- window.document.body.appendChild(div)
70
- }
71
- }
72
-
73
- const hideAll = () => {
74
- if(window[contextToken]){
75
- window[contextToken].map((h : Object) => h['ID'] != ID && h['fnc']())
76
- }
77
- }
78
-
79
- const _hide = () => {
80
- try{
81
- root?.unmount()
82
- document.querySelector(`#${ID}`).parentNode.removeChild(document.querySelector(`#${ID}`))
83
- setRoot(null)
84
- }catch(e){}
85
- }
86
-
87
- const hide = () => {
88
- _hide()
89
- hideAll()
90
- }
91
-
92
- const show = (e : MouseEvent, items : Array<any>) => {
93
- e.preventDefault(); e.stopPropagation();
94
- hideAll()
95
- root.render(<Menu e={e} width={contextWidth || 220} items={items} ID={ID} hide={hide} />)
96
- }
97
-
98
- useEffect(() => {
99
- createRoot()
100
- if(!root) setRoot(ReactDOM.createRoot(document.getElementById(ID)))
101
- }, [root])
102
-
103
- useEffect(() => {
104
- if(contextToken in window == false){
105
- window[contextToken] = []
106
- }
107
- if(window[contextToken].findIndex(x => x.ID == ID) == -1){
108
- window[contextToken].push({ ID: ID, fnc: _hide })
109
- if(window[contextToken].length > document.querySelectorAll('div[id^="contextmenu-"]').length){
110
- window[contextToken].shift()
111
- }
112
- }
113
- }, [])
114
-
115
- return {
116
- show,
117
- hide,
118
- hideAll
119
- }
120
-
121
- }
122
-
123
- export default useContextMenu