@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.
@@ -3,84 +3,119 @@ import ReactDOM from 'react-dom/client'
3
3
  import Box from '../comps/box'
4
4
  import { getMousePosition } from "../core"
5
5
 
6
- const useContextMenu = (contextID : string) => {
6
+ const Menu = ({ ID, hide, e, items, width }) => {
7
7
 
8
- const ID = `context-${contextID}`
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}`
9
59
  const [visible, setVisible] = useState(false)
10
60
  const [root, setRoot] = useState(null)
11
- const nodeRef = useRef<HTMLDivElement>(null);
12
61
 
13
62
  const el = (e : string) => window.document.createElement(e)
14
63
 
15
- function checkBoundaries(x: number, y: number) {
16
- if (nodeRef.current) {
17
- const { innerWidth, innerHeight } = window;
18
- const { offsetWidth, offsetHeight } = nodeRef.current;
19
64
 
20
- if (x + offsetWidth > innerWidth) x -= x + offsetWidth - innerWidth;
21
-
22
- if (y + offsetHeight > innerHeight) y -= y + offsetHeight - innerHeight;
65
+ const createRoot = () => {
66
+ if(!window.document.querySelector(`#${ID}`)){
67
+ let div = el(`div`)
68
+ div.id = ID
69
+ window.document.body.appendChild(div)
23
70
  }
24
-
25
- return { x, y };
26
71
  }
27
72
 
28
- const hide = () => {
73
+ const hideAll = () => {
74
+ if(window[contextToken]){
75
+ window[contextToken].map((h : Object) => h['ID'] != ID && h['fnc']())
76
+ }
77
+ }
78
+
79
+ const _hide = () => {
29
80
  try{
30
81
  root?.unmount()
82
+ document.querySelector(`#${ID}`).parentNode.removeChild(document.querySelector(`#${ID}`))
31
83
  setRoot(null)
32
84
  }catch(e){}
33
85
  }
34
86
 
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
- )
87
+ const hide = () => {
88
+ _hide()
89
+ hideAll()
60
90
  }
61
91
 
62
92
  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))
93
+ e.preventDefault(); e.stopPropagation();
94
+ hideAll()
95
+ root.render(<Menu e={e} width={contextWidth || 220} items={items} ID={ID} hide={hide} />)
70
96
  }
71
97
 
72
98
  useEffect(() => {
73
- if(!window.document.querySelector(`#context-${contextID}`)){
74
- let div = el(`div`)
75
- div.id = ID
76
- window.document.body.appendChild(div)
77
- }
99
+ createRoot()
78
100
  if(!root) setRoot(ReactDOM.createRoot(document.getElementById(ID)))
79
101
  }, [root])
80
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
+
81
115
  return {
82
116
  show,
83
- hide
117
+ hide,
118
+ hideAll
84
119
  }
85
120
 
86
121
  }
@@ -30,6 +30,8 @@ type Return<C extends Config, D> = D extends undefined
30
30
  ? Breakpoint<C>
31
31
  : never
32
32
 
