@zuzjs/ui 0.3.0 → 0.3.2

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.
@@ -1,13 +1,16 @@
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';
10
- import { buildCSS } from '../core'
13
+ import { cleanProps, buildCSS } from '../core'
11
14
  import { STORE_FORM_KEY } from '../context/AppProvider'
12
15
  import useDispatch from '../hooks/useDispatch'
13
16
  import useStore from '../hooks/useStore'
@@ -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 = !!(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,
@@ -34,7 +134,11 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
34
134
  onSubmit,
35
135
  value,
36
136
  defaultValue,
37
- fref
137
+ fref,
138
+ autoComplete,
139
+ elastic,
140
+ minRows,
141
+ maxRows
38
142
  } = props;
39
143
 
40
144
  const dispatch = useDispatch(STORE_FORM_KEY)
@@ -46,11 +150,36 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
46
150
 
47
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);`;
48
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
+
49
176
  return (
50
177
  <ClassNames>
51
178
  {({ css, cx }) => <El
179
+ {...cleanProps(props)}
52
180
  type={type || `text`}
53
181
  placeholder={placeholder || undefined}
182
+ autoComplete={autoComplete || undefined}
54
183
  name={name || nanoid()}
55
184
  multiple={type == 'file' ? multiple : undefined}
56
185
  accept={accept || `*`}
@@ -60,27 +189,36 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
60
189
  defaultValue={defaultValue || ``}
61
190
  onKeyUp={(e : SyntheticEvent) => {
62
191
  let k = e['keyCode'] || ['which'];
63
- if(El != 'textarea' && k == 13 && form && onSubmit){
192
+ if(form && onSubmit && El != 'textarea' && k == 13){
64
193
  onSubmit(forms[form]);
194
+ }else{
195
+ if(onKeyUp) onKeyUp(e)
65
196
  }
197
+
66
198
  }}
67
199
  onChange={e => {
68
200
  let val = type == 'file' ?
69
201
  e.currentTarget.files
70
202
  : e.currentTarget.value;
71
- 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()
72
205
  onChange && onChange(val == "" ? null : val)
73
206
  }}
74
207
  onClick={onClick ? onClick : () => {}}
75
208
  readOnly={readOnly || false}
76
209
  onBlur={e => {
210
+ if(onBlur) onBlur(e)
77
211
  if(touched){}
78
212
  }}
79
- 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
+ }}
80
217
  />}
81
218
  </ClassNames>
82
219
  )
83
-
220
+
221
+
84
222
  })
85
223
 
86
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;
@@ -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`,`tag`]
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 => {
@@ -216,7 +228,7 @@ const filterHTMLProps = (props : any) => {
216
228
  return filter;
217
229
  }
218
230
 
219
- const uuid = () => nanoid()
231
+ const uuid = (len=21) => nanoid(len)
220
232
 
221
233
  const addScript = (src: string, callback: () => any) => {
222
234
  var s = document.createElement('script')
@@ -278,10 +290,112 @@ 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
+
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
+
281
394
  export {
282
395
  addProps,
283
396
  addScript,
284
397
  buildCSS,
398
+ cleanProps,
285
399
  buildFormData,
286
400
  byName,
287
401
  byId,
@@ -306,6 +420,12 @@ export {
306
420
  generateModalRoutes,
307
421
  generatePreservedRoutes,
308
422
  generateRegularRoutes,
309
- getHostname
423
+ getHostname,
424
+ parseFilename,
425
+ camelCase,
426
+ getMousePosition,
427
+ formatSize,
428
+ copyToClipboard,
429
+ slugify
310
430
  }
311
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[] = [
@@ -5,4 +5,6 @@ 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'
10
+ export { default as useRender } from './useRender'
@@ -0,0 +1,123 @@
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
@@ -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
@@ -25,7 +25,7 @@ export { default as Spacer } from './comps/spacer'
25
25
  export { default as Spinner } from './comps/spinner'
26
26
  export { default as Text } from './comps/text'
27
27
  export { default as Tweet } from './comps/tweet'
28
- export { default as ContextMenu } from './comps/contextmenu'
28
+ // export { default as ContextMenu } from './comps/contextmenu'
29
29
 
30
30
  export { default as Header } from './kit/Header'
31
31
 
@@ -103,4 +103,31 @@
103
103
  left: 20px;
104
104
  }
105
105
  }
106
+ }
107
+
108
+ .zuz-contextmenu{
109
+ 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: #5183ff;
126
+ }
127
+ }
128
+ .line{
129
+ height: 1px;
130
+ background: rgba(255,255,255,0.25);
131
+ margin: 4px 6px;
132
+ }
106
133
  }