@zuzjs/ui 0.3.0 → 0.3.1

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/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import Cookies from 'js-cookie';
6
6
  import axios from 'axios';
7
7
  import { nanoid } from 'nanoid';
8
8
  import { ClassNames } from '@emotion/react';
9
+ import ReactDOM from 'react-dom/client';
9
10
  export { Link } from 'react-router-dom';
10
11
  import styled from '@emotion/styled';
11
12
 
@@ -80,7 +81,7 @@ class AppTheme {
80
81
  };
81
82
  const conf = _conf || {};
82
83
  __classPrivateFieldSet(this, _AppTheme_listen, "listen" in conf ? conf.listen : (mod) => { console.log(`Theme switched to ${mod}`); }, "f");
83
- __classPrivateFieldSet(this, _AppTheme_mode, conf.mode || "auto", "f");
84
+ __classPrivateFieldSet(this, _AppTheme_mode, conf.mode || conf.theme.mode || "auto", "f");
84
85
  __classPrivateFieldSet(this, _AppTheme_lightTheme, Object.assign({
85
86
  //CORE
86
87
  tag: "light", dark: false, primary: '#edeef5', secondary: '#f9f9f9', textColor: '#111111' }, ("theme" in conf && "light" in conf.theme ? Object.assign({}, conf.theme.light) : {})), "f");
@@ -581,14 +582,14 @@ const UPDATE_FORM_FIELD = (formName, field, value, forms) => {
581
582
  };
582
583
 
