@zuzjs/ui 0.2.7 → 0.3.0
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 +159 -70
- package/dist/styles.css +44 -0
- package/package.json +1 -1
- package/src/comps/image.tsx +15 -7
- package/src/comps/input.tsx +4 -0
- package/src/comps/toaster.tsx +9 -7
- package/src/context/AppProvider.tsx +8 -3
- package/src/context/store/lang.tsx +26 -0
- package/src/context/store/theme.tsx +14 -13
- package/src/core/defaultTheme.ts +14 -13
- package/src/core/index.ts +28 -2
- package/src/hooks/index.tsx +1 -1
- package/src/hooks/useImage.tsx +75 -49
- package/src/hooks/useLang.tsx +9 -0
- package/src/index.tsx +2 -0
- package/src/scss/props.scss +10 -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
|
+
export { Link } from 'react-router-dom';
|
|
9
10
|
import styled from '@emotion/styled';
|
|
10
11
|
|
|
11
12
|
const AppContext = createContext({});
|
|
@@ -59,20 +60,22 @@ class AppTheme {
|
|
|
59
60
|
_AppTheme_darkTheme.set(this, void 0);
|
|
60
61
|
this.get = () => {
|
|
61
62
|
let self = this;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// }else
|
|
71
|
-
if (__classPrivateFieldGet(self, _AppTheme_mode, "f") === "light") {
|
|
72
|
-
return __classPrivateFieldGet(self, _AppTheme_lightTheme, "f");
|
|
63
|
+
if (__classPrivateFieldGet(self, _AppTheme_mode, "f") === "auto") {
|
|
64
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
65
|
+
__classPrivateFieldSet(self, _AppTheme_mode, event.matches ? "dark" : "light", "f");
|
|
66
|
+
__classPrivateFieldGet(self, _AppTheme_listen, "f").call(self, __classPrivateFieldGet(self, _AppTheme_mode, "f"));
|
|
67
|
+
});
|
|
68
|
+
return window.matchMedia &&
|
|
69
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ?
|
|
70
|
+
__classPrivateFieldGet(self, _AppTheme_darkTheme, "f") : __classPrivateFieldGet(self, _AppTheme_lightTheme, "f");
|
|
73
71
|
}
|
|
74
|
-
else
|
|
75
|
-
|
|
72
|
+
else {
|
|
73
|
+
if (__classPrivateFieldGet(self, _AppTheme_mode, "f") === "light") {
|
|
74
|
+
return __classPrivateFieldGet(self, _AppTheme_lightTheme, "f");
|
|
75
|
+
}
|
|
76
|
+
else if (__classPrivateFieldGet(self, _AppTheme_mode, "f") === "dark") {
|
|
77
|
+
return __classPrivateFieldGet(self, _AppTheme_darkTheme, "f");
|
|
78
|
+
}
|
|
76
79
|
}
|
|
77
80
|
};
|
|
78
81
|
const conf = _conf || {};
|
|
@@ -88,6 +91,26 @@ class AppTheme {
|
|
|
88
91
|
}
|
|
89
92
|
_AppTheme_mode = new WeakMap(), _AppTheme_listen = new WeakMap(), _AppTheme_lightTheme = new WeakMap(), _AppTheme_darkTheme = new WeakMap();
|
|
90
93
|
|
|
94
|
+
var _AppLang_mode, _AppLang_listen, _AppLang_lang;
|
|
95
|
+
class AppLang {
|
|
96
|
+
constructor(_conf) {
|
|
97
|
+
_AppLang_mode.set(this, void 0);
|
|
98
|
+
_AppLang_listen.set(this, void 0);
|
|
99
|
+
_AppLang_lang.set(this, void 0);
|
|
100
|
+
this.get = () => {
|
|
101
|
+
let self = this;
|
|
102
|
+
return __classPrivateFieldGet(self, _AppLang_lang, "f");
|
|
103
|
+
};
|
|
104
|
+
const conf = _conf || {};
|
|
105
|
+
__classPrivateFieldSet(this, _AppLang_listen, "listen" in conf ? conf.listen : (mod) => { console.log(`Lang switched to ${mod}`); }, "f");
|
|
106
|
+
__classPrivateFieldSet(this, _AppLang_mode, conf.mode || "en", "f");
|
|
107
|
+
__classPrivateFieldSet(this, _AppLang_lang, Object.assign({
|
|
108
|
+
//CORE
|
|
109
|
+
tag: "en" }, ("lang" in conf ? Object.assign({}, conf.lang) : {})), "f");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
_AppLang_mode = new WeakMap(), _AppLang_listen = new WeakMap(), _AppLang_lang = new WeakMap();
|
|
113
|
+
|
|
91
114
|
let isMounted = true;
|
|
92
115
|
const STORE_KEY = `__zuzjs`;
|
|
93
116
|
const STORE_FORM_KEY = `${STORE_KEY}forms`;
|
|
@@ -102,14 +125,14 @@ const defaultState = {
|
|
|
102
125
|
}
|
|
103
126
|
};
|
|
104
127
|
const rootReducer = (state, action) => (Object.assign(Object.assign({}, state), action.payload));
|
|
105
|
-
const AppProvider = ({ children, initialState = {}, theme = {} }) => {
|
|
128
|
+
const AppProvider = ({ children, initialState = {}, theme = {}, lang = {} }) => {
|
|
106
129
|
useEffect(() => {
|
|
107
130
|
isMounted = true;
|
|
108
131
|
return () => {
|
|
109
132
|
isMounted = false;
|
|
110
133
|
};
|
|
111
134
|
}, []);
|
|
112
|
-
const rootState = useMemo(() => (Object.assign(Object.assign(Object.assign({}, defaultState), initialState), { theme: new AppTheme({ theme }).get() })), [initialState]);
|
|
135
|
+
const rootState = useMemo(() => (Object.assign(Object.assign(Object.assign({}, defaultState), initialState), { theme: new AppTheme({ theme }).get(), lang: new AppLang({ lang }).get() })), [initialState]);
|
|
113
136
|
const [state, _dispatch] = useReducer(rootReducer, rootState);
|
|
114
137
|
const dispatch = useCallback((args) => {
|
|
115
138
|
if (isMounted) {
|
|
@@ -135,12 +158,14 @@ const AppProvider = ({ children, initialState = {}, theme = {} }) => {
|
|
|
135
158
|
};
|
|
136
159
|
AppProvider.defaultProps = {
|
|
137
160
|
theme: {},
|
|
161
|
+
lang: {},
|
|
138
162
|
initialState: {},
|
|
139
163
|
};
|
|
140
164
|
AppProvider.propTypes = {
|
|
141
165
|
children: PropTypes.node.isRequired,
|
|
142
166
|
initialState: PropTypes.instanceOf(Object),
|
|
143
|
-
theme: PropTypes.instanceOf(Object)
|
|
167
|
+
theme: PropTypes.instanceOf(Object),
|
|
168
|
+
lang: PropTypes.instanceOf(Object)
|
|
144
169
|
};
|
|
145
170
|
|
|
146
171
|
const createSlice = ({ name, initialState, reducers }) => {
|
|
@@ -556,14 +581,14 @@ const UPDATE_FORM_FIELD = (formName, field, value, forms) => {
|
|
|
556
581
|
};
|
|
557
582
|
|
|
558
583
|
const Input = forwardRef((props, ref) => {
|
|
559
|
-
const { as, accept, multiple, onChange, onKeyUp, readOnly, type, tag, placeholder, name, form, touched, onSubmit, defaultValue, fref } = props;
|
|
584
|
+
const { as, accept, multiple, onChange, onKeyUp, onClick, readOnly, type, tag, placeholder, name, form, touched, onSubmit, value, defaultValue, fref } = props;
|
|
560
585
|
const dispatch = useDispatch(STORE_FORM_KEY);
|
|
561
586
|
const { forms } = useStore(state => state[STORE_FORM_KEY], false);
|
|
562
587
|
let Tag = tag || 'input';
|
|
563
588
|
const El = Tag;
|
|
564
589
|
const _ref = fref || useRef();
|
|
565
590
|
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);`;
|
|
566
|
-
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, defaultValue: defaultValue || ``, onKeyUp: (e) => {
|
|
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) => {
|
|
567
592
|
let k = e['keyCode'] || ['which'];
|
|
568
593
|
if (El != 'textarea' && k == 13 && form && onSubmit) {
|
|
569
594
|
onSubmit(forms[form]);
|
|
@@ -574,7 +599,7 @@ const Input = forwardRef((props, ref) => {
|
|
|
574
599
|
: e.currentTarget.value;
|
|
575
600
|
dispatch(dispatch(UPDATE_FORM_FIELD(form || 'orphan', name, val == "" ? null : val, forms)));
|
|
576
601
|
onChange && onChange(val == "" ? null : val);
|
|
577
|
-
}, readOnly: readOnly || false, onBlur: e => {
|
|
602
|
+
}, onClick: onClick ? onClick : () => { }, readOnly: readOnly || false, onBlur: e => {
|
|
578
603
|
}, onFocus: e => touched == false && dispatch(UPDATE_FORM_FIELD(form || 'orphan', `touched`, true, forms)) }) }));
|
|
579
604
|
});
|
|
580
605
|
|
|
@@ -601,45 +626,70 @@ const useTheme = (mod = 'auto') => {
|
|
|
601
626
|
return state['theme'] || {};
|
|
602
627
|
};
|
|
603
628
|
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
oldReferrerPolicy.current = referrerpolicy;
|
|
617
|
-
}
|
|
618
|
-
useLayoutEffect(function () {
|
|
619
|
-
if (!url)
|
|
620
|
-
return;
|
|
621
|
-
var img = document.createElement('img');
|
|
622
|
-
function onload() {
|
|
623
|
-
statusRef.current = 'loaded';
|
|
624
|
-
imageRef.current = img;
|
|
625
|
-
setStateToken(Math.random());
|
|
626
|
-
}
|
|
627
|
-
function onerror() {
|
|
628
|
-
statusRef.current = 'failed';
|
|
629
|
-
imageRef.current = undefined;
|
|
630
|
-
setStateToken(Math.random());
|
|
631
|
-
}
|
|
632
|
-
img.addEventListener('load', onload);
|
|
633
|
-
img.addEventListener('error', onerror);
|
|
634
|
-
crossOrigin && (img.crossOrigin = crossOrigin);
|
|
635
|
-
referrerpolicy && (img.referrerPolicy = referrerpolicy);
|
|
636
|
-
img.src = url;
|
|
637
|
-
return function cleanup() {
|
|
638
|
-
img.removeEventListener('load', onload);
|
|
639
|
-
img.removeEventListener('error', onerror);
|
|
629
|
+
const removeBlankArrayElements = (a) => a.filter((x) => x);
|
|
630
|
+
const stringToArray = (x) => (Array.isArray(x) ? x : [x]);
|
|
631
|
+
const cache = {};
|
|
632
|
+
// sequential map.find for promises
|
|
633
|
+
const promiseFind = (arr, promiseFactory) => {
|
|
634
|
+
let done = false;
|
|
635
|
+
return new Promise((resolve, reject) => {
|
|
636
|
+
const queueNext = (src) => {
|
|
637
|
+
return promiseFactory(src).then(() => {
|
|
638
|
+
done = true;
|
|
639
|
+
resolve(src);
|
|
640
|
+
});
|
|
640
641
|
};
|
|
641
|
-
|
|
642
|
-
|
|
642
|
+
arr
|
|
643
|
+
.reduce((p, src) => {
|
|
644
|
+
// ensure we aren't done before enqueuing the next source
|
|
645
|
+
return p.catch(() => {
|
|
646
|
+
if (!done)
|
|
647
|
+
return queueNext(src);
|
|
648
|
+
});
|
|
649
|
+
}, queueNext(arr.shift()))
|
|
650
|
+
.catch(reject);
|
|
651
|
+
});
|
|
652
|
+
};
|
|
653
|
+
const useImage = ({ srcList, imgPromise = imgPromiseFactory({ decode: true }), useSuspense = true, }) => {
|
|
654
|
+
const [, setIsSettled] = useState(false);
|
|
655
|
+
const sourceList = removeBlankArrayElements(stringToArray(srcList));
|
|
656
|
+
const sourceKey = sourceList.join('');
|
|
657
|
+
if (!cache[sourceKey]) {
|
|
658
|
+
// create promise to loop through sources and try to load one
|
|
659
|
+
cache[sourceKey] = {
|
|
660
|
+
promise: promiseFind(sourceList, imgPromise),
|
|
661
|
+
cache: 'pending',
|
|
662
|
+
error: null,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
// when promise resolves/reject, update cache & state
|
|
666
|
+
if (cache[sourceKey].cache === 'resolved') {
|
|
667
|
+
return { src: cache[sourceKey].src, isLoading: false, error: null };
|
|
668
|
+
}
|
|
669
|
+
if (cache[sourceKey].cache === 'rejected') {
|
|
670
|
+
if (useSuspense)
|
|
671
|
+
throw cache[sourceKey].error;
|
|
672
|
+
return { isLoading: false, error: cache[sourceKey].error, src: undefined };
|
|
673
|
+
}
|
|
674
|
+
cache[sourceKey].promise
|
|
675
|
+
// if a source was found, update cache
|
|
676
|
+
// when not using suspense, update state to force a rerender
|
|
677
|
+
.then((src) => {
|
|
678
|
+
cache[sourceKey] = Object.assign(Object.assign({}, cache[sourceKey]), { cache: 'resolved', src });
|
|
679
|
+
if (!useSuspense)
|
|
680
|
+
setIsSettled(sourceKey);
|
|
681
|
+
})
|
|
682
|
+
// if no source was found, or if another error occurred, update cache
|
|
683
|
+
// when not using suspense, update state to force a rerender
|
|
684
|
+
.catch((error) => {
|
|
685
|
+
cache[sourceKey] = Object.assign(Object.assign({}, cache[sourceKey]), { cache: 'rejected', error });
|
|
686
|
+
if (!useSuspense)
|
|
687
|
+
setIsSettled(sourceKey);
|
|
688
|
+
});
|
|
689
|
+
// cache[sourceKey].cache === 'pending')
|
|
690
|
+
if (useSuspense)
|
|
691
|
+
throw cache[sourceKey].promise;
|
|
692
|
+
return { isLoading: true, src: undefined, error: null };
|
|
643
693
|
};
|
|
644
694
|
|
|
645
695
|
/** External Dependencies */
|
|
@@ -799,13 +849,14 @@ const useDevice = (config, defaultBreakpoint, hydrateInitial = true) => {
|
|
|
799
849
|
return currentBreakpoint;
|
|
800
850
|
};
|
|
801
851
|
|
|
802
|
-
var _Toaster_container, _Toaster_startTop, _Toaster_tout, _Toaster_defaultTimeLeft;
|
|
852
|
+
var _Toaster_container, _Toaster_startTop, _Toaster_tout, _Toaster_defaultTimeLeft, _Toaster_root;
|
|
803
853
|
class Toaster {
|
|
804
854
|
constructor(config) {
|
|
805
855
|
_Toaster_container.set(this, void 0);
|
|
806
856
|
_Toaster_startTop.set(this, void 0);
|
|
807
857
|
_Toaster_tout.set(this, void 0);
|
|
808
858
|
_Toaster_defaultTimeLeft.set(this, void 0);
|
|
859
|
+
_Toaster_root.set(this, void 0);
|
|
809
860
|
this.show = (message, duration) => {
|
|
810
861
|
let self = this;
|
|
811
862
|
self.toast({
|
|
@@ -816,12 +867,13 @@ class Toaster {
|
|
|
816
867
|
__classPrivateFieldSet(this, _Toaster_startTop, 20, "f");
|
|
817
868
|
__classPrivateFieldSet(this, _Toaster_defaultTimeLeft, 4, "f");
|
|
818
869
|
const rootID = (config === null || config === void 0 ? void 0 : config.root) || `toast-container`;
|
|
819
|
-
|
|
870
|
+
__classPrivateFieldSet(this, _Toaster_root, rootID, "f");
|
|
871
|
+
if (window.document.querySelector(`#${rootID}`))
|
|
820
872
|
return;
|
|
821
|
-
var root = document.createElement('div');
|
|
873
|
+
var root = window.document.createElement('div');
|
|
822
874
|
root.id = rootID;
|
|
823
|
-
document.body.appendChild(root);
|
|
824
|
-
__classPrivateFieldSet(this, _Toaster_container, document.querySelector(`#${rootID}`), "f");
|
|
875
|
+
window.document.body.appendChild(root);
|
|
876
|
+
__classPrivateFieldSet(this, _Toaster_container, window.document.querySelector(`#${rootID}`), "f");
|
|
825
877
|
}
|
|
826
878
|
dismiss(ID) {
|
|
827
879
|
let self = this;
|
|
@@ -841,7 +893,7 @@ class Toaster {
|
|
|
841
893
|
}
|
|
842
894
|
toast(config) {
|
|
843
895
|
var self = this;
|
|
844
|
-
var ID = 'toast-' + nanoid(), toast = document.createElement('div'); document.createElement('div');
|
|
896
|
+
var ID = 'toast-' + nanoid(), toast = window.document.createElement('div'); window.document.createElement('div');
|
|
845
897
|
toast.id = ID;
|
|
846
898
|
toast.style.backgroundColor = `#111`;
|
|
847
899
|
toast.style.color = `#fff`;
|
|
@@ -856,7 +908,7 @@ class Toaster {
|
|
|
856
908
|
toast.classList.add('fixed');
|
|
857
909
|
toast.classList.add('f');
|
|
858
910
|
toast.innerHTML = config.message || `You haven't passed "message" in this toast`;
|
|
859
|
-
__classPrivateFieldGet(self, _Toaster_container, "f").appendChild(toast);
|
|
911
|
+
(__classPrivateFieldGet(self, _Toaster_container, "f") || window.document.querySelector(`#${__classPrivateFieldGet(self, _Toaster_root, "f")}`)).appendChild(toast);
|
|
860
912
|
self.arrangeToasts()
|
|
861
913
|
.then(() => {
|
|
862
914
|
setTimeout(() => {
|
|
@@ -883,13 +935,18 @@ class Toaster {
|
|
|
883
935
|
});
|
|
884
936
|
}
|
|
885
937
|
}
|
|
886
|
-
_Toaster_container = new WeakMap(), _Toaster_startTop = new WeakMap(), _Toaster_tout = new WeakMap(), _Toaster_defaultTimeLeft = new WeakMap();
|
|
938
|
+
_Toaster_container = new WeakMap(), _Toaster_startTop = new WeakMap(), _Toaster_tout = new WeakMap(), _Toaster_defaultTimeLeft = new WeakMap(), _Toaster_root = new WeakMap();
|
|
887
939
|
|
|
888
940
|
const useToast = () => {
|
|
889
941
|
const toaster = useMemo(() => new Toaster(), []);
|
|
890
942
|
return toaster;
|
|
891
943
|
};
|
|
892
944
|
|
|
945
|
+
const useLang = (mod = 'en') => {
|
|
946
|
+
const state = useContext(AppContext);
|
|
947
|
+
return state['lang'] || {};
|
|
948
|
+
};
|
|
949
|
+
|
|
893
950
|
const Button = forwardRef((props, ref) => {
|
|
894
951
|
const { forms } = useStore(state => state[STORE_FORM_KEY], false);
|
|
895
952
|
if (props.html) {
|
|
@@ -1037,7 +1094,7 @@ const randstr = function (len) {
|
|
|
1037
1094
|
}
|
|
1038
1095
|
return text;
|
|
1039
1096
|
};
|
|
1040
|
-
const setCookie = (key, value, expiry) => Cookies.set(key, value, { expires: expiry || 7 });
|
|
1097
|
+
const setCookie = (key, value, expiry, host) => Cookies.set(key, value, { expires: expiry || 7, domain: host || window.location.host });
|
|
1041
1098
|
const getCookie = (key) => Cookies.get(key) || null;
|
|
1042
1099
|
const removeCookie = (key) => Cookies.remove(key);
|
|
1043
1100
|
const buildFormData = (data) => {
|
|
@@ -1190,6 +1247,29 @@ const getUriParams = () => {
|
|
|
1190
1247
|
return xny;
|
|
1191
1248
|
};
|
|
1192
1249
|
const rgb2hex = rgb => `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(n => parseInt(n, 10).toString(16).padStart(2, '0')).join('')}`;
|
|
1250
|
+
const getHostname = url => {
|
|
1251
|
+
if (window.URL) {
|
|
1252
|
+
let u = new window.URL(url);
|
|
1253
|
+
return u.hostname;
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
var a = document.createElement(`a`);
|
|
1257
|
+
a.href = url;
|
|
1258
|
+
return a.hostname.replace("www.", "");
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
const imgPromiseFactory = ({ decode = true, crossOrigin = '' }) => (src) => {
|
|
1262
|
+
return new Promise((resolve, reject) => {
|
|
1263
|
+
const i = new Image();
|
|
1264
|
+
if (crossOrigin)
|
|
1265
|
+
i.crossOrigin = crossOrigin;
|
|
1266
|
+
i.onload = () => {
|
|
1267
|
+
decode && i.decode ? i.decode().then(resolve).catch(reject) : resolve();
|
|
1268
|
+
};
|
|
1269
|
+
i.onerror = reject;
|
|
1270
|
+
i.src = src;
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
1193
1273
|
|
|
1194
1274
|
const AppMain = forwardRef((props, ref) => {
|
|
1195
1275
|
// const { dispatch } = useStore()
|
|
@@ -1338,9 +1418,18 @@ const Icon = forwardRef((props, ref) => {
|
|
|
1338
1418
|
return (jsx(Box, Object.assign({ hover: hover || {}, flex: true, bref: ref, as: `icon-${as}`, ai: `c`, jc: `c`, size: size || 24, color: color || `#111111` }, { children: path && Array(path).fill(undefined).map((p, i) => jsx("span", { className: `path${i + 1}` }, `ico-${as}-${i}`)) })));
|
|
1339
1419
|
});
|
|
1340
1420
|
|
|
1341
|
-
const Image = forwardRef((props, ref) => {
|
|
1342
|
-
const
|
|
1343
|
-
return (jsx(ClassNames, { children: ({ css, cx }) => jsxs(
|
|
1421
|
+
const Image$1 = forwardRef((props, ref) => {
|
|
1422
|
+
const { src, isLoading, error } = useImage({ srcList: props.src, useSuspense: false });
|
|
1423
|
+
return (jsx(ClassNames, { children: ({ css, cx }) => jsxs(Fragment, { children: [isLoading && jsx(Box, { className: `${props.as ? `${props.as} ` : ``}${cx(css `background: #eee;${buildCSS(props)}`)}` }), !isLoading && jsx("img", { src: src, className: `${props.as ? `${props.as} ` : ``}${cx(css `${buildCSS(props)}`)}`, ref: ref })] }) })
|
|
1424
|
+
// <ClassNames>
|
|
1425
|
+
// {({ css, cx }) => <picture className={cx(css`${buildCSS({ flex: true })}`)}>
|
|
1426
|
+
// {status == 'loading' && <Box className={`${props.as ? `${props.as} ` : ``}${cx(css`background: #eee;${buildCSS(props)}`)}`} />}
|
|
1427
|
+
// {status == 'loaded' && <img src={props.src}
|
|
1428
|
+
// className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)}`)}`}
|
|
1429
|
+
// ref={ref} />}
|
|
1430
|
+
// </picture>}
|
|
1431
|
+
// </ClassNames>
|
|
1432
|
+
);
|
|
1344
1433
|
});
|
|
1345
1434
|
|
|
1346
1435
|
const DEFAULT_COLUMNS = 2;
|
|
@@ -1548,4 +1637,4 @@ const Header = forwardRef((props, ref) => {
|
|
|
1548
1637
|
return (jsx(Fragment, { children: buildComponent(data) }));
|
|
1549
1638
|
});
|
|
1550
1639
|
|
|
1551
|
-
export { App, Component as Block, Box, Button, Checkbox, ContextMenu, Cover, Form, Header, Heading, Icon, 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, getUriParams, grab, isEmail, isIPv4, isUrl, randstr, removeCookie, rgb2hex, setCSSVar, setCookie, shuffleArray, ucfirst, useDevice, useDispatch, useImage, useResizeObserver, useStore, useTheme, useToast, uuid };
|
|
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 };
|
package/dist/styles.css
CHANGED
|
@@ -312,6 +312,50 @@ button {
|
|
|
312
312
|
font-size: 72px;
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
/*
|
|
316
|
+
BoldSize
|
|
317
|
+
It will generate .b400 { font-weight: 400; } from '400'
|
|
318
|
+
*/
|
|
319
|
+
.bold {
|
|
320
|
+
font-weight: bold;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.b100 {
|
|
324
|
+
font-weight: 100;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.b200 {
|
|
328
|
+
font-weight: 200;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.b300 {
|
|
332
|
+
font-weight: 300;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.b400 {
|
|
336
|
+
font-weight: 400;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.b500 {
|
|
340
|
+
font-weight: 500;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.b600 {
|
|
344
|
+
font-weight: 600;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.b700 {
|
|
348
|
+
font-weight: 700;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.b800 {
|
|
352
|
+
font-weight: 800;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.b900 {
|
|
356
|
+
font-weight: 900;
|
|
357
|
+
}
|
|
358
|
+
|
|
315
359
|
@-webkit-keyframes rotating /* Safari and Chrome */ {
|
|
316
360
|
from {
|
|
317
361
|
-webkit-transform: rotate(0deg);
|
package/package.json
CHANGED
package/src/comps/image.tsx
CHANGED
|
@@ -9,17 +9,25 @@ import Box from './box'
|
|
|
9
9
|
|
|
10
10
|
const Image = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTMLImageElement>) => {
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const { src, isLoading, error } = useImage({ srcList: props.src, useSuspense: false });
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<ClassNames>
|
|
16
|
-
{({ css, cx }) =>
|
|
17
|
-
{
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
{({ css, cx }) => <>
|
|
17
|
+
{isLoading && <Box className={`${props.as ? `${props.as} ` : ``}${cx(css`background: #eee;${buildCSS(props)}`)}`} />}
|
|
18
|
+
{!isLoading && <img src={src}
|
|
19
|
+
className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)}`)}`}
|
|
20
|
+
ref={ref} />}
|
|
21
|
+
</>}
|
|
22
22
|
</ClassNames>
|
|
23
|
+
// <ClassNames>
|
|
24
|
+
// {({ css, cx }) => <picture className={cx(css`${buildCSS({ flex: true })}`)}>
|
|
25
|
+
// {status == 'loading' && <Box className={`${props.as ? `${props.as} ` : ``}${cx(css`background: #eee;${buildCSS(props)}`)}`} />}
|
|
26
|
+
// {status == 'loaded' && <img src={props.src}
|
|
27
|
+
// className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)}`)}`}
|
|
28
|
+
// ref={ref} />}
|
|
29
|
+
// </picture>}
|
|
30
|
+
// </ClassNames>
|
|
23
31
|
)
|
|
24
32
|
})
|
|
25
33
|
|
package/src/comps/input.tsx
CHANGED
|
@@ -23,6 +23,7 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
|
|
|
23
23
|
multiple,
|
|
24
24
|
onChange,
|
|
25
25
|
onKeyUp,
|
|
26
|
+
onClick,
|
|
26
27
|
readOnly,
|
|
27
28
|
type,
|
|
28
29
|
tag,
|
|
@@ -31,6 +32,7 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
|
|
|
31
32
|
form,
|
|
32
33
|
touched,
|
|
33
34
|
onSubmit,
|
|
35
|
+
value,
|
|
34
36
|
defaultValue,
|
|
35
37
|
fref
|
|
36
38
|
} = props;
|
|
@@ -54,6 +56,7 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
|
|
|
54
56
|
accept={accept || `*`}
|
|
55
57
|
className={`${as ? `${as} ` : ``}f ${cx(css`${_defaultCSS}${buildCSS(props)}&:hover {${buildCSS(props.hover || {})}} &:focus {${buildCSS(props.focus || {})}}`)}`}
|
|
56
58
|
ref={_ref}
|
|
59
|
+
value={value || undefined}
|
|
57
60
|
defaultValue={defaultValue || ``}
|
|
58
61
|
onKeyUp={(e : SyntheticEvent) => {
|
|
59
62
|
let k = e['keyCode'] || ['which'];
|
|
@@ -68,6 +71,7 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
|
|
|
68
71
|
dispatch( dispatch( UPDATE_FORM_FIELD( form || 'orphan', name, val == "" ? null : val, forms ) ) );
|
|
69
72
|
onChange && onChange(val == "" ? null : val)
|
|
70
73
|
}}
|
|
74
|
+
onClick={onClick ? onClick : () => {}}
|
|
71
75
|
readOnly={readOnly || false}
|
|
72
76
|
onBlur={e => {
|
|
73
77
|
if(touched){}
|
package/src/comps/toaster.tsx
CHANGED
|
@@ -6,6 +6,7 @@ class Toaster {
|
|
|
6
6
|
#startTop : number
|
|
7
7
|
#tout : null
|
|
8
8
|
#defaultTimeLeft : number
|
|
9
|
+
#root : string
|
|
9
10
|
|
|
10
11
|
constructor(config?: {
|
|
11
12
|
root : string,
|
|
@@ -14,12 +15,13 @@ class Toaster {
|
|
|
14
15
|
this.#startTop = 20
|
|
15
16
|
this.#defaultTimeLeft = 4
|
|
16
17
|
const rootID = config?.root || `toast-container`;
|
|
17
|
-
|
|
18
|
+
this.#root = rootID;
|
|
19
|
+
if(window.document.querySelector(`#${rootID}`)) return;
|
|
18
20
|
var self = this;
|
|
19
|
-
var root = document.createElement('div');
|
|
21
|
+
var root = window.document.createElement('div');
|
|
20
22
|
root.id = rootID;
|
|
21
|
-
document.body.appendChild(root);
|
|
22
|
-
this.#container = document.querySelector(`#${rootID}`)
|
|
23
|
+
window.document.body.appendChild(root);
|
|
24
|
+
this.#container = window.document.querySelector(`#${rootID}`)
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
show = (
|
|
@@ -57,8 +59,8 @@ class Toaster {
|
|
|
57
59
|
var self = this;
|
|
58
60
|
var tout = null,
|
|
59
61
|
ID = 'toast-' + nanoid(),
|
|
60
|
-
toast = document.createElement('div'),
|
|
61
|
-
toastBG = document.createElement('div');
|
|
62
|
+
toast = window.document.createElement('div'),
|
|
63
|
+
toastBG = window.document.createElement('div');
|
|
62
64
|
|
|
63
65
|
toast.id = ID;
|
|
64
66
|
toast.style.backgroundColor = `#111`
|
|
@@ -76,7 +78,7 @@ class Toaster {
|
|
|
76
78
|
|
|
77
79
|
toast.innerHTML = config.message || `You haven't passed "message" in this toast`;
|
|
78
80
|
|
|
79
|
-
self.#container.appendChild(toast);
|
|
81
|
+
(self.#container || window.document.querySelector(`#${self.#root}`)).appendChild(toast);
|
|
80
82
|
|
|
81
83
|
self.arrangeToasts()
|
|
82
84
|
.then(() => {
|
|
@@ -7,6 +7,7 @@ import React, {
|
|
|
7
7
|
import PropTypes from 'prop-types'
|
|
8
8
|
import AppContext from './AppContext'
|
|
9
9
|
import AppTheme from './store/theme'
|
|
10
|
+
import AppLang from './store/lang'
|
|
10
11
|
|
|
11
12
|
let isMounted = true;
|
|
12
13
|
export const STORE_KEY = `__zuzjs`
|
|
@@ -32,7 +33,8 @@ const rootReducer = (state, action ) => ({
|
|
|
32
33
|
const AppProvider = ({
|
|
33
34
|
children,
|
|
34
35
|
initialState = {},
|
|
35
|
-
theme = {}
|
|
36
|
+
theme = {},
|
|
37
|
+
lang = {}
|
|
36
38
|
}) => {
|
|
37
39
|
|
|
38
40
|
|
|
@@ -46,7 +48,8 @@ const AppProvider = ({
|
|
|
46
48
|
const rootState = useMemo(() => ({
|
|
47
49
|
...defaultState,
|
|
48
50
|
...initialState,
|
|
49
|
-
theme: new AppTheme({ theme }).get()
|
|
51
|
+
theme: new AppTheme({ theme }).get(),
|
|
52
|
+
lang: new AppLang({ lang }).get(),
|
|
50
53
|
}), [initialState])
|
|
51
54
|
|
|
52
55
|
const [state, _dispatch] = useReducer(rootReducer, rootState);
|
|
@@ -89,13 +92,15 @@ const AppProvider = ({
|
|
|
89
92
|
|
|
90
93
|
AppProvider.defaultProps = {
|
|
91
94
|
theme: {},
|
|
95
|
+
lang: {},
|
|
92
96
|
initialState : {},
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
AppProvider.propTypes = {
|
|
96
100
|
children: PropTypes.node.isRequired,
|
|
97
101
|
initialState: PropTypes.instanceOf(Object),
|
|
98
|
-
theme: PropTypes.instanceOf(Object)
|
|
102
|
+
theme: PropTypes.instanceOf(Object),
|
|
103
|
+
lang: PropTypes.instanceOf(Object)
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
export default AppProvider
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class AppLang {
|
|
2
|
+
|
|
3
|
+
#mode;
|
|
4
|
+
#listen;
|
|
5
|
+
#lang;
|
|
6
|
+
|
|
7
|
+
constructor(_conf){
|
|
8
|
+
const conf = _conf || {}
|
|
9
|
+
this.#listen = "listen" in conf ? conf.listen : (mod) => { console.log(`Lang switched to ${mod}`); };
|
|
10
|
+
this.#mode = conf.mode || "en";
|
|
11
|
+
this.#lang = {
|
|
12
|
+
//CORE
|
|
13
|
+
tag: "en",
|
|
14
|
+
...( "lang" in conf ? {...conf.lang} : {})
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get = () => {
|
|
19
|
+
let self = this;
|
|
20
|
+
return self.#lang
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default AppLang
|
|
@@ -31,19 +31,20 @@ class AppTheme {
|
|
|
31
31
|
|
|
32
32
|
get = () => {
|
|
33
33
|
let self = this;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
if(self.#mode === "auto"){
|
|
35
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
36
|
+
self.#mode = event.matches ? "dark" : "light";
|
|
37
|
+
self.#listen(self.#mode);
|
|
38
|
+
});
|
|
39
|
+
return window.matchMedia &&
|
|
40
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ?
|
|
41
|
+
self.#darkTheme : self.#lightTheme;
|
|
42
|
+
}else{
|
|
43
|
+
if(self.#mode === "light"){
|
|
44
|
+
return self.#lightTheme;
|
|
45
|
+
}else if(self.#mode === "dark"){
|
|
46
|
+
return self.#darkTheme;
|
|
47
|
+
}
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
package/src/core/defaultTheme.ts
CHANGED
|
@@ -67,19 +67,20 @@ class AppTheme {
|
|
|
67
67
|
|
|
68
68
|
get = () => {
|
|
69
69
|
let self = this;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
+
}
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
package/src/core/index.ts
CHANGED
|
@@ -78,7 +78,7 @@ const randstr = function(len? : number){
|
|
|
78
78
|
return text;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const setCookie = (key : string, value : any, expiry? : number) => Cookies.set(key, value, { expires: expiry || 7 })
|
|
81
|
+
const setCookie = (key : string, value : any, expiry? : number, host? : string) => Cookies.set(key, value, { expires: expiry || 7, domain: host || window.location.host })
|
|
82
82
|
|
|
83
83
|
const getCookie = (key : string) => Cookies.get(key) || null;
|
|
84
84
|
|
|
@@ -254,6 +254,30 @@ const getUriParams = () => {
|
|
|
254
254
|
|
|
255
255
|
const rgb2hex = rgb => `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map(n => parseInt(n, 10).toString(16).padStart(2, '0')).join('')}`
|
|
256
256
|
|
|
257
|
+
const getHostname = url => {
|
|
258
|
+
if(window.URL){
|
|
259
|
+
let u = new window.URL(url);
|
|
260
|
+
return u.hostname
|
|
261
|
+
}else{
|
|
262
|
+
var a = document.createElement(`a`)
|
|
263
|
+
a.href = url
|
|
264
|
+
return a.hostname.replace("www.", "")
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const imgPromiseFactory = ({decode = true, crossOrigin = ''}) =>
|
|
269
|
+
(src): Promise<void> => {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const i = new Image()
|
|
272
|
+
if (crossOrigin) i.crossOrigin = crossOrigin
|
|
273
|
+
i.onload = () => {
|
|
274
|
+
decode && i.decode ? i.decode().then(resolve).catch(reject) : resolve()
|
|
275
|
+
}
|
|
276
|
+
i.onerror = reject
|
|
277
|
+
i.src = src
|
|
278
|
+
})
|
|
279
|
+
}
|
|
280
|
+
|
|
257
281
|
export {
|
|
258
282
|
addProps,
|
|
259
283
|
addScript,
|
|
@@ -266,6 +290,7 @@ export {
|
|
|
266
290
|
isEmail,
|
|
267
291
|
isIPv4,
|
|
268
292
|
isUrl,
|
|
293
|
+
imgPromiseFactory,
|
|
269
294
|
randstr,
|
|
270
295
|
setCSSVar,
|
|
271
296
|
getCookie,
|
|
@@ -280,6 +305,7 @@ export {
|
|
|
280
305
|
rgb2hex,
|
|
281
306
|
generateModalRoutes,
|
|
282
307
|
generatePreservedRoutes,
|
|
283
|
-
generateRegularRoutes
|
|
308
|
+
generateRegularRoutes,
|
|
309
|
+
getHostname
|
|
284
310
|
}
|
|
285
311
|
|
package/src/hooks/index.tsx
CHANGED
|
@@ -5,4 +5,4 @@ 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
|
-
|
|
8
|
+
export { default as useLang } from './useLang'
|
package/src/hooks/useImage.tsx
CHANGED
|
@@ -1,58 +1,84 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {useState} from 'react'
|
|
2
|
+
import { imgPromiseFactory } from '../core'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export type useImageProps = {
|
|
5
|
+
srcList: string | string[]
|
|
6
|
+
imgPromise?: (...args: any[]) => Promise<void>
|
|
7
|
+
useSuspense?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const removeBlankArrayElements = (a) => a.filter((x) => x)
|
|
11
|
+
const stringToArray = (x) => (Array.isArray(x) ? x : [x])
|
|
12
|
+
const cache = {}
|
|
7
13
|
|
|
8
|
-
|
|
14
|
+
// sequential map.find for promises
|
|
15
|
+
const promiseFind = (arr, promiseFactory) => {
|
|
16
|
+
let done = false
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const queueNext = (src) => {
|
|
19
|
+
return promiseFactory(src).then(() => {
|
|
20
|
+
done = true
|
|
21
|
+
resolve(src)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
arr
|
|
26
|
+
.reduce((p, src) => {
|
|
27
|
+
// ensure we aren't done before enqueuing the next source
|
|
28
|
+
return p.catch(() => {
|
|
29
|
+
if (!done) return queueNext(src)
|
|
30
|
+
})
|
|
31
|
+
}, queueNext(arr.shift()))
|
|
32
|
+
.catch(reject)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
9
35
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
36
|
+
const useImage = ({
|
|
37
|
+
srcList,
|
|
38
|
+
imgPromise = imgPromiseFactory({decode: true}),
|
|
39
|
+
useSuspense = true,
|
|
40
|
+
} : useImageProps) : {src: string | undefined; isLoading: boolean; error: any} => {
|
|
13
41
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
42
|
+
const [, setIsSettled] = useState(false)
|
|
43
|
+
const sourceList = removeBlankArrayElements(stringToArray(srcList))
|
|
44
|
+
const sourceKey = sourceList.join('')
|
|
45
|
+
|
|
46
|
+
if (!cache[sourceKey]) {
|
|
47
|
+
// create promise to loop through sources and try to load one
|
|
48
|
+
cache[sourceKey] = {
|
|
49
|
+
promise: promiseFind(sourceList, imgPromise),
|
|
50
|
+
cache: 'pending',
|
|
51
|
+
error: null,
|
|
52
|
+
}
|
|
20
53
|
}
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
[url, crossOrigin, referrerpolicy]
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
return [imageRef.current, statusRef.current];
|
|
55
|
+
// when promise resolves/reject, update cache & state
|
|
56
|
+
if (cache[sourceKey].cache === 'resolved') {
|
|
57
|
+
return {src: cache[sourceKey].src, isLoading: false, error: null}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (cache[sourceKey].cache === 'rejected') {
|
|
61
|
+
if (useSuspense) throw cache[sourceKey].error
|
|
62
|
+
return {isLoading: false, error: cache[sourceKey].error, src: undefined}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
cache[sourceKey].promise
|
|
66
|
+
// if a source was found, update cache
|
|
67
|
+
// when not using suspense, update state to force a rerender
|
|
68
|
+
.then((src) => {
|
|
69
|
+
cache[sourceKey] = {...cache[sourceKey], cache: 'resolved', src}
|
|
70
|
+
if (!useSuspense) setIsSettled(sourceKey)
|
|
71
|
+
})
|
|
72
|
+
// if no source was found, or if another error occurred, update cache
|
|
73
|
+
// when not using suspense, update state to force a rerender
|
|
74
|
+
.catch((error) => {
|
|
75
|
+
cache[sourceKey] = {...cache[sourceKey], cache: 'rejected', error}
|
|
76
|
+
if (!useSuspense) setIsSettled(sourceKey)
|
|
77
|
+
})
|
|
78
|
+
// cache[sourceKey].cache === 'pending')
|
|
79
|
+
if (useSuspense) throw cache[sourceKey].promise
|
|
80
|
+
return {isLoading: true, src: undefined, error: null}
|
|
81
|
+
|
|
56
82
|
}
|
|
57
83
|
|
|
58
84
|
export default useImage;
|
package/src/index.tsx
CHANGED
|
@@ -4,6 +4,8 @@ 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
|
+
|
|
7
9
|
export { default as App } from './comps/app'
|
|
8
10
|
export { default as Box } from './comps/box'
|
|
9
11
|
export { default as Button } from './comps/button'
|
package/src/scss/props.scss
CHANGED
|
@@ -57,4 +57,14 @@ $colors: 'fff','111','222','333','444','555','666','777','888','999';
|
|
|
57
57
|
@if $i % 2 == 0 {
|
|
58
58
|
.s#{$i} { font-size: #{$i}px; }
|
|
59
59
|
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
BoldSize
|
|
64
|
+
It will generate .b400 { font-weight: 400; } from '400'
|
|
65
|
+
*/
|
|
66
|
+
$bsizes: 100,200,300,400,500,600,700,800,900;
|
|
67
|
+
.bold{ font-weight: bold; }
|
|
68
|
+
@each $n in $bsizes{
|
|
69
|
+
.b#{$n} { font-weight: #{$n}; }
|
|
60
70
|
}
|