@zuzjs/ui 0.2.8 → 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.
@@ -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 [photo, status] = useImage(props.src, 'anonymous', 'origin');
12
+ const { src, isLoading, error } = useImage({ srcList: props.src, useSuspense: false });
13
13
 
14
14
  return (
15
15
  <ClassNames>
16
- {({ css, cx }) => <picture className={cx(css`${buildCSS({ flex: true })}`)}>
17
- {status == 'loading' && <Box className={`${props.as ? `${props.as} ` : ``}${cx(css`background: #eee;${buildCSS(props)}`)}`} />}
18
- {status == 'loaded' && <img src={props.src}
19
- className={`${props.as ? `${props.as} ` : ``}${cx(css`${buildCSS(props)}`)}`}
20
- ref={ref} />}
21
- </picture>}
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
 
@@ -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'
@@ -32,8 +32,10 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
32
32
  form,
33
33
  touched,
34
34
  onSubmit,
35
+ value,
35
36
  defaultValue,
36
- fref
37
+ fref,
38
+ autoComplete
37
39
  } = props;
38
40
 
39
41
  const dispatch = useDispatch(STORE_FORM_KEY)
@@ -48,13 +50,16 @@ const Input = forwardRef((props : { [ key: string ] : any }, ref : LegacyRef<HTM
48
50
  return (
49
51
  <ClassNames>
50
52
  {({ css, cx }) => <El
53
+ {...cleanProps(props)}
51
54
  type={type || `text`}
52
55
  placeholder={placeholder || undefined}
56
+ autoComplete={autoComplete || undefined}
53
57
  name={name || nanoid()}
54
58
  multiple={type == 'file' ? multiple : undefined}
55
59
  accept={accept || `*`}
56
60
  className={`${as ? `${as} ` : ``}f ${cx(css`${_defaultCSS}${buildCSS(props)}&:hover {${buildCSS(props.hover || {})}} &:focus {${buildCSS(props.focus || {})}}`)}`}
57
61
  ref={_ref}
62
+ value={value || undefined}
58
63
  defaultValue={defaultValue || ``}
59
64
  onKeyUp={(e : SyntheticEvent) => {
60
65
  let k = e['keyCode'] || ['which'];
@@ -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
- if(document.querySelector(`#${rootID}`)) return;
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
@@ -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",
@@ -31,19 +31,20 @@ class AppTheme {
31
31
 
32
32
  get = () => {
33
33
  let self = this;
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;
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
 
@@ -67,19 +67,20 @@ class AppTheme {
67
67
 
68
68
  get = () => {
69
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;
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
@@ -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 => {
@@ -78,7 +90,7 @@ const randstr = function(len? : number){
78
90
  return text;
79
91
  }
80
92
 
81
- const setCookie = (key : string, value : any, expiry? : number) => Cookies.set(key, value, { expires: expiry || 7 })
93
+ const setCookie = (key : string, value : any, expiry? : number, host? : string) => Cookies.set(key, value, { expires: expiry || 7, domain: host || window.location.host })
82
94
 
83
95
  const getCookie = (key : string) => Cookies.get(key) || null;
84
96
 
@@ -254,10 +266,65 @@ const getUriParams = () => {
254
266
 
255
267
  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
268
 
269
+ const getHostname = url => {
270
+ if(window.URL){
271
+ let u = new window.URL(url);
272
+ return u.hostname
273
+ }else{
274
+ var a = document.createElement(`a`)
275
+ a.href = url
276
+ return a.hostname.replace("www.", "")
277
+ }
278
+ }
279
+
280
+ const imgPromiseFactory = ({decode = true, crossOrigin = ''}) =>
281
+ (src): Promise<void> => {
282
+ return new Promise((resolve, reject) => {
283
+ const i = new Image()
284
+ if (crossOrigin) i.crossOrigin = crossOrigin
285
+ i.onload = () => {
286
+ decode && i.decode ? i.decode().then(resolve).catch(reject) : resolve()
287
+ }
288
+ i.onerror = reject
289
+ i.src = src
290
+ })
291
+ }
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
+
257
323
  export {
258
324
  addProps,
259
325
  addScript,
260
326
  buildCSS,
327
+ cleanProps,
261
328
  buildFormData,
262
329
  byName,
263
330
  byId,
@@ -266,6 +333,7 @@ export {
266
333
  isEmail,
267
334
  isIPv4,
268
335
  isUrl,
336
+ imgPromiseFactory,
269
337
  randstr,
270
338
  setCSSVar,
271
339
  getCookie,
@@ -280,6 +348,10 @@ export {
280
348
  rgb2hex,
281
349
  generateModalRoutes,
282
350
  generatePreservedRoutes,
283
- generateRegularRoutes
351
+ generateRegularRoutes,
352
+ getHostname,
353
+ parseFilename,
354
+ camelCase,
355
+ getMousePosition
284
356
  }
285
357
 
@@ -5,4 +5,5 @@ export { default as useDispatch } from './useDispatch'
5
5
  export { default as useResizeObserver } from './useResizeObserver'
6
6
  export { default as useDevice } from './useDevice'
7
7
  export { default as useToast } from './useToast'
8
- // export { default as useRouter } from './useRouter'
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
@@ -1,58 +1,84 @@
1
- import React, { useRef, useLayoutEffect, useState } from 'react'
1
+ import React, {useState} from 'react'
2
+ import { imgPromiseFactory } from '../core'
2
3
 
3
- const useImage = (url, crossOrigin, referrerpolicy) => {
4
-
5
- const statusRef = useRef('loading')
6
- const imageRef = useRef<HTMLImageElement>();
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
- const [_, setStateToken] = useState(0);
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
- const oldUrl = useRef();
11
- const oldCrossOrigin = useRef();
12
- const oldReferrerPolicy = useRef();
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
- if(oldUrl.current !== url || oldCrossOrigin.current !== crossOrigin || oldReferrerPolicy.current !== referrerpolicy){
15
- statusRef.current = 'loading';
16
- imageRef.current = undefined;
17
- oldUrl.current = url;
18
- oldCrossOrigin.current = crossOrigin;
19
- oldReferrerPolicy.current = referrerpolicy;
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
- useLayoutEffect(
23
- function(){
24
-
25
- if(!url) return;
26
- var img = document.createElement('img');
27
-
28
- function onload(){
29
- statusRef.current = 'loaded';
30
- imageRef.current = img;
31
- setStateToken(Math.random());
32
- }
33
-
34
- function onerror(){
35
- statusRef.current = 'failed';
36
- imageRef.current = undefined;
37
- setStateToken(Math.random());
38
- }
39
-
40
- img.addEventListener('load', onload);
41
- img.addEventListener('error', onerror);
42
- crossOrigin && (img.crossOrigin = crossOrigin);
43
- referrerpolicy && (img.referrerPolicy = referrerpolicy);
44
- img.src = url;
45
-
46
- return function cleanup() {
47
- img.removeEventListener('load', onload);
48
- img.removeEventListener('error', onerror);
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;
@@ -0,0 +1,9 @@
1
+ import { useContext, useEffect } from 'react'
2
+ import AppContext from '../context'
3
+
4
+ const useLang = (mod = 'en') => {
5
+ const state = useContext(AppContext)
6
+ return state['lang'] || {};
7
+ }
8
+
9
+ export default useLang
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'
@@ -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
  }