583
584
  const Input = forwardRef((props, ref) => {
584
- const { as, accept, multiple, onChange, onKeyUp, onClick, readOnly, type, tag, placeholder, name, form, touched, onSubmit, value, defaultValue, fref } = props;
585
+ const { as, accept, multiple, onChange, onKeyUp, onClick, readOnly, type, tag, placeholder, name, form, touched, onSubmit, value, defaultValue, fref, autoComplete } = props;
585
586
  const dispatch = useDispatch(STORE_FORM_KEY);
586
587
  const { forms } = useStore(state => state[STORE_FORM_KEY], false);
587
588
  let Tag = tag || 'input';
588
589
  const El = Tag;
589
590
  const _ref = fref || useRef();
590
591
  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);`;
591
- return (jsx(ClassNames, { children: ({ css, cx }) => jsx(El, { type: type || `text`, placeholder: placeholder || undefined, name: name || nanoid(), multiple: type == 'file' ? multiple : undefined, accept: accept || `*`, className: `${as ? `${as} ` : ``}f ${cx(css `${_defaultCSS}${buildCSS(props)}&:hover {${buildCSS(props.hover || {})}} &:focus {${buildCSS(props.focus || {})}}`)}`, ref: _ref, value: value || undefined, defaultValue: defaultValue || ``, onKeyUp: (e) => {
592
+ return (jsx(ClassNames, { children: ({ css, cx }) => jsx(El, Object.assign({}, cleanProps(props), { type: type || `text`, placeholder: placeholder || undefined, autoComplete: autoComplete || undefined, name: name || nanoid(), multiple: type == 'file' ? multiple : undefined, accept: accept || `*`, className: `${as ? `${as} ` : ``}f ${cx(css `${_defaultCSS}${buildCSS(props)}&:hover {${buildCSS(props.hover || {})}} &:focus {${buildCSS(props.focus || {})}}`)}`, ref: _ref, value: value || undefined, defaultValue: defaultValue || ``, onKeyUp: (e) => {
592
593
  let k = e['keyCode'] || ['which'];
593
594
  if (El != 'textarea' && k == 13 && form && onSubmit) {
594
595
  onSubmit(forms[form]);
@@ -600,7 +601,7 @@ const Input = forwardRef((props, ref) => {
600
601
  dispatch(dispatch(UPDATE_FORM_FIELD(form || 'orphan', name, val == "" ? null : val, forms)));
601
602
  onChange && onChange(val == "" ? null : val);
602
603
  }, onClick: onClick ? onClick : () => { }, readOnly: readOnly || false, onBlur: e => {
603
- }, onFocus: e => touched == false && dispatch(UPDATE_FORM_FIELD(form || 'orphan', `touched`, true, forms)) }) }));
604
+ }, onFocus: e => touched == false && dispatch(UPDATE_FORM_FIELD(form || 'orphan', `touched`, true, forms)) })) }));
604
605
  });
605
606
 
606
607
  const Select = forwardRef((props, ref) => {
@@ -947,13 +948,81 @@ const useLang = (mod = 'en') => {
947
948
  return state['lang'] || {};
948
949
  };
949
950
 
951
+ const Box = forwardRef((props, ref) => {
952
+ const _noClick = () => { };
953
+ // console.log(props)
954
+ return (jsx(ClassNames, { children: ({ css, cx }) => jsx("div", Object.assign({}, cleanProps(props), { title: props.title || undefined, id: props.id || undefined, onClick: props.onClick || _noClick, className: `${props.as ? `${props.as} ` : ``}${cx(css `${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`, ref: props.bref || ref }, { children: props.html ? jsx("span", { dangerouslySetInnerHTML: { __html: props.html } }) : props.children })) }));
955
+ });
956
+
957
+ const useContextMenu = (contextID) => {
958
+ const ID = `context-${contextID}`;
959
+ useState(false);
960
+ const [root, setRoot] = useState(null);
961
+ const nodeRef = useRef(null);
962
+ const el = (e) => window.document.createElement(e);
963
+ function checkBoundaries(x, y) {
964
+ if (nodeRef.current) {
965
+ const { innerWidth, innerHeight } = window;
966
+ const { offsetWidth, offsetHeight } = nodeRef.current;
967
+ if (x + offsetWidth > innerWidth)
968
+ x -= x + offsetWidth - innerWidth;
969
+ if (y + offsetHeight > innerHeight)
970
+ y -= y + offsetHeight - innerHeight;
971
+ }
972
+ return { x, y };
973
+ }
974
+ const hide = () => {
975
+ try {
976
+ root === null || root === void 0 ? void 0 : root.unmount();
977
+ setRoot(null);
978
+ }
979
+ catch (e) { }
980
+ };
981
+ const Menu = (e, items) => {
982
+ const p = getMousePosition(e);
983
+ const { x, y } = checkBoundaries(p.x, p.y);
984
+ return (jsx(Box, Object.assign({ bref: nodeRef, flex: true, dir: `cols`, fixed: true, top: y, left: x, as: `zuz-contextmenu ${ID}` }, { children: items && items.map((m, i) => m.id == `line` ? jsx(Box, { as: `line` }, `line-${i}-${m.id}`) : jsx("button", Object.assign({ onClick: ev => {
985
+ if (m.onClick) {
986
+ m.onClick(ev, m);
987
+ }
988
+ else {
989
+ console.log(`No onClick eventFound`);
990
+ }
991
+ hide();
992
+ } }, { children: m.label }), `cm-${i}-${m.id}`)) })));
993
+ };
994
+ const show = (e, items) => {
995
+ e.preventDefault();
996
+ e.stopPropagation();
997
+ if (!window.document.querySelector(`#context-${contextID}`)) {
998
+ let div = el(`div`);
999
+ div.id = ID;
1000
+ window.document.body.appendChild(div);
1001
+ }
1002
+ root.render(Menu(e, items));
1003
+ };
1004
+ useEffect(() => {
1005
+ if (!window.document.querySelector(`#context-${contextID}`)) {
1006
+ let div = el(`div`);
1007
+ div.id = ID;
1008
+ window.document.body.appendChild(div);
1009
+ }
1010
+ if (!root)
1011
+ setRoot(ReactDOM.createRoot(document.getElementById(ID)));
1012
+ }, [root]);
1013
+ return {
1014
+ show,
1015
+ hide
1016
+ };
1017
+ };
1018
+
950
1019
  const Button = forwardRef((props, ref) => {
951
1020
  const { forms } = useStore(state => state[STORE_FORM_KEY], false);
952
1021
  if (props.html) {
953
1022
  ({ __html: props.html });
954
1023
  }
955
1024
  return (jsx(ClassNames, { children: ({ css, cx }) => {
956
- return (jsx("button", Object.assign({ title: "title" in props ? props.title : undefined, type: props.type, className: `button${props.as ? ` ${props.as}` : ``} ${cx(css `
1025
+ return (jsx("button", Object.assign({}, cleanProps(props), { title: "title" in props ? props.title : undefined, type: props.type, className: `button${props.as ? ` ${props.as}` : ``} ${cx(css `
957
1026
  padding: 5px 10px;
958
1027
  border-radius: 2px;
959
1028
  ${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`, ref: ref, onClick: e => {
@@ -1058,6 +1127,17 @@ const makeCSSValue = (k, v, o) => {
1058
1127
  }
1059
1128
  return `${k}:${v}${unit};`;
1060
1129
  };
1130
+ const cleanProps = (props) => {
1131
+ let _props = Object.assign({}, props);
1132
+ Object.keys(_props).map(k => {
1133
+ if (k in cssProps) {
1134
+ delete _props[k];
1135
+ }
1136
+ });
1137
+ let _extras = [`as`, `hover`, `bref`];
1138
+ _extras.map(x => x in _props && delete _props[x]);
1139
+ return _props;
1140
+ };
1061
1141
  const buildCSS = (props) => {
1062
1142
  let css = ``;
1063
1143
  Object.keys(props).map(k => {
@@ -1270,6 +1350,30 @@ const imgPromiseFactory = ({ decode = true, crossOrigin = '' }) => (src) => {
1270
1350
  i.src = src;
1271
1351
  });
1272
1352
  };
1353
+ const parseFilename = nm => {
1354
+ var re = /(?:\.([^.]+))?$/;
1355
+ return {
1356
+ name: nm.split('.').slice(0, -1).join('.'),
1357
+ ext: re.exec(nm)[1]
1358
+ };
1359
+ };
1360
+ const camelCase = str => str.replace(":", "-").replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
1361
+ const getMousePosition = e => {
1362
+ const pos = {
1363
+ x: e.clientX,
1364
+ y: e.clientY,
1365
+ };
1366
+ const touch = e.changedTouches;
1367
+ if (touch) {
1368
+ pos.x = touch[0].clientX;
1369
+ pos.y = touch[0].clientY;
1370
+ }
1371
+ if (!pos.x || pos.x < 0)
1372
+ pos.x = 0;
1373
+ if (!pos.y || pos.y < 0)
1374
+ pos.y = 0;
1375
+ return pos;
1376
+ };
1273
1377
 
1274
1378
  const AppMain = forwardRef((props, ref) => {
1275
1379
  // const { dispatch } = useStore()
@@ -1282,11 +1386,6 @@ const App = forwardRef((props, ref) => {
1282
1386
  return (jsx(AppProvider, { children: jsx(AppMain, {}) }));
1283
1387
  });
1284
1388
 
1285
- const Box = forwardRef((props, ref) => {
1286
- const _noClick = () => { };
1287
- return (jsx(ClassNames, { children: ({ css, cx }) => jsx("div", Object.assign({ id: props.id || undefined, onClick: props.onClick || _noClick, className: `${props.as ? `${props.as} ` : ``}${cx(css `${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`, ref: props.bref || ref }, { children: props.html ? jsx("span", { dangerouslySetInnerHTML: { __html: props.html } }) : props.children })) }));
1288
- });
1289
-
1290
1389
  // const CoreBlock = styled.section`display: block;`
1291
1390
  // const buildComponent = (props: any) => Block.withComponent(props.for || `div`)`${buildCSS(props)}`
1292
1391
  const Component = forwardRef((props, ref) => {
@@ -1617,7 +1716,7 @@ const Tweet = (props) => {
1617
1716
 
1618
1717
  const ContextMenu = forwardRef((props, ref) => {
1619
1718
  const { as, size, color, hover, pos, items } = props;
1620
- return (jsx(Box, Object.assign({ hover: hover || {}, flex: true, fixed: true, top: (pos === null || pos === void 0 ? void 0 : pos.y) || 0, left: (pos === null || pos === void 0 ? void 0 : pos.x) || 0, bref: ref, as: `contextmenu-${as}`, ai: `c`, jc: `c`, size: size || 24, color: color || `#111111` }, { children: items && items.map((m, i) => jsx(Button, Object.assign({ onClick: m.on ? m.on : () => { } }, { children: m.label }), `cm-${i}-${m.id}`)) })));
1719
+ return (jsx(Box, Object.assign({ hover: hover || {}, flex: true, dir: `cols`, fixed: true, top: (pos === null || pos === void 0 ? void 0 : pos.y) || 0, left: (pos === null || pos === void 0 ? void 0 : pos.x) || 0, bref: ref, as: `contextmenu-${as}`, size: size || 24, color: color || `#111111` }, { children: items && items.map((m, i) => jsx(Button, Object.assign({ onClick: m.on ? m.on : () => { } }, { children: m.label }), `cm-${i}-${m.id}`)) })));
1621
1720
  });
1622
1721
 
1623
1722
  const buildElement = (el) => {
@@ -1637,4 +1736,4 @@ const Header = forwardRef((props, ref) => {
1637
1736
  return (jsx(Fragment, { children: buildComponent(data) }));
1638
1737
  });
1639
1738
 
1640
- export { App, Component as Block, Box, Button, Checkbox, ContextMenu, Cover, Form, Header, Heading, Icon, Image$1 as Image, Input, Masonry, Placeholder, AppProvider as Provider, Select, Spacer, Spinner, Text, Toaster, Tweet, addProps, addScript, buildCSS, buildFormData, byId, byName, createSlice, el, filterHTMLProps, filterStyleProps, generateModalRoutes, generatePreservedRoutes, generateRegularRoutes, getCookie, getHostname, getUriParams, grab, imgPromiseFactory, isEmail, isIPv4, isUrl, randstr, removeCookie, rgb2hex, setCSSVar, setCookie, shuffleArray, ucfirst, useDevice, useDispatch, useImage, useLang, useResizeObserver, useStore, useTheme, useToast, uuid };
1739
+ export { App, Component as Block, Box, Button, Checkbox, ContextMenu, Cover, Form, Header, Heading, Icon, Image$1 as Image, Input, Masonry, Placeholder, AppProvider as Provider, Select, Spacer, Spinner, Text, Toaster, Tweet, addProps, addScript, buildCSS, buildFormData, byId, byName, camelCase, cleanProps, createSlice, el, filterHTMLProps, filterStyleProps, generateModalRoutes, generatePreservedRoutes, generateRegularRoutes, getCookie, getHostname, getMousePosition, getUriParams, grab, imgPromiseFactory, isEmail, isIPv4, isUrl, parseFilename, randstr, removeCookie, rgb2hex, setCSSVar, setCookie, shuffleArray, ucfirst, useContextMenu, useDevice, useDispatch, useImage, useLang, useResizeObserver, useStore, useTheme, useToast, uuid };
package/dist/styles.css CHANGED
@@ -455,4 +455,31 @@ button {
455
455
  }
456
456
  .zuz-checkbox:checked + label:after {
457
457
  left: 20px;
458
+ }
459
+
460
+ .zuz-contextmenu {
461
+ min-width: 220px;
462
+ border-radius: 5px;
463
+ padding: 4px;
464
+ background: rgba(34, 34, 34, 0.5);
465
+ border: 1px rgba(255, 255, 255, 0.25) solid;
466
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.45);
467
+ backdrop-filter: blur(20px);
468
+ }
469
+ .zuz-contextmenu button {
470
+ border: 0px;
471
+ text-align: left;
472
+ padding: 4px 6px;
473
+ background: rgba(0, 0, 0, 0);
474
+ cursor: pointer;
475
+ color: #fff;
476
+ border-radius: 4px;
477
+ }
478
+ .zuz-contextmenu button:hover {
479
+ background: #385fd2;
480
+ }
481
+ .zuz-contextmenu .line {
482
+ height: 1px;
483
+ background: rgba(255, 255, 255, 0.25);
484
+ margin: 4px 6px;
458
485
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zuzjs/ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
package/src/comps/box.tsx CHANGED
@@ -3,15 +3,19 @@ import {
3
3
  useEffect
4
4
  } from 'react';
5
5
  import { ClassNames } from '@emotion/react'
6
- import { buildCSS } from '../core'
6
+ import { cleanProps, buildCSS } from '../core'
7
7
 
8
8
  const Box = forwardRef((props : { [ key: string ] : any }, ref) => {
9
9
 
10
10
  const _noClick = () => {}
11
11
 
12
+ // console.log(props)
13
+
12
14
  return (
13
15
  <ClassNames>
14
16
  {({ css, cx }) => <div
17
+ {...cleanProps(props)}
18
+ title={props.title || undefined}
15
19
  id={props.id || undefined}
16
20
  onClick={props.onClick || _noClick}
17
21
  className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)} &:hover {${buildCSS(props.hover || {})}} &:active {${buildCSS(props.active || {})}}`)}`}
@@ -5,7 +5,7 @@ import React, {
5
5
  import { ClassNames } from '@emotion/react'
6
6
  import {
7
7
  buildCSS,
8
- filterStyleProps
8
+ cleanProps
9
9
  } from '../core'
10
10
  import { useStore } from '../hooks'
11
11
  import { STORE_FORM_KEY } from '../context/AppProvider'
@@ -22,6 +22,7 @@ const Button = forwardRef((props: { [ key: string ] : any }, ref : LegacyRef<HTM
22
22
  {({ css, cx }) => {
23
23
  return (
24
24
  <button
25
+ {...cleanProps(props)}
25
26
  title={"title" in props ? props.title : undefined}
26
27
  type={props.type}
27
28
  className={`button${props.as ? ` ${props.as}` : ``} ${cx(css`
@@ -22,14 +22,12 @@ const ContextMenu = forwardRef((props : { [ key: string ] : any }, ref : LegacyR
22
22
  return (
23
23
  <Box
24
24
  hover={hover || {}}
25
- flex
25
+ flex dir={`cols`}
26
26
  fixed
27
27
  top={pos?.y || 0}
28
28
  left={pos?.x || 0}
29
29
  bref={ref}
30
30
  as={`contextmenu-${as}`}
31
- ai={`c`}
32
- jc={`c`}
33
31
  size={size || 24}
34
32
  color={color || `#111111`}
35
33
  >
@@ -7,7 +7,7 @@ import {
7
7
  } from 'react';
8
8
  import { ClassNames } from '@emotion/react'
9
9
  import { nanoid } from 'nanoid';
10
- import { buildCSS } from '../core'
10
+ import { cleanProps, buildCSS } from '../core'
11
11
  import { STORE_FORM_KEY } from '../context/AppProvider'
12
12
  import useDispatch from '../hooks/useDispatch'
13
13
  import useStore from '../hooks/useStore'
@@ -34,7 +34,8 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
34
34
  onSubmit,
35
35
  value,
36
36
  defaultValue,
37
- fref
37
+ fref,
38
+ autoComplete
38
39
  } = props;
39
40
 
40
41
  const dispatch = useDispatch(STORE_FORM_KEY)
@@ -49,8 +50,10 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
49
50
  return (
50
51
  <ClassNames>
51
52
  {({ css, cx }) => <El
53
+ {...cleanProps(props)}
52
54
  type={type || `text`}
53
55
  placeholder={placeholder || undefined}
56
+ autoComplete={autoComplete || undefined}
54
57
  name={name || nanoid()}
55
58
  multiple={type == 'file' ? multiple : undefined}
56
59
  accept={accept || `*`}
@@ -8,7 +8,7 @@ class AppTheme {
8
8
  constructor(_conf){
9
9
  const conf = _conf || {}
10
10
  this.#listen = "listen" in conf ? conf.listen : (mod) => { console.log(`Theme switched to ${mod}`); };
11
- this.#mode = conf.mode || "auto";
11
+ this.#mode = conf.mode || conf.theme.mode || "auto";
12
12
  this.#lightTheme = {
13
13
  //CORE
14
14
  tag: "light",
package/src/core/index.ts CHANGED
@@ -38,6 +38,18 @@ const makeCSSValue = (k : any, v : any, o : any) => {
38
38
  return `${k}:${v}${unit};`;
39
39
  }
40
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`]
49
+ _extras.map(x => x in _props && delete _props[x])
50
+ return _props
51
+ }
52
+
41
53
  const buildCSS = (props : any) => {
42
54
  let css = ``;
43
55
  Object.keys(props).map(k => {
@@ -278,10 +290,41 @@ const imgPromiseFactory = ({decode = true, crossOrigin = ''}) =>
278
290
  })
279
291
  }
280
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
+
281
323
  export {
282
324
  addProps,
283
325
  addScript,
284
326
  buildCSS,
327
+ cleanProps,
285
328
  buildFormData,
286
329
  byName,
287
330
  byId,
@@ -306,6 +349,9 @@ export {
306
349
  generateModalRoutes,
307
350
  generatePreservedRoutes,
308
351
  generateRegularRoutes,
309
- getHostname
352
+ getHostname,
353
+ parseFilename,
354
+ camelCase,
355
+ getMousePosition
310
356
  }
311
357
 
@@ -5,4 +5,5 @@ export { default as useDispatch } from './useDispatch'
5
5
  export { default as useResizeObserver } from './useResizeObserver'
6
6
  export { default as useDevice } from './useDevice'
7
7
  export { default as useToast } from './useToast'
8
- export { default as useLang } from './useLang'
8
+ export { default as useLang } from './useLang'
9
+ export { default as useContextMenu } from './useContextMenu'
@@ -0,0 +1,88 @@
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 useContextMenu = (contextID : string) => {
7
+
8
+ const ID = `context-${contextID}`
9
+ const [visible, setVisible] = useState(false)
10
+ const [root, setRoot] = useState(null)
11
+ const nodeRef = useRef<HTMLDivElement>(null);
12
+
13
+ const el = (e : string) => window.document.createElement(e)
14
+
15
+ function checkBoundaries(x: number, y: number) {
16
+ if (nodeRef.current) {
17
+ const { innerWidth, innerHeight } = window;
18
+ const { offsetWidth, offsetHeight } = nodeRef.current;
19
+
20
+ if (x + offsetWidth > innerWidth) x -= x + offsetWidth - innerWidth;
21
+
22
+ if (y + offsetHeight > innerHeight) y -= y + offsetHeight - innerHeight;
23
+ }
24
+
25
+ return { x, y };
26
+ }
27
+
28
+ const hide = () => {
29
+ try{
30
+ root?.unmount()
31
+ setRoot(null)
32
+ }catch(e){}
33
+ }
34
+
35
+ const Menu = (e: MouseEvent, items : Array<any>) => {
36
+
37
+ const p = getMousePosition(e);
38
+ const { x, y } = checkBoundaries(p.x, p.y);
39
+
40
+ return (
41
+ <Box
42
+ bref={nodeRef}
43
+ flex dir={`cols`}
44
+ fixed
45
+ top={y}
46
+ left={x}
47
+ as={`zuz-contextmenu ${ID}`}>
48
+ {items && items.map((m, i) => m.id == `line` ? <Box as={`line`} key={`line-${i}-${m.id}`} /> : <button
49
+ key={`cm-${i}-${m.id}`}
50
+ onClick={ev => {
51
+ if(m.onClick){
52
+ m.onClick(ev, m)
53
+ }else{
54
+ console.log(`No onClick eventFound`)
55
+ }
56
+ hide()
57
+ }}>{m.label}</button>)}
58
+ </Box>
59
+ )
60
+ }
61
+
62
+ const show = (e : MouseEvent, items : Array<any>) => {
63
+ e.preventDefault(); e.stopPropagation();
64
+ if(!window.document.querySelector(`#context-${contextID}`)){
65
+ let div = el(`div`)
66
+ div.id = ID
67
+ window.document.body.appendChild(div)
68
+ }
69
+ root.render(Menu(e, items))
70
+ }
71
+
72
+ useEffect(() => {
73
+ if(!window.document.querySelector(`#context-${contextID}`)){
74
+ let div = el(`div`)
75
+ div.id = ID
76
+ window.document.body.appendChild(div)
77
+ }
78
+ if(!root) setRoot(ReactDOM.createRoot(document.getElementById(ID)))
79
+ }, [root])
80
+
81
+ return {
82
+ show,
83
+ hide
84
+ }
85
+
86
+ }
87
+
88
+ export default useContextMenu
@@ -103,4 +103,31 @@
103
103
  left: 20px;
104
104
  }
105
105
  }
106
+ }
107
+
108
+ .zuz-contextmenu{
109
+ min-width: 220px;
110
+ border-radius: 5px;
111
+ padding: 4px;
112
+ background: rgba(34,34,34,0.5);
113
+ border: 1px rgba(255,255,255,0.25) solid;
114
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, .45);
115
+ backdrop-filter: blur(20px);
116
+ button{
117
+ border: 0px;
118
+ text-align: left;
119
+ padding: 4px 6px;
120
+ background: rgba(0, 0, 0, 0);
121
+ cursor: pointer;
122
+ color: #fff;
123
+ border-radius: 4px;
124
+ &:hover{
125
+ background: #385fd2;
126
+ }
127
+ }
128
+ .line{
129
+ height: 1px;
130
+ background: rgba(255,255,255,0.25);
131
+ margin: 4px 6px;
132
+ }
106
133
  }