@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 +111 -12
- package/dist/styles.css +27 -0
- package/package.json +1 -1
- package/src/comps/box.tsx +5 -1
- package/src/comps/button.tsx +2 -1
- package/src/comps/contextmenu.tsx +1 -3
- package/src/comps/input.tsx +5 -2
- package/src/context/store/theme.tsx +1 -1
- package/src/core/index.ts +47 -1
- package/src/hooks/index.tsx +2 -1
- package/src/hooks/useContextMenu.tsx +88 -0
- package/src/scss/style.scss +27 -0
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}`,
|
|
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
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 || {})}}`)}`}
|
package/src/comps/button.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import React, {
|
|
|
5
5
|
import { ClassNames } from '@emotion/react'
|
|
6
6
|
import {
|
|
7
7
|
buildCSS,
|
|
8
|
-
|
|
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
|
>
|
package/src/comps/input.tsx
CHANGED
|
@@ -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
|
|
package/src/hooks/index.tsx
CHANGED
|
@@ -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
|
package/src/scss/style.scss
CHANGED
|
@@ -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
|
}
|