33
+ const isBrowser = typeof window !== 'undefined'
34
+
33
35
  const useDevice = <C extends Config, D extends keyof C | undefined>(
34
36
  config: C,
35
37
  defaultBreakpoint?: D,
@@ -89,7 +91,7 @@ const useDevice = <C extends Config, D extends keyof C | undefined>(
89
91
  typeof window !== 'undefined' &&
90
92
  !(defaultBreakpoint && hydrateInitial)
91
93
  ) {
92
- const mediaQuery = window.matchMedia(query)
94
+ const mediaQuery = window?.matchMedia(query)
93
95
  if (mediaQuery.matches) {
94
96
  return breakpoint as Breakpoint<C>
95
97
  }
@@ -117,35 +119,37 @@ const useDevice = <C extends Config, D extends keyof C | undefined>(
117
119
 
118
120
  /** On changes to mediaQueries, subscribe to changes using window.matchMedia */
119
121
  useChooseEffect(() => {
120
- const unsubscribers = mediaQueries.map(({ query, ...breakpoint }) => {
121
- const list = window.matchMedia(query)
122
- updateBreakpoint(list, breakpoint as Breakpoint<C>)
123
-
124
- const handleChange = (event: MediaQueryListEvent) => {
125
- updateBreakpoint(event, breakpoint as Breakpoint<C>)
126
- }
122
+ if (isBrowser) {
123
+ const unsubscribers = mediaQueries.map(({ query, ...breakpoint }) => {
124
+ const list = window.matchMedia(query)
125
+ updateBreakpoint(list, breakpoint as Breakpoint<C>)
127
126
 
128
- const supportsNewEventListeners =
129
- 'addEventListener' in list && 'removeEventListener' in list
127
+ const handleChange = (event: MediaQueryListEvent) => {
128
+ updateBreakpoint(event, breakpoint as Breakpoint<C>)
129
+ }
130
130
 
131
- if (supportsNewEventListeners) {
132
- list.addEventListener('change', handleChange)
133
- } else {
134
- ;(list as MediaQueryList).addListener(handleChange)
135
- }
131
+ const supportsNewEventListeners =
132
+ 'addEventListener' in list && 'removeEventListener' in list
136
133
 
137
- /** Map the unsubscribers array to a list of unsubscriber methods */
138
- return () => {
139
134
  if (supportsNewEventListeners) {
140
- list.removeEventListener('change', handleChange)
135
+ list.addEventListener('change', handleChange)
141
136
  } else {
142
- ;(list as MediaQueryList).removeListener(handleChange)
137
+ ;(list as MediaQueryList).addListener(handleChange)
143
138
  }
144
- }
145
- })
146
139
 
147
- /** Return a function that when called, will call all unsubscribers */
148
- return () => unsubscribers.forEach((unsubscriber) => unsubscriber())
140
+ /** Map the unsubscribers array to a list of unsubscriber methods */
141
+ return () => {
142
+ if (supportsNewEventListeners) {
143
+ list.removeEventListener('change', handleChange)
144
+ } else {
145
+ ;(list as MediaQueryList).removeListener(handleChange)
146
+ }
147
+ }
148
+ })
149
+
150
+ /** Return a function that when called, will call all unsubscribers */
151
+ return () => unsubscribers.forEach((unsubscriber) => unsubscriber())
152
+ }
149
153
  }, [mediaQueries, updateBreakpoint])
150
154
 
151
155
  /** Print a nice debug value for React Devtools */
@@ -0,0 +1,27 @@
1
+ import { useEffect, useMemo } from "react"
2
+ import { uuid } from "../core"
3
+
4
+ const useMediaPlayer = (
5
+ playerID? : string
6
+ ) => {
7
+
8
+ const pid = useMemo(() => playerID || uuid(), [playerID])
9
+ const ID = `media-player-${pid}`
10
+
11
+ const el = (e : string) => window.document.createElement(e)
12
+
13
+ const createRoot = () => {
14
+ if(!window.document.querySelector(`#${ID}`)){
15
+ let div = el(`div`)
16
+ div.id = ID
17
+ window.document.body.appendChild(div)
18
+ }
19
+ }
20
+
21
+ useEffect(() => {
22
+
23
+ }, [])
24
+
25
+ }
26
+
27
+ export default useMediaPlayer
@@ -0,0 +1,6 @@
1
+ const useNavigator = () => {
2
+
3
+
4
+
5
+ return []
6
+ }
@@ -0,0 +1,29 @@
1
+ import { useState, useEffect } from "react"
2
+
3
+ const useRender = (isMounted: boolean, delay: number) => {
4
+
5
+ const [canRender, setCanRender] = useState(false)
6
+
7
+ // console.log(
8
+ // `isMounted:`, isMounted,
9
+ // `canRender:`, canRender,
10
+ // )
11
+
12
+ useEffect(() => {
13
+ let outID : any
14
+ if(isMounted && !canRender){
15
+ setCanRender(true)
16
+ }else if(isMounted && canRender){
17
+ outID = setTimeout(
18
+ () => setCanRender(false),
19
+ delay * 1000
20
+ )
21
+ }
22
+ return () => clearTimeout(outID);
23
+ }, [isMounted, delay])
24
+
25
+ return canRender;
26
+
27
+ }
28
+
29
+ export default useRender
package/src/index.tsx CHANGED
@@ -4,8 +4,6 @@ import { Provider, createSlice } from './context'
4
4
  export * from './core/index';
5
5
  export * from './hooks/index';
6
6
 
7
- export { Link } from "react-router-dom"
8
-
9
7
  export { default as App } from './comps/app'
10
8
  export { default as Box } from './comps/box'
11
9
  export { default as Button } from './comps/button'
@@ -25,7 +23,7 @@ export { default as Spacer } from './comps/spacer'
25
23
  export { default as Spinner } from './comps/spinner'
26
24
  export { default as Text } from './comps/text'
27
25
  export { default as Tweet } from './comps/tweet'
28
- export { default as ContextMenu } from './comps/contextmenu'
26
+ // export { default as ContextMenu } from './comps/contextmenu'
29
27
 
30
28
  export { default as Header } from './kit/Header'
31
29
 
@@ -106,7 +106,7 @@
106
106
  }
107
107
 
108
108
  .zuz-contextmenu{
109
- min-width: 220px;
109
+ width: 220px;
110
110
  border-radius: 5px;
111
111
  padding: 4px;
112
112
  background: rgba(34,34,34,0.5);
@@ -122,7 +122,7 @@
122
122
  color: #fff;
123
123
  border-radius: 4px;
124
124
  &:hover{
125
- background: #385fd2;
125
+ background: #5183ff;
126
126
  }
127
127
  }
128
128
  .line{
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2017",
4
+ "jsx": "react-jsx",
5
+ "allowJs": true,
6
+ "esModuleInterop": true,
7
+ "allowSyntheticDefaultImports": true,
8
+ "moduleResolution":"node",
9
+ "module": "ESNext",
10
+ },
11
+ "files": [],
12
+ "include": [],
13
+ "references": [
14
+ {
15
+ "path": "./tsconfig.lib.json"
16
+ },
17
+ {
18
+ "path": "./tsconfig.spec.json"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "types": ["node","vite/client"]
6
+ },
7
+ "exclude": ["**/*.spec.ts", "**/*.spec.tsx", "node_modules"],
8
+ "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
9
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "module": "commonjs",
6
+ "types": [
7
+ "jest",
8
+ "node"
9
+ ]
10
+ },
11
+ "include": [
12
+ "**/*.spec.ts",
13
+ "**/*.spec.tsx",
14
+ "**/*.spec.js",
15
+ "**/*.spec.jsx",
16
+ "**/*.d.ts"
17
+ ],
18
+ "exclude": [
19
+ "node_modules"
20
+ ]
21
+ }
@@ -1,90 +0,0 @@
1
- class AppTheme {
2
-
3
- #mode;
4
- #listen;
5
- #lightTheme;
6
- #darkTheme;
7
-
8
- constructor(_conf){
9
- const conf = _conf || {}
10
- this.#listen = "listen" in conf ? conf.listen : (mod) => { console.log(`Theme switched to ${mod}`); };
11
- this.#mode = conf.mode || "auto";
12
- this.#lightTheme = {
13
- //CORE
14
- tag: "light",
15
- dark: false,
16
- color: "#3a43ac",
17
- primary: '#edeef5',
18
- secondary: '#e8e9f0',
19
- textColor: '#111111',
20
- borderColor: '#dcdce1',
21
- //APP
22
- app: {
23
- action: 'rgba(0,0,0,0.1)',
24
- actionHover: 'rgba(255,255,255,0.1)',
25
- actionActive: 'rgba(0,0,0,0.8)',
26
- actionDisabled: 'rgba(255,255,255,0.01)',
27
- shimmerBG: '#383a48',
28
- shimmerFrom: '#383a48',
29
- shimmerTo: '#4a4c5b',
30
- iconBG: '#4a4c5b',
31
- icon: '#111111',
32
- sidebarHover: 'rgba(255,255,255,0.2)',
33
- sidebarActive: 'rgba(255,255,255,1)',
34
- toolbar: '#d5d6dc',
35
- tool: 'transparent',
36
- toolActive: 'rgba(255,255,255,0.75)',
37
- toolHover: 'rgba(255,255,255,0.5)',
38
- }
39
- };
40
- this.#darkTheme = {
41
- //CORE
42
- tag: "dark",
43
- dark: true,
44
- color: "#3a43ac",
45
- primary: '#21222d',
46
- secondary: '#282a37',
47
- textColor: '#f9f9f9',
48
- borderColor: '#353640',
49
- //APP
50
- app: {
51
- action: 'rgba(255,255,255,0.05)',
52
- actionHover: 'rgba(255,255,255,0.1)',
53
- actionActive: 'rgba(255,255,255,0.2)',
54
- actionDisabled: 'rgba(255,255,255,0.01)',
55
- shimmerBG: '#383a48',
56
- shimmerFrom: '#383a48',
57
- shimmerTo: '#4a4c5b',
58
- iconBG: '#4a4c5b',
59
- icon: '#ffffff',
60
- toolbar: '#21222d',
61
- tool: 'transparent',
62
- toolHover: '#2f3144',
63
- toolActive: '#2f3144',
64
- }
65
- };
66
- }
67
-
68
- get = () => {
69
- let self = this;
70
- if(self.#mode === "auto"){
71
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
72
- self.#mode = event.matches ? "dark" : "light";
73
- self.#listen(self.#mode);
74
- });
75
- return window.matchMedia &&
76
- window.matchMedia('(prefers-color-scheme: dark)').matches ?
77
- self.#darkTheme : self.#lightTheme;
78
- }else{
79
- if(self.#mode === "light"){
80
- return self.#lightTheme;
81
- }else if(self.#mode === "dark"){
82
- return self.#darkTheme;
83
- }
84
- }
85
- }
86
-
87
-
88
- }
89
-
90
- export default AppTheme
@@ -1,26 +0,0 @@
1
- import { createSlice } from '@reduxjs/toolkit'
2
-
3
- export const appSlice = createSlice({
4
- name: `app`,
5
- initialState: {
6
- debug: true,
7
- loading: true,
8
- theme: null,
9
- _tmp: Math.random()
10
- },
11
- reducers: {
12
- setLoading: (state, action) => {
13
- state.loading = action.payload || false;
14
- },
15
- setTheme: (state, action) => { state.theme = action.payload },
16
- forceUpdate: (state, action) => { state._tmp = Math.random() }
17
- }
18
- });
19
-
20
- export const {
21
- setLoading,
22
- setTheme,
23
- forceUpdate
24
- } = appSlice.actions;
25
-
26
- export default appSlice.reducer;
@@ -1,46 +0,0 @@
1
- import { createSlice } from '@reduxjs/toolkit'
2
-
3
- export const formSlice = createSlice({
4
- name: `form`,
5
- initialState: {
6
- forms: {}
7
- },
8
- reducers: {
9
- addForm: (state, action) => {
10
- state.forms[action.payload] = { touched: false, loading: false }
11
- },
12
- updateForm: (state, action) => {
13
- // console.log(action.payload)
14
- const { name, k, v } = action.payload;
15
- if(state.forms[name]) state.forms[name][k] = v;
16
- },
17
- addFields: (state, action) => {
18
- const { form, fields } = action.payload;
19
- if(state.forms[form]) state.forms[form] = { ...state.forms[form], ...fields };
20
- },
21
- addField: (state, action) => {
22
- // console.log('addField', action.payload)
23
- // state.forms[action.payload.form][action.payload.name] = action.payload.value
24
- // if(
25
- // state.forms[action.payload.form] &&
26
- // !state.forms[action.payload.form][action.payload.name]
27
- // ){
28
- // state.forms[action.payload.form][action.payload.name] = action.payload.value
29
- // }
30
- },
31
- updateField: (state, action) => {
32
- const { form, name, value } = action.payload;
33
- // console.log(`updatingField: ${form} = ${name} => ${value}`, state.forms[form])
34
- state.forms[form][name] = value;
35
- }
36
- }
37
- });
38
-
39
- export const {
40
- addForm,
41
- updateForm,
42
- addField,
43
- addFields,
44
- updateField
45
- } = formSlice.actions;
46
- export default formSlice.reducer;
@@ -1,33 +0,0 @@
1
- import {
2
- configureStore,
3
- combineReducers
4
- } from '@reduxjs/toolkit'
5
- import appReducer from './slices/app'
6
- import formReducer from './slices/form'
7
-
8
- const staticReducers = {
9
- app: appReducer,
10
- form: formReducer
11
- }
12
-
13
- const appstore = configureStore({
14
- reducer: staticReducers,
15
- middleware: getDefaultMiddleware => getDefaultMiddleware({
16
- serializableCheck: false
17
- })
18
- })
19
-
20
- appstore.asyncReducers = {}
21
- appstore.injectReducer = (k, asyncReducer) => {
22
- appstore.asyncReducers[k] = asyncReducer;
23
- appstore.replaceReducer(createReducer(appstore.asyncReducers))
24
- }
25
-
26
- const createReducer = asyncReducers => {
27
- return combineReducers({
28
- ...staticReducers,
29
- ...asyncReducers
30
- })
31
- }
32
-
33
- export default appstore;