From daa5b1b89a6f37a34be6e37453872f259f30264d Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 11:41:18 -0800 Subject: [PATCH 01/31] save --- .../HomePage/AllInOnePitchDemoSection.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/projects/site/src/pages/HomePage/AllInOnePitchDemoSection.tsx b/projects/site/src/pages/HomePage/AllInOnePitchDemoSection.tsx index 9eacd8467b..508d126cc1 100644 --- a/projects/site/src/pages/HomePage/AllInOnePitchDemoSection.tsx +++ b/projects/site/src/pages/HomePage/AllInOnePitchDemoSection.tsx @@ -14,20 +14,26 @@ import { PillButtonDark } from '../../views/PillButtonDark' import { TiltSquircle } from '../../views/Squircle' import { TitleText } from '../../views/TitleText' import { SpacedPageContent } from './SpacedPageContent' -import { TitleTextSub } from './TitleTextSub' const SubSection = props => ( - + ) const Dot = gloss(Box, { borderRadius: 100, - width: 9, - height: 9, + width: 8, + height: 8, border: [5, 'transparent'], - margin: [0, 10], + margin: [0, 8], background: [255, 255, 255, 0.5], - opacity: 0.5, + opacity: 0.35, cursor: 'pointer', transition: 'all ease 300ms', @@ -35,8 +41,10 @@ const Dot = gloss(Box, { opacity: 0.8, }, - activeStyle: { - opacity: 1, + conditional: { + isActive: { + opacity: 1, + }, }, }) @@ -171,11 +179,11 @@ export default memo(() => { Batteries included - + {/* Import, display, build, and export. Easier than ever. - + */} } > @@ -194,10 +202,6 @@ export default memo(() => { Display - - - Rich, powerful views to display data & tasks easily. - @@ -238,11 +242,10 @@ export default memo(() => { @@ -395,9 +398,9 @@ export default memo(() => { - + {[0, 1, 2].map(x => ( - goTo(x)} /> + goTo(x)} /> ))} From 99ce4a2a7aaba4ff2a6cca7ceecc5636f1f70de6 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 14:10:43 -0800 Subject: [PATCH 02/31] save --- app/orbit-app/package.json | 4 ++++ app/orbit-app/src/main.tsx | 20 ++++++++++++++++++++ packages/css/src/css.ts | 4 ++++ packages/css/src/validCSSAttribute.dom.ts | 2 ++ packages/gloss/src/stylesheet/sheet.ts | 7 +++++++ packages/ui/src/lists/ListItemSimple.tsx | 6 +++++- 6 files changed, 42 insertions(+), 1 deletion(-) diff --git a/app/orbit-app/package.json b/app/orbit-app/package.json index 8e804ecb3d..6cafc68b9e 100644 --- a/app/orbit-app/package.json +++ b/app/orbit-app/package.json @@ -50,6 +50,10 @@ "react-shadow": "^17.1.3", "react-use-gesture": "^5.1.2", "reconnecting-websocket": "4.1.10", + "requestidlecallback-polyfill": "1.0.2", + "resize-observer-polyfill": "1.5.1", + "intersection-observer": "0.5.1", + "array-flat-polyfill": "^1.0.1", "typeorm": "^0.3.0-alpha.23", "webpack-runtime-require": "0.3.0" }, diff --git a/app/orbit-app/src/main.tsx b/app/orbit-app/src/main.tsx index bbfb7e3f3d..ed69c819e3 100644 --- a/app/orbit-app/src/main.tsx +++ b/app/orbit-app/src/main.tsx @@ -1,4 +1,5 @@ import '../public/styles/base.css' +import 'requestidlecallback-polyfill' /** * ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ @@ -96,6 +97,8 @@ async function main() { // we've already started, ignore if (getGlobalConfig()) return + await initPolyfills() + // if you want to show a loading screen, do it above here await fetchInitialConfig() @@ -194,6 +197,23 @@ async function startApp(forceRefresh: boolean | 'mode' = false) { } } +async function initPolyfills() { + let polyfills: any[] = [] + // polyfills + if (!Array.prototype.flatMap) { + polyfills.push(import('array-flat-polyfill')) + } + if (!window['IntersectionObserver']) { + polyfills.push(import('intersection-observer')) + } + if (!window['ResizeObserver']) { + polyfills.push(async () => { + window['ResizeObserver'] = (await import('resize-observer-polyfill')).default + }) + } + await Promise.all(polyfills.map(x => x())) +} + // hot reloading if (process.env.NODE_ENV === 'development') { if (typeof module['hot'] !== 'undefined') { diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts index b6e0f1c120..c147c284ce 100644 --- a/packages/css/src/css.ts +++ b/packages/css/src/css.ts @@ -146,6 +146,10 @@ export function cssValue(key: string, value: any, recurse = false, options?: CSS if (value.getCSSValue) { return value.getCSSValue() } + // remove any weird looking objects (non-plain) + if (value.constructor.name !== 'Object') { + return + } const res = processObject(key, value, options) if (res !== undefined) { return res diff --git a/packages/css/src/validCSSAttribute.dom.ts b/packages/css/src/validCSSAttribute.dom.ts index 46475ed9dd..349d49b5cf 100644 --- a/packages/css/src/validCSSAttribute.dom.ts +++ b/packages/css/src/validCSSAttribute.dom.ts @@ -8,6 +8,8 @@ const allCSSAttr = {} // add standard ones if (typeof document !== 'undefined') { for (const key in document.body.style) { + // for some reason chrome has 0-11 as valid css attributes + if (+key === +key) continue allCSSAttr[key] = true } } diff --git a/packages/gloss/src/stylesheet/sheet.ts b/packages/gloss/src/stylesheet/sheet.ts index a4936e64e8..8b120615f1 100644 --- a/packages/gloss/src/stylesheet/sheet.ts +++ b/packages/gloss/src/stylesheet/sheet.ts @@ -51,6 +51,13 @@ export class StyleSheet { } insert(key: string, rule: string) { + // this helps catch a nasty class of bugs where you insert an accidental huge string/object + // that you didn't meant to pass into css, causing big slowdowns + if (__DEV__) { + if (rule.length > 2000) { + debugger + } + } const tag = this.tag if (!tag) { return diff --git a/packages/ui/src/lists/ListItemSimple.tsx b/packages/ui/src/lists/ListItemSimple.tsx index 56f065c470..341a5474d3 100644 --- a/packages/ui/src/lists/ListItemSimple.tsx +++ b/packages/ui/src/lists/ListItemSimple.tsx @@ -273,7 +273,11 @@ const ListItemInner = memo(function ListItemInner(props: ListItemSimpleProps) { )} - {coat ? {childrenElement} : childrenElement} + {coat && childrenElement ? ( + {childrenElement} + ) : ( + childrenElement + )} {hasAfterTitle && ( <> From bc7b22877517096098b1e639db5535ea69291905 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 15:49:25 -0800 Subject: [PATCH 03/31] save --- packages/gloss/src/stylesheet/sheet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gloss/src/stylesheet/sheet.ts b/packages/gloss/src/stylesheet/sheet.ts index 8b120615f1..26dd3ab930 100644 --- a/packages/gloss/src/stylesheet/sheet.ts +++ b/packages/gloss/src/stylesheet/sheet.ts @@ -53,7 +53,7 @@ export class StyleSheet { insert(key: string, rule: string) { // this helps catch a nasty class of bugs where you insert an accidental huge string/object // that you didn't meant to pass into css, causing big slowdowns - if (__DEV__) { + if (process.env.NODE_ENV === 'development') { if (rule.length > 2000) { debugger } From e6eb02039d81bd800c0ae276928656fed61b452b Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 17:07:07 -0800 Subject: [PATCH 04/31] save --- packages/gloss-webpack/src/ast/extractStyles.ts | 6 +++--- packages/gloss/src/gloss.tsx | 2 +- projects/site/package.json | 2 +- projects/site/src/pages/HomePage/IntroSection.tsx | 4 ++-- projects/site/src/views/SectionContent.tsx | 6 ++++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/gloss-webpack/src/ast/extractStyles.ts b/packages/gloss-webpack/src/ast/extractStyles.ts index a6ed51a596..87a269b3e3 100644 --- a/packages/gloss-webpack/src/ast/extractStyles.ts +++ b/packages/gloss-webpack/src/ast/extractStyles.ts @@ -235,10 +235,12 @@ export function extractStyles( } // uses the base styles if necessary, merges just like gloss does + if (shouldPrintDebug) console.log('view?.internal?.depth', view?.internal?.depth) + const depth = (view?.internal?.depth ?? -1) + 1 const { styles, conditionalStyles, defaultProps, internalDefaultProps } = getGlossProps( propObject, view, - view?.internal?.depth ?? 0 + depth ) if (shouldPrintDebug) { console.log('glossCall.arguments parse gloss props', name, styles, conditionalStyles) @@ -254,8 +256,6 @@ export function extractStyles( delete defaultProps.className } - const depth = (view?.internal?.depth ?? -1) + 1 - for (const ns in styles) { const info = StaticUtils.getStyles(styles[ns], depth, ns) if (shouldPrintDebug) { diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 18a3741d92..ea421c3fa8 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -990,7 +990,7 @@ function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, e usedProps: new Set() } // themes always one above, extraDepth if theres a local view - const depth = view.internal.depth + 1 + extraDepth + const depth = view.internal.depth + extraDepth const themeStyles: StaticStyleDesc[] = [] const len = themeFns.length - 1 const theme = createThemeProxy(userTheme, trackState, props) diff --git a/projects/site/package.json b/projects/site/package.json index 1c01cfa64d..9fd57512c4 100644 --- a/projects/site/package.json +++ b/projects/site/package.json @@ -5,7 +5,7 @@ "main": "./src/index.tsx", "scripts": { "start": "mcro-build --port 5000 --extract-static-styles --target web", - "start:prod": "mcro-build --extract-static-styles --prod --port 5000 --report --target web", + "start:prod": "OPTIMIZE_REACT=1 mcro-build --extract-static-styles --prod --port 5000 --report --target web", "build": "OPTIMIZE_REACT=1 ANALYZE_BUNDLE=1 mcro-build --extract-static-styles --prod --build --target web", "build:no-min": "NO_OPTIMIZE=1 mcro-build --prod --build --target web", "build:debug": "NODE_ENV=development mcro-build --prod --build --target web", diff --git a/projects/site/src/pages/HomePage/IntroSection.tsx b/projects/site/src/pages/HomePage/IntroSection.tsx index bdf2ff67bb..c8002363fa 100644 --- a/projects/site/src/pages/HomePage/IntroSection.tsx +++ b/projects/site/src/pages/HomePage/IntroSection.tsx @@ -79,8 +79,8 @@ export default function IntroSection() {  with a powerful internal tool platform. - It's a powerful platform to build common apps for teams. With easy data plugins, a - rich view kit, and a collaborative app-store. + An easy, beautiful workspace where you can install and build apps. Easy data plugins, + a rich view kit, and a collaborative app-store. {wordsWithBrandMark(`Think of it as Bootstrap, for internal apps.`)} diff --git a/projects/site/src/views/SectionContent.tsx b/projects/site/src/views/SectionContent.tsx index 18c0ec3543..717a3d4dcd 100644 --- a/projects/site/src/views/SectionContent.tsx +++ b/projects/site/src/views/SectionContent.tsx @@ -60,8 +60,10 @@ export const SectionContentChrome = gloss<{ readablePage?: boolean }, ViewProps> paddingRight: sidePad, position: 'relative', - readablePage: { - maxWidth: widths.md, + conditional: { + readablePage: { + maxWidth: widths.md, + }, }, [mediaQueries.lg]: { From 7760849ff0ee56628c90d00fcc905eade5be3314 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 17:28:09 -0800 Subject: [PATCH 05/31] ts --- app/orbit-desktop/package.json | 2 +- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/orbit-desktop/package.json b/app/orbit-desktop/package.json index 5a8c4f9a42..2daf4fdd02 100644 --- a/app/orbit-desktop/package.json +++ b/app/orbit-desktop/package.json @@ -93,7 +93,7 @@ "time-fix-plugin": "^2.0.6", "ts-loader": "^6.0.1", "typeorm": "^0.3.0-alpha.23", - "typescript": "3.7.1-rc", + "typescript": "3.7.2", "url-loader": "^2.1.0", "webpack": "4.41.2", "webpack-dev-middleware": "^3.7.1", diff --git a/package.json b/package.json index 9a378b5b01..96181be3cc 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "sb-promisify": "^2.0.2", "sqlite": "^3.0.3", "ts-jest": "24.0.0", - "typescript": "3.7.1-rc", + "typescript": "3.7.2", "webpack-cli": "^3.3.7" }, "resolutions": { diff --git a/yarn.lock b/yarn.lock index 813ef5cd33..d42bed1c65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20693,10 +20693,10 @@ typeorm@0.3.0-alpha.23, typeorm@^0.3.0-alpha.23: yargs "^13.2.1" zen-observable "^0.8.9" -typescript@3.7.1-rc: - version "3.7.1-rc" - resolved "https://registry.npmjs.org/typescript/-/typescript-3.7.1-rc.tgz#2054b872d67f8dc732e36c1df397f9327f37ada0" - integrity sha512-2rMtWppLsaPvmpXsoIAXWDBQVnJMw1ITGGSnidMuayLj9iCmMRT69ncKZw/Mk5rXfJkilApKucWQZxproALoRw== +typescript@3.7.2: + version "3.7.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" + integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== ua-parser-js@^0.7.18: version "0.7.20" From b1d93b10997f47a8d09b4d1ed94444938bf5509d Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 18:05:43 -0800 Subject: [PATCH 06/31] save --- packages/ui/src/text/textSizeTheme.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/text/textSizeTheme.ts b/packages/ui/src/text/textSizeTheme.ts index 1eb8a152eb..c41f758a52 100644 --- a/packages/ui/src/text/textSizeTheme.ts +++ b/packages/ui/src/text/textSizeTheme.ts @@ -11,8 +11,8 @@ export type TextSizeProps = { fontSize?: Size size?: Size scale?: Size - marginTop?: number - marginBottom?: number + marginTop?: any + marginBottom?: any } export function textSizeTheme(props: TextSizeProps) { From 2d9c58b7b7145f4becae6e94d529edea8cb05303 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 19:39:58 -0800 Subject: [PATCH 07/31] start on atomic css --- packages/css/src/constants.ts | 15 ++ packages/css/src/css.ts | 2 +- packages/gloss/src/gloss.tsx | 138 ++++------ projects/playground/src/TestMediaQueries.tsx | 5 + projects/playground/src/TestUI.tsx | 252 +------------------ projects/playground/src/TestUIGlossSpeed.tsx | 25 ++ projects/playground/src/TestUIParallax.tsx | 190 ++++++++++++++ 7 files changed, 294 insertions(+), 333 deletions(-) create mode 100644 projects/playground/src/TestMediaQueries.tsx create mode 100644 projects/playground/src/TestUIGlossSpeed.tsx create mode 100644 projects/playground/src/TestUIParallax.tsx diff --git a/packages/css/src/constants.ts b/packages/css/src/constants.ts index d19e477051..97dc9f6069 100644 --- a/packages/css/src/constants.ts +++ b/packages/css/src/constants.ts @@ -12,6 +12,21 @@ export const validCSSAttr: Partial = ? require('./validCSSAttribute.node').default : require('./validCSSAttribute.dom').default +const existing = new Set() +export const cssAttributeAbbreviations = Object.keys(validCSSAttr).reduce((acc, key) => { + let found = '' + let i = 1 + while (true) { + found = key.slice(0, i) + if (!existing.has(found)) break + i++ + } + existing.add(found) + acc[key] = found + return acc +}, {}) +console.log('cssAttributeAbbreviations', cssAttributeAbbreviations) + // various helpful constants export const UNDEFINED = 'undefined' diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts index c147c284ce..b18ec74b7b 100644 --- a/packages/css/src/css.ts +++ b/packages/css/src/css.ts @@ -8,7 +8,7 @@ import { px, stringHash } from './helpers' export { GlossPropertySet } from './cssPropertySet' export { configureCSS } from './config' -export { validCSSAttr } from './constants' +export { validCSSAttr, cssAttributeAbbreviations } from './constants' export { CSSPropertySet, CSSPropertySetResolved, diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index ea421c3fa8..612a9a8cf7 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -1,4 +1,4 @@ -import { CSSPropertySet, CSSPropertySetLoose, cssStringWithHash, stringHash, validCSSAttr } from '@o/css' +import { cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, stringHash, validCSSAttr } from '@o/css' import React from 'react' import { createElement, isValidElement, memo, useEffect, useRef } from 'react' @@ -383,7 +383,7 @@ function addStyles( selectorPrefix?: string ) { const namespaces = getSortedNamespaces(styles) - let classNames: string[] | null = null + let allClassNames: string[] | null = null for (const ns of namespaces) { const style = styles[ns] // they may return falsy, conditional '&:hover': active ? hoverStyle : null @@ -392,21 +392,24 @@ function addStyles( // add the stylesheets and classNames // TODO could do a simple "diff" so that fast-changing styles only change the "changing" props // it would likely help things like when you animate based on mousemove, may be slower in default case - const className = addRules(displayName, style, ns, depth || 0, selectorPrefix, true) + const classNames = addRules(displayName, style, ns, depth || 0, selectorPrefix, true) - if (className) { - classNames = classNames || [] - classNames.push(className) + if (classNames.length) { + allClassNames = allClassNames || [] + // @ts-ignore + allClassNames = [...allClassNames, ...classNames] // if this is the first mount render or we didn't previously have this class then add it as new - if (!prevClassNames || !prevClassNames.includes(className)) { - gc.registerClassUse(className.slice(2)) + for (const className of classNames) { + if (!prevClassNames || !prevClassNames.includes(className)) { + gc.registerClassUse(className) + } } } } if (isDeveloping && shouldDebug) { - console.log('addStyles sorted', classNames, namespaces, styles) + console.log('addStyles sorted', allClassNames, namespaces, styles) } - return classNames + return allClassNames } // sort pseudos into priority @@ -783,93 +786,56 @@ const createParentKey = (k: string) => { return next } +// : (A extends true ? string[] : { +// css: string, +// className: string +// }) | null function addRules( displayName = '_', rules: BaseRules, namespace: string, - depth: number, - selectorPrefix?: string, + _depth: number, + _selectorPrefix?: string, insert?: A, -): (A extends true ? string : { - css: string, - className: string -}) | null { - const [hash, style] = cssStringWithHash(rules, cssOpts) - - // empty object, no need to add anything - if (!hash) { - return null - } - - let className = `${hash}` - // build the class name with the display name of the styled component and a unique id based on the css and namespace - // ensure we are unique for unique namespaces - if (namespace !== '.') { - const postfix = nicePostfix[namespace] || stringHash(namespace) - className += `-${postfix}` - } - if (selectorPrefix) { - className += parentKeys[selectorPrefix] || createParentKey(selectorPrefix) - } - +): string[] { const isMediaQuery = namespace[0] === '@' - const selector = getSelector(className, namespace, selectorPrefix) - const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` - const finalClassName = `g${depth}${className}` - - if (insert === true) { - // this is the first time we've found this className - if (!tracker.has(className)) { - // insert the new style text - tracker.set(className, { - displayName, - namespace, - rules, - selector, - style, - className, - }) - sheet.insert(isMediaQuery ? namespace : selector, css) + const classNames: string[] = [] + + for (const key in rules) { + const val = cssValue(key, rules[key], false, cssOpts) + const style = `${key}: ${val}` + const className = cssAttributeAbbreviations[key] + stringHash(val) + let selector = `.${className}` + if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { + selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') + } + const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` + if (className !== undefined) { + classNames.push(className) + if (insert === true) { + // this is the first time we've found this className + if (!tracker.has(className)) { + // insert the new style text + tracker.set(className, { + displayName, + namespace, + rules, + selector, + style, + className, + }) + sheet.insert(isMediaQuery ? namespace : selector, css) + } + } } - // @ts-ignore - return finalClassName } - // @ts-ignore - return { css, className: finalClassName } -} - -// has to return a .s-id and .id selector for use in parents passing down styles -function getSelector(className: string, namespace: string, selectorPrefix = '') { - if (namespace[0] === '@') { - // media queries need stronger binding, we'll do html selector - return getSpecificSelectors(className, selectorPrefix + 'body') - } - if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { - // namespace === '&:hover, &:focus, & > div' - const namespacedSelectors = namespace - .split(',') - .flatMap(part => { - return getSpecificSelectors(className, selectorPrefix, part.replace('&', '')) - }) - .join(',') - return namespacedSelectors - } - return getSpecificSelectors(className, selectorPrefix) -} + return classNames -// for now, assume now more than 6 levels nesting (css = 🤮) -const depths = [0, 1, 2, 3, 4, 5] -const dSelectors = depths.map(i => i === 0 ? '' : `._g${i}`.repeat(i)) -function getSpecificSelectors(base: string, parent = '', after = '') { - let s: string[] = [] - for (const i of depths) { - s.push(`${parent}${dSelectors[i]} .g${i}${base}${after}`) - } - return s.join(',') + // @ts-ignore + // return { css, className: finalClassName } } - // some internals we can export if (typeof window !== 'undefined') { window['gloss'] = window['gloss'] || { @@ -953,6 +919,7 @@ function getAllStyles(props: any, depth = 0, ns = '.') { if (!styleObj) continue const info = addRules('', styleObj, ns, depth, '', false) if (info) { + // @ts-ignore styles.push({ ns, ...info, }) } } @@ -1007,6 +974,7 @@ function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, e if (!styleObj) continue const info = addRules('', styleObj, ns, themeDepth, 'html ', false) if (info) { + // @ts-ignore themeStyles.push({ ns, ...info, }) } } diff --git a/projects/playground/src/TestMediaQueries.tsx b/projects/playground/src/TestMediaQueries.tsx new file mode 100644 index 0000000000..85b4838c98 --- /dev/null +++ b/projects/playground/src/TestMediaQueries.tsx @@ -0,0 +1,5 @@ +import { View } from '@o/ui' + +export function TestMediaQueries() { + return +} diff --git a/projects/playground/src/TestUI.tsx b/projects/playground/src/TestUI.tsx index 7d42f452c2..0b423b1505 100644 --- a/projects/playground/src/TestUI.tsx +++ b/projects/playground/src/TestUI.tsx @@ -1,9 +1,6 @@ -import { Button, CardSimple, Parallax, Stack, Title, View } from '@o/ui' -import _ from 'lodash' +import { View } from '@o/ui' import * as React from 'react' -import { TestUIMotion } from './TestUIMotion' - export function TestUI() { return ( <> @@ -11,254 +8,15 @@ export function TestUI() { {/* */} {/* */} {/* */} - + {/* */} {/* */} {/* */} {/* */} + ) } -export function TestMediaQueries() { - return -} - -const logPass = x => { - console.log(x) - return x -} - -export function TestUIParallax() { - console.log('render me') - - const views = index => ( - <> - {/* - helloooooo {index} - */} - - offset 0.75 speed 0.25 index {index} - - - offset 0 speed 0.25 index {index} - - { - return { - y: geometry - .useParallaxIntersection({ speed: 3, offset: 0.5, clamp: [-1, 1.5] }) - .transform([-1, -0.4, 0, 1, 1.4, 1.5], [-1, -0.1, 0, 0.1, 0.2, 0.8]) - .transform(geometry.transforms.scrollParent), - - opacity: geometry - .useParallaxIntersection({ speed: 3, offset: 0.5, clamp: [-1, 1.5] }) - .transform([-1, -0.4, 0, 1, 1.4, 1.5], [-1, 1, 1, 1, 1, 0.8]), - } - }} - > - Hello {index} - - - offset 0.5 speed 2 - - - ({ - opacity: geometry - .useParallaxIntersection({ speed: 1, offset: 0 }) - .transform([-1, 0.5, 0.5, 1], [0, 1, 1, -1]), - })} - > - opacity in/out - - - ({ - opacity: geometry.useParallaxIntersection(), - })} - > - opacity - - - ({ - opacity: geometry.useParallaxIntersection(), - })} - > - offset 0 speed 1 - - { - return { - y: geometry - .useParallaxIntersection({ - speed: 1, - relativeTo: 'frame', - }) - .transform([-1, -0.2, 0, 0.2, 1], [-3, -0.05, 0, 0.05, 3]) - .transform([-1, -0.2, 0, 0.2, 1], [-3, -0.05, 0, 0.05, 3]) - .transform(x => x * 4), - } - }} - > - Hellow Orld - - - ) - - function getParallax({ speed = 1 } = {}) { - const startClamp = [-1, -0.15, 0, 0.3, 1.5] - return geometry => ({ - y: geometry - .useParallaxIntersection({ - speed, - relativeTo: 'frame', - }) - .transform(startClamp, [-2, -0.025, 0, 0.025, 0]) - .transform(x => x * 250) - .transform(x => Math.max(-300, Math.min(x, 300))), - opacity: geometry - .useParallaxIntersection({ - speed: 1, - relativeTo: 'frame', - }) - .transform(startClamp, [-3, 1, 1, 1, 1]) - .transform(x => Math.max(0, Math.min(x, 1))), - }) - } - - return ( - <> - - {views(0)} - - - - - Hello World - - - Hello World - - - Hello World - - - - - {views(2)} - - - {views(3)} - - - {views(4)} - - - {views(5)} - - - ) -} - -export function TestUIGlossSpeed() { - const [key, setKey] = React.useState(0) - - console.time('render') - console.time('write') - React.useLayoutEffect(() => { - console.timeEnd('write') - }, []) - - const items = ( - - - - {_.fill(new Array(50), 0).map((_, index) => ( - - lorem ipsume sit amet - - ))} - - - ) - console.timeEnd('render') - return items +function TestIndividualClassNames() { + return } diff --git a/projects/playground/src/TestUIGlossSpeed.tsx b/projects/playground/src/TestUIGlossSpeed.tsx new file mode 100644 index 0000000000..62d448c7ea --- /dev/null +++ b/projects/playground/src/TestUIGlossSpeed.tsx @@ -0,0 +1,25 @@ +import { Button, CardSimple, Stack } from '@o/ui' +import _ from 'lodash' + +export function TestUIGlossSpeed() { + const [key, setKey] = React.useState(0) + console.time('render') + console.time('write') + React.useLayoutEffect(() => { + console.timeEnd('write') + }, []) + const items = ( + + + + {_.fill(new Array(50), 0).map((_, index) => ( + + lorem ipsume sit amet + + ))} + + + ) + console.timeEnd('render') + return items +} diff --git a/projects/playground/src/TestUIParallax.tsx b/projects/playground/src/TestUIParallax.tsx new file mode 100644 index 0000000000..8b7a89b950 --- /dev/null +++ b/projects/playground/src/TestUIParallax.tsx @@ -0,0 +1,190 @@ +import { Parallax, Stack, Title } from '@o/ui' + +export function TestUIParallax() { + console.log('render me') + const views = index => ( + <> + + offset 0.75 speed 0.25 index {index} + + + offset 0 speed 0.25 index {index} + + { + return { + y: geometry + .useParallaxIntersection({ speed: 3, offset: 0.5, clamp: [-1, 1.5] }) + .transform([-1, -0.4, 0, 1, 1.4, 1.5], [-1, -0.1, 0, 0.1, 0.2, 0.8]) + .transform(geometry.transforms.scrollParent), + opacity: geometry + .useParallaxIntersection({ speed: 3, offset: 0.5, clamp: [-1, 1.5] }) + .transform([-1, -0.4, 0, 1, 1.4, 1.5], [-1, 1, 1, 1, 1, 0.8]), + } + }} + > + Hello {index} + + + offset 0.5 speed 2 + + + ({ + opacity: geometry + .useParallaxIntersection({ speed: 1, offset: 0 }) + .transform([-1, 0.5, 0.5, 1], [0, 1, 1, -1]), + })} + > + opacity in/out + + + ({ + opacity: geometry.useParallaxIntersection(), + })} + > + opacity + + + ({ + opacity: geometry.useParallaxIntersection(), + })} + > + offset 0 speed 1 + + { + return { + y: geometry + .useParallaxIntersection({ + speed: 1, + relativeTo: 'frame', + }) + .transform([-1, -0.2, 0, 0.2, 1], [-3, -0.05, 0, 0.05, 3]) + .transform([-1, -0.2, 0, 0.2, 1], [-3, -0.05, 0, 0.05, 3]) + .transform(x => x * 4), + } + }} + > + Hellow Orld + + + ) + function getParallax({ speed = 1 } = {}) { + const startClamp = [-1, -0.15, 0, 0.3, 1.5] + return geometry => ({ + y: geometry + .useParallaxIntersection({ + speed, + relativeTo: 'frame', + }) + .transform(startClamp, [-2, -0.025, 0, 0.025, 0]) + .transform(x => x * 250) + .transform(x => Math.max(-300, Math.min(x, 300))), + opacity: geometry + .useParallaxIntersection({ + speed: 1, + relativeTo: 'frame', + }) + .transform(startClamp, [-3, 1, 1, 1, 1]) + .transform(x => Math.max(0, Math.min(x, 1))), + }) + } + return ( + <> + + {views(0)} + + + + + Hello World + + + Hello World + + + Hello World + + + + + {views(2)} + + + {views(3)} + + + {views(4)} + + + {views(5)} + + + ) +} From 78ceec032d11e5d181cc3fc446937b89f3a6cd66 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 20:26:40 -0800 Subject: [PATCH 08/31] save --- packages/css/src/constants.ts | 26 ++++++++++++--- packages/css/src/css.ts | 1 + packages/gloss/src/gloss.tsx | 34 ++++++++------------ packages/gloss/src/stylesheet/gc.ts | 1 - projects/playground/src/TestCSSVariables.tsx | 4 +-- projects/playground/src/TestUI.tsx | 10 ++---- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/packages/css/src/constants.ts b/packages/css/src/constants.ts index 97dc9f6069..e8c9747bdc 100644 --- a/packages/css/src/constants.ts +++ b/packages/css/src/constants.ts @@ -15,16 +15,32 @@ export const validCSSAttr: Partial = const existing = new Set() export const cssAttributeAbbreviations = Object.keys(validCSSAttr).reduce((acc, key) => { let found = '' - let i = 1 - while (true) { - found = key.slice(0, i) - if (!existing.has(found)) break - i++ + if (key.length < 4) { + found = `${key}` + } else { + let i = 1 + while (true) { + const abbrevs = getAbbrevs(key) + found = abbrevs.slice(0, i).join('') + if (i > abbrevs.length) { + found += `${i}` + } + if (!existing.has(found)) break + i++ + } } existing.add(found) acc[key] = found return acc }, {}) + +function getAbbrevs(key: string) { + let options = [key[0]] + const uppercases = key.match(/[A-Z]/g) + if (uppercases) options = [...options, ...uppercases] + return options +} + console.log('cssAttributeAbbreviations', cssAttributeAbbreviations) // various helpful constants diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts index b18ec74b7b..49bebb4ec7 100644 --- a/packages/css/src/css.ts +++ b/packages/css/src/css.ts @@ -6,6 +6,7 @@ import { px, stringHash } from './helpers' // exports +export { SNAKE_TO_CAMEL, CAMEL_TO_SNAKE } from './cssNameMap' export { GlossPropertySet } from './cssPropertySet' export { configureCSS } from './config' export { validCSSAttr, cssAttributeAbbreviations } from './constants' diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 612a9a8cf7..8096ba2547 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -1,4 +1,4 @@ -import { cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, stringHash, validCSSAttr } from '@o/css' +import { CAMEL_TO_SNAKE, cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, stringHash, validCSSAttr } from '@o/css' import React from 'react' import { createElement, isValidElement, memo, useEffect, useRef } from 'react' @@ -88,7 +88,7 @@ export type GlossStaticStyleDescription = { const GlossComponentSymbol = Symbol('__GLOSS_SIMPLE_COMPONENT__') as any export const tracker: StyleTracker = new Map() -export const sheet = new StyleSheet(true) +export const sheet = new StyleSheet(false) const gc = new GarbageCollector(sheet, tracker) // helpful global to let us add debugging in dev mode anywhere in here @@ -771,21 +771,6 @@ const cssOpts = { resolveFunctionValue: val => val(curTheme) } -const nicePostfix = { - '&:hover': 'hover', - '&:active': 'active', - '&:disabled': 'disabled', - '&:focus': 'focus', - '&:focus-within': 'focuswithin', -} - -const parentKeys = {} -const createParentKey = (k: string) => { - const next = '-' + k.replace(/\s+/g, '') - parentKeys[k] = next - return next -} - // : (A extends true ? string[] : { // css: string, // className: string @@ -801,11 +786,19 @@ function addRules( const isMediaQuery = namespace[0] === '@' const classNames: string[] = [] - for (const key in rules) { + for (let key in rules) { + const abbrev = cssAttributeAbbreviations[key] + if (!abbrev) { + console.warn('what the key', key) + continue + } const val = cssValue(key, rules[key], false, cssOpts) - const style = `${key}: ${val}` - const className = cssAttributeAbbreviations[key] + stringHash(val) + key = CAMEL_TO_SNAKE[key] || key + if (val === undefined) continue + const style = `${key}:${val};` + const className = abbrev + stringHash(`${val}`) let selector = `.${className}` + if (selector === '.NaN') debugger if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') } @@ -819,7 +812,6 @@ function addRules( tracker.set(className, { displayName, namespace, - rules, selector, style, className, diff --git a/packages/gloss/src/stylesheet/gc.ts b/packages/gloss/src/stylesheet/gc.ts index 6a9736edce..beb6af76e9 100644 --- a/packages/gloss/src/stylesheet/gc.ts +++ b/packages/gloss/src/stylesheet/gc.ts @@ -15,7 +15,6 @@ export type StyleTracker = Map< { displayName?: string namespace: string - rules: BaseRules // we can compile it later and use it for speed // only using in the motion View.tsx stuff for now styleObject?: Object diff --git a/projects/playground/src/TestCSSVariables.tsx b/projects/playground/src/TestCSSVariables.tsx index 641cede999..703820a402 100644 --- a/projects/playground/src/TestCSSVariables.tsx +++ b/projects/playground/src/TestCSSVariables.tsx @@ -11,9 +11,9 @@ export function TestCSSVariables() { - {/* + - */} + diff --git a/projects/playground/src/TestUI.tsx b/projects/playground/src/TestUI.tsx index 0b423b1505..962bd91e6e 100644 --- a/projects/playground/src/TestUI.tsx +++ b/projects/playground/src/TestUI.tsx @@ -1,6 +1,7 @@ -import { View } from '@o/ui' import * as React from 'react' +import { TestCSSVariables } from './TestCSSVariables' + export function TestUI() { return ( <> @@ -11,12 +12,7 @@ export function TestUI() { {/* */} {/* */} {/* */} - {/* */} - + ) } - -function TestIndividualClassNames() { - return -} From f6338b4c5f90b86304d3d2613468806338deb469 Mon Sep 17 00:00:00 2001 From: natew Date: Tue, 5 Nov 2019 22:20:38 -0800 Subject: [PATCH 09/31] save --- packages/css/src/constants.ts | 24 ++++--- packages/css/src/css.ts | 2 +- packages/gloss/src/gloss.tsx | 72 +++++++++++-------- projects/site/package.json | 2 +- .../src/pages/HomePage/FeaturesSection.tsx | 4 +- .../site/src/pages/HomePage/FooterSection.tsx | 2 +- 6 files changed, 63 insertions(+), 43 deletions(-) diff --git a/packages/css/src/constants.ts b/packages/css/src/constants.ts index e8c9747bdc..91a310ba8a 100644 --- a/packages/css/src/constants.ts +++ b/packages/css/src/constants.ts @@ -7,11 +7,26 @@ import { CSSPropertySet } from './cssPropertySet' type CSSPropertyKey = keyof CSSPropertySet type ValidCSSPropertyMap = { [key in CSSPropertyKey]: boolean } -export const validCSSAttr: Partial = +export const SHORTHANDS = { + borderLeftRadius: ['borderTopLeftRadius', 'borderBottomLeftRadius'], + borderRightRadius: ['borderTopRightRadius', 'borderBottomRightRadius'], + borderBottomRadius: ['borderBottomLeftRadius', 'borderBottomRightRadius'], + borderTopRadius: ['borderTopRightRadius', 'borderTopLeftRadius'], +} + +const regularCSSAttributes = process.env.RENDER_TARGET === 'node' ? require('./validCSSAttribute.node').default : require('./validCSSAttribute.dom').default +export const validCSSAttr: Partial = { + ...regularCSSAttributes, + ...Object.keys(SHORTHANDS).reduce((acc, key) => { + acc[key] = true + return acc + }, {}), +} + const existing = new Set() export const cssAttributeAbbreviations = Object.keys(validCSSAttr).reduce((acc, key) => { let found = '' @@ -70,13 +85,6 @@ export const TRANSFORM_KEYS_MAP = { export const COMMA_JOINED = new Set(['boxShadow', 'transition']) -export const SHORTHANDS = { - borderLeftRadius: ['borderTopLeftRadius', 'borderBottomLeftRadius'], - borderRightRadius: ['borderTopRightRadius', 'borderBottomRightRadius'], - borderBottomRadius: ['borderBottomLeftRadius', 'borderBottomRightRadius'], - borderTopRadius: ['borderTopRightRadius', 'borderTopLeftRadius'], -} - export const FALSE_VALUES = { background: 'transparent', backgroundColor: 'transparent', diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts index 49bebb4ec7..a2eb76de2f 100644 --- a/packages/css/src/css.ts +++ b/packages/css/src/css.ts @@ -9,7 +9,7 @@ import { px, stringHash } from './helpers' export { SNAKE_TO_CAMEL, CAMEL_TO_SNAKE } from './cssNameMap' export { GlossPropertySet } from './cssPropertySet' export { configureCSS } from './config' -export { validCSSAttr, cssAttributeAbbreviations } from './constants' +export { SHORTHANDS, validCSSAttr, cssAttributeAbbreviations } from './constants' export { CSSPropertySet, CSSPropertySetResolved, diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 8096ba2547..366b133a18 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -1,4 +1,4 @@ -import { CAMEL_TO_SNAKE, cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, stringHash, validCSSAttr } from '@o/css' +import { CAMEL_TO_SNAKE, cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, SHORTHANDS, stringHash, validCSSAttr } from '@o/css' import React from 'react' import { createElement, isValidElement, memo, useEffect, useRef } from 'react' @@ -783,51 +783,61 @@ function addRules( _selectorPrefix?: string, insert?: A, ): string[] { - const isMediaQuery = namespace[0] === '@' const classNames: string[] = [] - for (let key in rules) { - const abbrev = cssAttributeAbbreviations[key] - if (!abbrev) { - console.warn('what the key', key) + if (!cssAttributeAbbreviations[key]) { + // console.warn('what the key', key) continue } const val = cssValue(key, rules[key], false, cssOpts) - key = CAMEL_TO_SNAKE[key] || key if (val === undefined) continue - const style = `${key}:${val};` - const className = abbrev + stringHash(`${val}`) - let selector = `.${className}` - if (selector === '.NaN') debugger - if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { - selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') - } - const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` - if (className !== undefined) { - classNames.push(className) - if (insert === true) { - // this is the first time we've found this className - if (!tracker.has(className)) { - // insert the new style text - tracker.set(className, { - displayName, - namespace, - selector, - style, - className, - }) - sheet.insert(isMediaQuery ? namespace : selector, css) - } + if (SHORTHANDS[key]) { + for (let k of SHORTHANDS[key]) { + addRule(k, val, namespace, classNames, insert, displayName) } + } else { + addRule(key, val, namespace, classNames, insert, displayName) } } - return classNames // @ts-ignore // return { css, className: finalClassName } } +function addRule(key: string, val: string, namespace: string, classNames: string[], insert: any, displayName: string) { + const abbrev = cssAttributeAbbreviations[key] + key = CAMEL_TO_SNAKE[key] || key + const isMediaQuery = namespace[0] === '@' + if (!abbrev) { + debugger + } + const style = `${key}:${val};` + const className = abbrev + stringHash(`${val}`) + let selector = `.${className}` + if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { + selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') + } + const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` + if (className !== undefined) { + classNames.push(className) + if (insert === true) { + // this is the first time we've found this className + if (!tracker.has(className)) { + // insert the new style text + tracker.set(className, { + displayName, + namespace, + selector, + style, + className, + }) + sheet.insert(isMediaQuery ? namespace : selector, css) + } + } + } +} + // some internals we can export if (typeof window !== 'undefined') { window['gloss'] = window['gloss'] || { diff --git a/projects/site/package.json b/projects/site/package.json index 9fd57512c4..03c36e6e9c 100644 --- a/projects/site/package.json +++ b/projects/site/package.json @@ -4,7 +4,7 @@ "private": true, "main": "./src/index.tsx", "scripts": { - "start": "mcro-build --port 5000 --extract-static-styles --target web", + "start": "mcro-build --port 5000 --target web", "start:prod": "OPTIMIZE_REACT=1 mcro-build --extract-static-styles --prod --port 5000 --report --target web", "build": "OPTIMIZE_REACT=1 ANALYZE_BUNDLE=1 mcro-build --extract-static-styles --prod --build --target web", "build:no-min": "NO_OPTIMIZE=1 mcro-build --prod --build --target web", diff --git a/projects/site/src/pages/HomePage/FeaturesSection.tsx b/projects/site/src/pages/HomePage/FeaturesSection.tsx index 6ab8fe1746..748f58f8e5 100644 --- a/projects/site/src/pages/HomePage/FeaturesSection.tsx +++ b/projects/site/src/pages/HomePage/FeaturesSection.tsx @@ -62,7 +62,9 @@ export default memo(function FeaturesSection() { Create internal apps with - + + + ) From aa1c98d071024acace79328b9a6a1bd29e055497 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 11:48:54 -0800 Subject: [PATCH 10/31] save --- packages/css/src/constants.ts | 17 +- .../gloss-webpack/src/ast/extractStyles.ts | 2 - packages/gloss/src/gloss.tsx | 162 +++++++++--------- projects/site/src/SiteRoot.tsx | 98 ++++++----- .../src/pages/HomePage/FeaturesSection.tsx | 4 +- 5 files changed, 149 insertions(+), 134 deletions(-) diff --git a/packages/css/src/constants.ts b/packages/css/src/constants.ts index 91a310ba8a..46d549fc5d 100644 --- a/packages/css/src/constants.ts +++ b/packages/css/src/constants.ts @@ -27,11 +27,14 @@ export const validCSSAttr: Partial = { }, {}), } +// css attribute key abbreviations const existing = new Set() -export const cssAttributeAbbreviations = Object.keys(validCSSAttr).reduce((acc, key) => { +export const cssAttributeAbbreviations = {} +export const cssAbbreviationToAttribute = {} // inverse +for (const key in validCSSAttr) { let found = '' if (key.length < 4) { - found = `${key}` + found = `${key}-` } else { let i = 1 while (true) { @@ -40,15 +43,15 @@ export const cssAttributeAbbreviations = Object.keys(validCSSAttr).reduce((acc, if (i > abbrevs.length) { found += `${i}` } + found += '-' if (!existing.has(found)) break i++ } } existing.add(found) - acc[key] = found - return acc -}, {}) - + cssAbbreviationToAttribute[found] = key + cssAttributeAbbreviations[key] = found +} function getAbbrevs(key: string) { let options = [key[0]] const uppercases = key.match(/[A-Z]/g) @@ -56,8 +59,6 @@ function getAbbrevs(key: string) { return options } -console.log('cssAttributeAbbreviations', cssAttributeAbbreviations) - // various helpful constants export const UNDEFINED = 'undefined' diff --git a/packages/gloss-webpack/src/ast/extractStyles.ts b/packages/gloss-webpack/src/ast/extractStyles.ts index 87a269b3e3..08f4eec037 100644 --- a/packages/gloss-webpack/src/ast/extractStyles.ts +++ b/packages/gloss-webpack/src/ast/extractStyles.ts @@ -235,12 +235,10 @@ export function extractStyles( } // uses the base styles if necessary, merges just like gloss does - if (shouldPrintDebug) console.log('view?.internal?.depth', view?.internal?.depth) const depth = (view?.internal?.depth ?? -1) + 1 const { styles, conditionalStyles, defaultProps, internalDefaultProps } = getGlossProps( propObject, view, - depth ) if (shouldPrintDebug) { console.log('glossCall.arguments parse gloss props', name, styles, conditionalStyles) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 366b133a18..688b22e410 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -58,7 +58,6 @@ export interface ThemeFn { } type GlossInternals = { - depth: number parent: any targetElement: any themeFns: ThemeFn[] | null @@ -139,18 +138,21 @@ export function gloss< // in this case assume were 1 depth because it accepts gloss className typeof target !== 'string' ? 1 : 0 + const targetElement = hasGlossyParent ? target.internal.targetElement : target - const glossProps = getGlossProps(glossPropsObject ?? null, hasGlossyParent ? target : null, depth) + const glossProps = getGlossProps(glossPropsObject ?? null, hasGlossyParent ? target : null) const config = glossProps.config - const getEl = config?.getElement const staticClassNames = glossProps.staticClasses?.join(' ') ?? '' + + // calc before render const ignoreAttrs = glossProps.defaultProps?.ignoreAttrs ?? (hasGlossyParent && target.ignoreAttrs) ?? baseIgnoreAttrs - // static compilation information - const { compiledClassName, conditionalClassNames } = getCompiledClasses(target, compiledInfo || null, depth) + const getEl = config?.getElement + // static compilation information // just add conditional classnames right away, they are small + const { compiledClassName, conditionalClassNames } = getCompiledClasses(target, compiledInfo || null, depth) for (const key in glossProps.conditionalStyles) { - const names = addStyles(glossProps.conditionalStyles[key], depth + 1) + const names = addStyles(glossProps.conditionalStyles[key]) if (names) { conditionalClassNames[key] = names.join(' ') } @@ -175,7 +177,7 @@ export function gloss< // debug if (isDeveloping && glossPropsObject?.['debug']) { - console.warn('gloss info', { glossProps, depth }) + console.warn('gloss info', { glossProps }) } /** @@ -203,10 +205,7 @@ export function gloss< const last = useRef<{ props: Object; theme: CompiledTheme }>() let shouldAvoidStyleUpdate = false if (!last.current) { - last.current = { - props, - theme, - } + last.current = { props, theme } } else { // ensure update on theme change if (last.current.theme !== theme) { @@ -243,21 +242,19 @@ export function gloss< // set up final props with filtering for various attributes let finalProps: any = {} - let avoidStyles = false if (config?.shouldAvoidProcessingStyles) { avoidStyles = config.shouldAvoidProcessingStyles(props) } - const dynStyles = addDynamicStyles( + const dynClassNames = addDynamicStyles( ThemedView.displayName, dynClasses.current, - depth, theme as any, themeFns, avoidStyles, ) - dynClasses.current = curDynClassNames + dynClasses.current = dynClassNames const isDOMElement = typeof element === 'string' || (config ? config.isDOMElement : false) @@ -265,8 +262,8 @@ export function gloss< if (props.className) { className += ` ${props.className}` } - if (curDynClassNames.length) { - className += ' ' + curDynClassNames.join(' ') + if (dynClassNames.length) { + className += ' ' + dynClassNames.join(' ') } if (compiledClassName) { className += compiledClassName @@ -302,13 +299,7 @@ export function gloss< // hook: setting your own props const postProcessProps = config && config.postProcessProps if (postProcessProps) { - // TODO could hoist this cb fn - postProcessProps(props, finalProps, () => { - return { - ...glossProps.styles?.['.'], - ...dynStyles['.'], - } - }) + postProcessProps(props, finalProps, () => getStylesForClassNames(className)) } if (isDeveloping && shouldDebug) { @@ -334,7 +325,6 @@ export function gloss< themeFns: null, parent, targetElement, - depth, getConfig: () => ({ displayName: ThemedView.displayName || '', themeFns, @@ -359,6 +349,12 @@ export function gloss< return ThemedView as any } +function getStylesForClassNames(_className: string) { + // TODO + console.log('todo') + return {} +} + function createGlossView(GlossView, config) { const { isEqual, shouldUpdateMap } = createGlossIsEqual() // @ts-ignore @@ -377,10 +373,9 @@ function createGlossView(GlossView, config) { // takes a style object, adds it to stylesheet, returns classnames function addStyles( styles: any, - depth: number, displayName?: string, + curClassNames?: string[], prevClassNames?: string[] | null, - selectorPrefix?: string ) { const namespaces = getSortedNamespaces(styles) let allClassNames: string[] | null = null @@ -389,10 +384,20 @@ function addStyles( // they may return falsy, conditional '&:hover': active ? hoverStyle : null if (!style) continue - // add the stylesheets and classNames - // TODO could do a simple "diff" so that fast-changing styles only change the "changing" props - // it would likely help things like when you animate based on mousemove, may be slower in default case - const classNames = addRules(displayName, style, ns, depth || 0, selectorPrefix, true) + const nextClassNames = addRules(displayName, style, ns, true) + + // TODO optimize/refactor, just getting working + let classNames: string[] = [] + if (curClassNames) { + for (const cn of nextClassNames) { + const prefix = getPrefix(cn) + if (curClassNames.some(x => x.indexOf(prefix) === 0) === false) { + classNames.push(cn) + } + } + } else { + classNames = nextClassNames + } if (classNames.length) { allClassNames = allClassNames || [] @@ -438,42 +443,26 @@ function deregisterClassName(name: string) { gc.deregisterClassUse(name.slice(2)) } -let curDynClassNames: string[] = [] function addDynamicStyles( displayName: string = 'g', prevClassNames: string[] | null, - depth: number, props: GlossThemeProps, themeFns?: ThemeFn[][] | null, avoidStyles?: boolean, ) { - const dynStyles = {} - curDynClassNames = [] - - if (!avoidStyles) { - if (props && themeFns) { - const len = themeFns.length - 1 - for (const [index, themeFnList] of themeFns.entries()) { - const themeDepth = depth - (len - index) - const themeStyles = getStylesFromThemeFns(themeFnList, props) - // TODO is this bad perf? now that we always create an object for themes - // the next block would always execute, but there are times themes do nothing - // not sure its worth checking keys here but it avoids object creation in the block - // i really wish js added shit like themeStyles.keysLength or something - if (Object.keys(themeStyles).length) { - dynStyles['.'] = dynStyles['.'] || {} - // make an object for each level of theme - const curThemeObj = { ['.']: {} } - mergeStyles('.', curThemeObj, themeStyles, true) - // TODO console.log this see if we can optimize - Object.assign(dynStyles['.'], curThemeObj['.']) - // `html ` prefix makes it slightly stronger than the glossProp styles - const dynClassNames = addStyles(curThemeObj, themeDepth, displayName, prevClassNames, 'html ') - if (dynClassNames) { - for (const cn of dynClassNames) { - curDynClassNames.push(cn) - } - } + let dynClassNames: string[] = [] + + if (!avoidStyles && props && themeFns) { + for (const themeFnList of themeFns) { + const themeStyles = getStylesFromThemeFns(themeFnList, props) + if (Object.keys(themeStyles).length) { + // make an object for each level of theme + const curThemeObj = { ['.']: {} } + mergeStyles('.', curThemeObj, themeStyles, false) + console.log('applying theme', curThemeObj) + const next = addStyles(curThemeObj, displayName, dynClassNames, prevClassNames) + if (next) { + dynClassNames = [...next, dynClassNames] } } } @@ -482,13 +471,13 @@ function addDynamicStyles( // de-register removed classNames if (prevClassNames) { for (const className of prevClassNames) { - if (!curDynClassNames.includes(className)) { + if (dynClassNames.indexOf(className) === -1) { deregisterClassName(className) } } } - return dynStyles + return dynClassNames } const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' @@ -588,7 +577,7 @@ function mergeStyles( // happens once at initial gloss() call, so not as perf intense // get all parent styles and merge them into a big object // const staticClasses: string[] | null = addStyles(glossProps.styles, depth) -export function getGlossProps(allProps: GlossProps | null, parent: GlossView | null, depth: number): GlossParsedProps { +export function getGlossProps(allProps: GlossProps | null, parent: GlossView | null): GlossParsedProps { const { config = null, ...rest } = allProps || {} const styles = { '.': {}, @@ -619,10 +608,11 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } // merge together the parent chain of static classes - const curStaticClasses = addStyles(styles, depth) || [] + const curStaticClasses = addStyles(styles) || [] const parentStaticClasses = parent?.internal?.glossProps.staticClasses || [] + const staticClasses = getUniqueStylesByClassName([...curStaticClasses, ...parentStaticClasses]) return { - staticClasses: [...parentStaticClasses, ...curStaticClasses], + staticClasses, config: compileConfig(config, parent), styles, conditionalStyles, @@ -631,6 +621,29 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } +// const styleInfo = { +// borderLeftRadius: 'bLR-123213123' +// } + +// const X = gloss({ background: 'red' }) +// const X2 = gloss(X, { background: 'yellow' }) +// const X3 = gloss(X2).theme(() => ({ background: 'green' })) +// const X4 = gloss(X3, { background: 'orange' }) + +const getPrefix = (cn: string) => cn.slice(0, cn.indexOf('-')) + +function getUniqueStylesByClassName(classNames: string[]) { + const res: string[] = [] + const usedPrefix = {} + for (const cn of classNames) { + const prefix = getPrefix(cn) + if (usedPrefix[prefix]) continue + usedPrefix[prefix] = true + res.push(cn) + } + return res +} + // excludes gloss internal props and leaves style/html props function getGlossDefaultProps(props: any) { const x = {} @@ -716,9 +729,6 @@ function compileThemes(viewOG: GlossView) { cur = conf.parent } - // reverse so we have [grandparent, parent, cur] - all.reverse() - // we need to make sure theme priority is either above or below the static styles, depending. // so if: // const Parent = gloss({ background: 'red' }).theme(changeBg) @@ -731,14 +741,14 @@ function compileThemes(viewOG: GlossView) { // we ensure that the hoisted will always go at the top, as well as ensuring the depth/priority // is kept as it should be if (all.length && !hasOwnTheme) { - all.push([]) + all.unshift([]) } // hoisted always go onto starting of the cur theme if (hoisted.length) { - all[all.length - 1] = [ + all[0] = [ ...hoisted, - ...all[all.length - 1] + ...all[0] ] } @@ -779,8 +789,6 @@ function addRules( displayName = '_', rules: BaseRules, namespace: string, - _depth: number, - _selectorPrefix?: string, insert?: A, ): string[] { const classNames: string[] = [] @@ -908,7 +916,7 @@ export type StaticStyleDesc = { ns: string } -function getAllStyles(props: any, depth = 0, ns = '.') { +function getAllStyles(props: any, _depth = 0, ns = '.') { if (!props) { return [] } @@ -919,7 +927,7 @@ function getAllStyles(props: any, depth = 0, ns = '.') { for (const ns of namespaces) { const styleObj = allStyles[ns] if (!styleObj) continue - const info = addRules('', styleObj, ns, depth, '', false) + const info = addRules('', styleObj, ns, false) if (info) { // @ts-ignore styles.push({ ns, ...info, }) @@ -964,7 +972,7 @@ function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, e const len = themeFns.length - 1 const theme = createThemeProxy(userTheme, trackState, props) for (const [index, themeFnList] of themeFns.entries()) { - const themeDepth = depth - (len - index) + // const themeDepth = depth - (len - index) const styles = getStylesFromThemeFns(themeFnList, theme) if (Object.keys(styles).length) { // make an object for each level of theme @@ -974,7 +982,7 @@ function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, e for (const ns of namespaces) { const styleObj = curThemeObj[ns] if (!styleObj) continue - const info = addRules('', styleObj, ns, themeDepth, 'html ', false) + const info = addRules('', styleObj, ns, false) if (info) { // @ts-ignore themeStyles.push({ ns, ...info, }) diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index 14a467becd..17e301f524 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,51 +1,59 @@ -// //! -// import { ProvideUI, View, Stack } from '@o/ui' -// // -// const React = require('react') -// const { themes } = require('./themes') -// const height = eval(`1`) -// export const SiteRoot = () => { -// return ( -// -// -// {}} -// flexDirection="row" -// alignItems="center" -// position="relative" -// justifyContent="center" -// /> -// -// ) -// } -import { ErrorBoundary, ProvideUI } from '@o/ui' -import React, { StrictMode, Suspense } from 'react' -import { Router, View } from 'react-navi' - -import { Layout } from './Layout' -import { Navigation } from './Navigation' -import { SiteStoreContext } from './SiteStore' -import { themes } from './themes' +//! +import { Paragraph, ProvideUI } from '@o/ui' +import { gloss } from 'gloss' +const P2 = gloss(Paragraph, { + color: 'red', + fontSize: 25, +}) +// +const React = require('react') +const { themes } = require('./themes') export const SiteRoot = () => { return ( - - - - {/* this key helps HMR for lazy imports... but it breaks scroll position */} - - - - - - - - - + + + 123 + ) } +// import { ErrorBoundary, ProvideUI } from '@o/ui' +// import React, { StrictMode, Suspense } from 'react' +// import { Router, View } from 'react-navi' + +// import { Layout } from './Layout' +// import { Navigation } from './Navigation' +// import { SiteStoreContext } from './SiteStore' +// import { themes } from './themes' + +// export const SiteRoot = () => { +// return ( +// +// +// +// {/* this key helps HMR for lazy imports... but it breaks scroll position */} +// +// +// +// +// +// +// +// +// +// +// ) +// } diff --git a/projects/site/src/pages/HomePage/FeaturesSection.tsx b/projects/site/src/pages/HomePage/FeaturesSection.tsx index 748f58f8e5..1799b63de6 100644 --- a/projects/site/src/pages/HomePage/FeaturesSection.tsx +++ b/projects/site/src/pages/HomePage/FeaturesSection.tsx @@ -73,7 +73,7 @@ export default memo(function FeaturesSection() { > The all-in-one
- app workspace + workspace @@ -227,7 +227,7 @@ const sections = { { title: `One-click data sources`, icon: `data`, - body: [`Every app provides data, installs with a click.`], + body: [`Apps provide data, install with a click.`], }, { title: 'Query Builder', From 38505b46b14d6e49ce58e60fcf07b9921a465ab8 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 13:09:09 -0800 Subject: [PATCH 11/31] save --- packages/css/src/cssPropertySet.ts | 2 +- packages/gloss/src/gloss.tsx | 172 ++++++++++++++++++++--------- 2 files changed, 119 insertions(+), 55 deletions(-) diff --git a/packages/css/src/cssPropertySet.ts b/packages/css/src/cssPropertySet.ts index db0a0f2d04..b99faaf24a 100644 --- a/packages/css/src/cssPropertySet.ts +++ b/packages/css/src/cssPropertySet.ts @@ -1068,7 +1068,7 @@ export type GenerateCSSPropertySet
= { position?: CSSPropertyVal quotes?: CSSPropertyVal resize?: CSSPropertyVal - rest?: CSSPropertyVal + props?: CSSPropertyVal restAfter?: CSSPropertyVal restBefore?: CSSPropertyVal right?: CSSPropertyVal diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 688b22e410..9b06dd5026 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -247,13 +247,19 @@ export function gloss< avoidStyles = config.shouldAvoidProcessingStyles(props) } - const dynClassNames = addDynamicStyles( - ThemedView.displayName, - dynClasses.current, - theme as any, - themeFns, - avoidStyles, - ) + if (themeFns) { + console.log('glossProps', glossProps.styles) + console.log('what up doe', getClassNames(theme, themeFns, [], themeFns.length)) + } + + const dynClassNames = [] + // const dynClassNames = addDynamicStyles( + // ThemedView.displayName, + // dynClasses.current, + // theme as any, + // themeFns, + // avoidStyles, + // ) dynClasses.current = dynClassNames const isDOMElement = typeof element === 'string' || (config ? config.isDOMElement : false) @@ -384,7 +390,8 @@ function addStyles( // they may return falsy, conditional '&:hover': active ? hoverStyle : null if (!style) continue - const nextClassNames = addRules(displayName, style, ns, true) + const next = addRules(displayName, style, ns, true) + const nextClassNames = Object.values(next) as any // TODO optimize/refactor, just getting working let classNames: string[] = [] @@ -443,42 +450,42 @@ function deregisterClassName(name: string) { gc.deregisterClassUse(name.slice(2)) } -function addDynamicStyles( - displayName: string = 'g', - prevClassNames: string[] | null, - props: GlossThemeProps, - themeFns?: ThemeFn[][] | null, - avoidStyles?: boolean, -) { - let dynClassNames: string[] = [] - - if (!avoidStyles && props && themeFns) { - for (const themeFnList of themeFns) { - const themeStyles = getStylesFromThemeFns(themeFnList, props) - if (Object.keys(themeStyles).length) { - // make an object for each level of theme - const curThemeObj = { ['.']: {} } - mergeStyles('.', curThemeObj, themeStyles, false) - console.log('applying theme', curThemeObj) - const next = addStyles(curThemeObj, displayName, dynClassNames, prevClassNames) - if (next) { - dynClassNames = [...next, dynClassNames] - } - } - } - } - - // de-register removed classNames - if (prevClassNames) { - for (const className of prevClassNames) { - if (dynClassNames.indexOf(className) === -1) { - deregisterClassName(className) - } - } - } - - return dynClassNames -} +// function addDynamicStyles( +// displayName: string = 'g', +// prevClassNames: string[] | null, +// props: GlossThemeProps, +// themeFns?: ThemeFn[][] | null, +// avoidStyles?: boolean, +// ) { +// let dynClassNames: string[] = [] + +// if (!avoidStyles && props && themeFns) { +// for (const themeFnList of themeFns) { +// const themeStyles = getStylesFromThemeFns(themeFnList, props) +// if (Object.keys(themeStyles).length) { +// // make an object for each level of theme +// const curThemeObj = { ['.']: {} } +// mergeStyles('.', curThemeObj, themeStyles, false) +// console.log('applying theme', curThemeObj) +// const next = addStyles(curThemeObj, displayName, dynClassNames, prevClassNames) +// if (next) { +// dynClassNames = [...next, dynClassNames] +// } +// } +// } +// } + +// // de-register removed classNames +// if (prevClassNames) { +// for (const className of prevClassNames) { +// if (dynClassNames.indexOf(className) === -1) { +// deregisterClassName(className) +// } +// } +// } + +// return dynClassNames +// } const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' @@ -507,7 +514,7 @@ function mergeStyles( } if (key === 'conditional') { // propStyles - // definition: gloss({ isTall: { height: '100%' } }) + // definition: gloss({ conditional: { isTall: { height: '100%' } } }) // usage: for (const pKey in nextStyles[key]) { let subStyles = nextStyles[key][pKey] @@ -578,13 +585,13 @@ function mergeStyles( // get all parent styles and merge them into a big object // const staticClasses: string[] | null = addStyles(glossProps.styles, depth) export function getGlossProps(allProps: GlossProps | null, parent: GlossView | null): GlossParsedProps { - const { config = null, ...rest } = allProps || {} + const { config = null, ...glossProp } = allProps || {} const styles = { '.': {}, } - // all the "rest" go onto default props + // all the "glossProp" go onto default props let defaultProps: any = getGlossDefaultProps(allProps) - let conditionalStyles = mergeStyles('.', styles, rest, false, defaultProps) ?? null + let conditionalStyles = mergeStyles('.', styles, glossProp, false, defaultProps) ?? null const internalDefaultProps = defaultProps // merge parent config if (parent?.internal) { @@ -626,9 +633,66 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n // } // const X = gloss({ background: 'red' }) +// { background: 'bg-1231321' } // const X2 = gloss(X, { background: 'yellow' }) +// { background: 'bg-1231312' } // const X3 = gloss(X2).theme(() => ({ background: 'green' })) +// [{ background: 'green' }] // const X4 = gloss(X3, { background: 'orange' }) +// + +/** + * themes: [[themeCurrent, ...themeHoisted], x, x, x] + * statics: [{ background: 'bg-123123213' }, undefined, undefined, { background: '12312321' }] + * + * const className = getClassNames(props, themes, statics, depth) + * + */ + +type ClassNames = { + [key: string]: string | ClassNames +} + +function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[], depth: number) { + const classNames = {} + for (let i = 0; i < depth; i++) { + const themeStyles = themes[i] && getStylesFromThemeFns(themes[i], props) + const staticStyles = styles[i] + if (themeStyles) { + for (const key in themeStyles) { + mergeStyle(key, themeStyles[key], classNames) + } + } + if (staticStyles) { + for (const key in staticStyles) { + mergeStyle(key, themeStyles[key], classNames) + } + } + } + return Object.values(classNames) +} + +function mergeStyle(key: string, val: any, existing: ClassNames) { + // check for validity + if (validCSSAttr[key]) { + // dont overwrite as we go down in importance + if (existing[key]) return + addRule(key, val, '.', existing, true, '') + return + } + // will be captured next in isSubStyle + if (pseudoProps[key]) { + key = pseudoProps[key] + } + if (isSubStyle(key)) { + existing[key] = existing[key] || {} + for (const skey in val[key]) { + if (existing[key][skey]) continue + addRule(skey, val, key, existing[key] as ClassNames, true, '') + } + return + } +} const getPrefix = (cn: string) => cn.slice(0, cn.indexOf('-')) @@ -790,8 +854,8 @@ function addRules( rules: BaseRules, namespace: string, insert?: A, -): string[] { - const classNames: string[] = [] +): ClassNames { + const classNames: ClassNames = {} for (let key in rules) { if (!cssAttributeAbbreviations[key]) { // console.warn('what the key', key) @@ -813,7 +877,7 @@ function addRules( // return { css, className: finalClassName } } -function addRule(key: string, val: string, namespace: string, classNames: string[], insert: any, displayName: string) { +function addRule(key: string, val: string, namespace: string, classNames: ClassNames, insert: any, displayName: string) { const abbrev = cssAttributeAbbreviations[key] key = CAMEL_TO_SNAKE[key] || key const isMediaQuery = namespace[0] === '@' @@ -828,7 +892,7 @@ function addRule(key: string, val: string, namespace: string, classNames: string } const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` if (className !== undefined) { - classNames.push(className) + classNames[key] = className if (insert === true) { // this is the first time we've found this className if (!tracker.has(className)) { @@ -952,7 +1016,7 @@ export type ThemeStyleInfo = { themeStyles: StaticStyleDesc[] | null } -function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, extraDepth = 0): ThemeStyleInfo { +function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, _extraDepth = 0): ThemeStyleInfo { const themeFns = compileThemes(view) if (!themeFns) { return { @@ -967,7 +1031,7 @@ function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, e usedProps: new Set() } // themes always one above, extraDepth if theres a local view - const depth = view.internal.depth + extraDepth + // const depth = view.internal.depth + extraDepth const themeStyles: StaticStyleDesc[] = [] const len = themeFns.length - 1 const theme = createThemeProxy(userTheme, trackState, props) From 2ad5d91d884cabe10346966d6fcc6a0374ad62ab Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 13:18:56 -0800 Subject: [PATCH 12/31] save --- packages/gloss/src/gloss.tsx | 126 ++++-------------------- packages/gloss/src/staticStyleUtils.tsx | 101 +++++++++++++++++++ 2 files changed, 122 insertions(+), 105 deletions(-) create mode 100644 packages/gloss/src/staticStyleUtils.tsx diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 9b06dd5026..d35cdb11b6 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -9,10 +9,9 @@ import { styleKeysSort } from './styleKeysSort' import { GarbageCollector, StyleTracker } from './stylesheet/gc' import { StyleSheet } from './stylesheet/sheet' import { CompiledTheme } from './theme/createTheme' -import { createThemeProxy } from './theme/createThemeProxy' import { pseudoProps } from './theme/pseudos' import { themeVariableManager } from './theme/ThemeVariableManager' -import { ThemeTrackState, UnwrapThemeSymbol, useTheme } from './theme/useTheme' +import { UnwrapThemeSymbol, useTheme } from './theme/useTheme' import { defaultTheme } from './themes/defaultTheme' import { GlossProps, GlossPropsPartial, GlossThemeProps, GlossViewConfig } from './types' @@ -71,6 +70,7 @@ type GlossInternals = { type GlossParsedProps = { staticClasses: string[] | null + statics: { [key: string]: ClassNames }[] config: GlossViewConfig | null defaultProps: Partial | null internalDefaultProps: any @@ -248,7 +248,7 @@ export function gloss< } if (themeFns) { - console.log('glossProps', glossProps.styles) + console.log('glossProps', glossProps) console.log('what up doe', getClassNames(theme, themeFns, [], themeFns.length)) } @@ -425,7 +425,7 @@ function addStyles( } // sort pseudos into priority -const getSortedNamespaces = (styles: any) => { +export const getSortedNamespaces = (styles: any) => { const keys = Object.keys(styles) if (keys.length > 1) { keys.sort(styleKeysSort) @@ -497,7 +497,7 @@ const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' // BUT its also used nested! See themeFn => mergePropStyles // likely can be refactored, but just need to study it a bit before you do // -function mergeStyles( +export function mergeStyles( id: string, baseStyles: Object, nextStyles?: CSSPropertySet | null | void, @@ -581,6 +581,15 @@ function mergeStyles( return propStyles } +function stylesToClassNames(stylesByNs: any) { + const statics: { [key: string]: ClassNames } = {} + for (const ns of stylesByNs) { + const styles = stylesByNs[ns] + statics[ns] = addRules('', styles, ns, false) + } + return statics +} + // happens once at initial gloss() call, so not as perf intense // get all parent styles and merge them into a big object // const staticClasses: string[] | null = addStyles(glossProps.styles, depth) @@ -592,6 +601,9 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n // all the "glossProp" go onto default props let defaultProps: any = getGlossDefaultProps(allProps) let conditionalStyles = mergeStyles('.', styles, glossProp, false, defaultProps) ?? null + + const statics = stylesToClassNames(styles) + const internalDefaultProps = defaultProps // merge parent config if (parent?.internal) { @@ -620,6 +632,7 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n const staticClasses = getUniqueStylesByClassName([...curStaticClasses, ...parentStaticClasses]) return { staticClasses, + statics, config: compileConfig(config, parent), styles, conditionalStyles, @@ -759,7 +772,7 @@ function compileConfig( } // compile theme from parents -function compileThemes(viewOG: GlossView) { +export function compileThemes(viewOG: GlossView) { let cur = viewOG const hasOwnTheme = cur.internal.themeFns @@ -825,7 +838,7 @@ function compileThemes(viewOG: GlossView) { return themes } -function getStylesFromThemeFns(themeFns: ThemeFn[], themeProps: Object) { +export function getStylesFromThemeFns(themeFns: ThemeFn[], themeProps: Object) { let styles: CSSPropertySetLoose = {} for (const themeFn of themeFns) { const next = themeFn(themeProps as any, styles) @@ -849,7 +862,7 @@ const cssOpts = { // css: string, // className: string // }) | null -function addRules( +export function addRules( displayName = '_', rules: BaseRules, namespace: string, @@ -964,101 +977,4 @@ const replaceDepth = (className: string, depth: number) => { return className[0] === 'g' && +className[1] == +className[1] ? `g${depth}${className.slice(2)}` : className } -/** - * START external static style block (TODO move out to own thing) - * - * keeping it here for now because dont want to add more fns in sensitive loops above - * this is a really hacky area right now as im just trying to figure out the right way - * to do all of this, once it settles into something working we can set up some tests, - * some performance checks, and then hopefully dedupe this code with the code above + - * split it out and make it all a lot more clearly named/structured. - */ - -export type StaticStyleDesc = { - css: string, - className: string; - ns: string -} - -function getAllStyles(props: any, _depth = 0, ns = '.') { - if (!props) { - return [] - } - const allStyles = { [ns]: {} } - mergeStyles(ns, allStyles, props) - const styles: StaticStyleDesc[] = [] - const namespaces = getSortedNamespaces(allStyles) - for (const ns of namespaces) { - const styleObj = allStyles[ns] - if (!styleObj) continue - const info = addRules('', styleObj, ns, false) - if (info) { - // @ts-ignore - styles.push({ ns, ...info, }) - } - } - return styles -} - -/** - * For use externally only (static style extract) - */ -function getStyles(props: any, depth = 0, ns = '.') { - return getAllStyles(props, depth, ns)[0] ?? null -} - -/** - * For use externally only (static style extract) - * see addDynamicStyles equivalent - */ -export type ThemeStyleInfo = { - trackState: ThemeTrackState | null, - themeStyles: StaticStyleDesc[] | null -} -function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, _extraDepth = 0): ThemeStyleInfo { - const themeFns = compileThemes(view) - if (!themeFns) { - return { - themeStyles: null, - trackState: null, - } - } - const trackState: ThemeTrackState = { - theme: userTheme, - hasUsedOnlyCSSVariables: true, - nonCSSVariables: new Set(), - usedProps: new Set() - } - // themes always one above, extraDepth if theres a local view - // const depth = view.internal.depth + extraDepth - const themeStyles: StaticStyleDesc[] = [] - const len = themeFns.length - 1 - const theme = createThemeProxy(userTheme, trackState, props) - for (const [index, themeFnList] of themeFns.entries()) { - // const themeDepth = depth - (len - index) - const styles = getStylesFromThemeFns(themeFnList, theme) - if (Object.keys(styles).length) { - // make an object for each level of theme - const curThemeObj = { ['.']: {} } - mergeStyles('.', curThemeObj, styles, true) - const namespaces = getSortedNamespaces(curThemeObj) - for (const ns of namespaces) { - const styleObj = curThemeObj[ns] - if (!styleObj) continue - const info = addRules('', styleObj, ns, false) - if (info) { - // @ts-ignore - themeStyles.push({ ns, ...info, }) - } - } - } - } - return { themeStyles, trackState } -} - -export const StaticUtils = { getAllStyles, getStyles, getThemeStyles } - -/** - * END external static style block - */ diff --git a/packages/gloss/src/staticStyleUtils.tsx b/packages/gloss/src/staticStyleUtils.tsx new file mode 100644 index 0000000000..95b0d6531d --- /dev/null +++ b/packages/gloss/src/staticStyleUtils.tsx @@ -0,0 +1,101 @@ +import { addRules, compileThemes, getSortedNamespaces, getStylesFromThemeFns, GlossView, mergeStyles } from './gloss' +import { CompiledTheme } from './theme/createTheme' +import { createThemeProxy } from './theme/createThemeProxy' +import { ThemeTrackState } from './theme/useTheme' + +/** + * START external static style block (TODO move out to own thing) + * + * keeping it here for now because dont want to add more fns in sensitive loops above + * this is a really hacky area right now as im just trying to figure out the right way + * to do all of this, once it settles into something working we can set up some tests, + * some performance checks, and then hopefully dedupe this code with the code above + + * split it out and make it all a lot more clearly named/structured. + */ + + export type StaticStyleDesc = { + css: string + className: string + ns: string +} + +function getAllStyles(props: any, _depth = 0, ns = '.') { + if (!props) { + return [] + } + const allStyles = { [ns]: {} } + mergeStyles(ns, allStyles, props) + const styles: StaticStyleDesc[] = [] + const namespaces = getSortedNamespaces(allStyles) + for (const ns of namespaces) { + const styleObj = allStyles[ns] + if (!styleObj) + continue + const info = addRules('', styleObj, ns, false) + if (info) { + // @ts-ignore + styles.push({ ns, ...info, }) + } + } + return styles +} + +/** + * For use externally only (static style extract) + */ +function getStyles(props: any, depth = 0, ns = '.') { + return getAllStyles(props, depth, ns)[0] ?? null +} + +/** + * For use externally only (static style extract) + * see addDynamicStyles equivalent + */ +export type ThemeStyleInfo = { + trackState: ThemeTrackState | null + themeStyles: StaticStyleDesc[] | null +} + +function getThemeStyles(view: GlossView, userTheme: CompiledTheme, props: any, _extraDepth = 0): ThemeStyleInfo { + const themeFns = compileThemes(view) + if (!themeFns) { + return { + themeStyles: null, + trackState: null, + } + } + const trackState: ThemeTrackState = { + theme: userTheme, + hasUsedOnlyCSSVariables: true, + nonCSSVariables: new Set(), + usedProps: new Set() + } + // themes always one above, extraDepth if theres a local view + // const depth = view.internal.depth + extraDepth + const themeStyles: StaticStyleDesc[] = [] + const len = themeFns.length - 1 + const theme = createThemeProxy(userTheme, trackState, props) + for (const [index, themeFnList] of themeFns.entries()) { + // const themeDepth = depth - (len - index) + const styles = getStylesFromThemeFns(themeFnList, theme) + if (Object.keys(styles).length) { + // make an object for each level of theme + const curThemeObj = { ['.']: {} } + mergeStyles('.', curThemeObj, styles, true) + const namespaces = getSortedNamespaces(curThemeObj) + for (const ns of namespaces) { + const styleObj = curThemeObj[ns] + if (!styleObj) + continue + const info = addRules('', styleObj, ns, false) + if (info) { + // @ts-ignore + themeStyles.push({ ns, ...info, }) + } + } + } + } + return { themeStyles, trackState } +} + +export const StaticUtils = { getAllStyles, getStyles, getThemeStyles } From f12638b566a60994905167b2be0455ff307606b9 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 13:20:18 -0800 Subject: [PATCH 13/31] save --- packages/gloss/src/gloss.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index d35cdb11b6..30ca61a086 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -582,8 +582,9 @@ export function mergeStyles( } function stylesToClassNames(stylesByNs: any) { + if (!stylesByNs) return null const statics: { [key: string]: ClassNames } = {} - for (const ns of stylesByNs) { + for (const ns in stylesByNs) { const styles = stylesByNs[ns] statics[ns] = addRules('', styles, ns, false) } From ad69db8d85f037d505d501c8953e9c459631a5d7 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 14:15:38 -0800 Subject: [PATCH 14/31] save --- packages/gloss/src/gloss.tsx | 133 ++++++++++++++++----------------- projects/site/src/SiteRoot.tsx | 6 ++ 2 files changed, 70 insertions(+), 69 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 30ca61a086..a2377073b2 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -70,12 +70,11 @@ type GlossInternals = { type GlossParsedProps = { staticClasses: string[] | null - statics: { [key: string]: ClassNames }[] + statics: ({ [key: string]: ClassNames } | null)[] config: GlossViewConfig | null defaultProps: Partial | null internalDefaultProps: any styles: Object | null - conditionalStyles: Object | null } export type GlossStaticStyleDescription = { @@ -151,12 +150,14 @@ export function gloss< // static compilation information // just add conditional classnames right away, they are small const { compiledClassName, conditionalClassNames } = getCompiledClasses(target, compiledInfo || null, depth) - for (const key in glossProps.conditionalStyles) { - const names = addStyles(glossProps.conditionalStyles[key]) - if (names) { - conditionalClassNames[key] = names.join(' ') - } - } + + //! + // for (const key in glossProps.conditionalStyles) { + // const names = addStyles(glossProps.conditionalStyles[key]) + // if (names) { + // conditionalClassNames[key] = names.join(' ') + // } + // } // put the "rest" of non-styles onto defaultProps GlossView.defaultProps = glossProps.defaultProps @@ -489,70 +490,41 @@ function deregisterClassName(name: string) { const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' -// -// this... THIS... -// ... this is a tricky function -// because its used on initial mount AND during renders -// which is actually useful, yes, because you want the logic the same -// BUT its also used nested! See themeFn => mergePropStyles -// likely can be refactored, but just need to study it a bit before you do -// export function mergeStyles( id: string, - baseStyles: Object, + styles: Object, nextStyles?: CSSPropertySet | null | void, overwrite?: boolean, rest?: Object -): Object | undefined { +): { [key: string]: any } | undefined { if (!nextStyles) return - // this is just for the conditional prop styles - let propStyles for (const key in nextStyles) { // dont overwrite as we go down - if (overwrite !== true && baseStyles[id][key] !== undefined) { + if (overwrite !== true && styles[id] && styles[id][key] !== undefined) { continue } if (key === 'conditional') { - // propStyles - // definition: gloss({ conditional: { isTall: { height: '100%' } } }) - // usage: - for (const pKey in nextStyles[key]) { - let subStyles = nextStyles[key][pKey] - propStyles = propStyles || {} - propStyles[pKey] = {} - // they can nest (media queries/psuedo), split it out, eg: - for (const sKey in subStyles) { - // key = isTall - // sKey = &:before - if (isSubStyle(sKey)) { - // keep all sub-styles on their key - propStyles[pKey] = propStyles[pKey] || {} - propStyles[pKey][sKey] = subStyles[sKey] - } else { - // we put base styles here, see 'base' check above - propStyles[pKey]['.'] = propStyles[pKey]['.'] || {} - propStyles[pKey]['.'][sKey] = subStyles[sKey] - } - } - } + styles.conditional = styles.conditional || {} + styles.conditional = getConditionalStyles(nextStyles[key]) continue } if (validCSSAttr[key]) { // valid regular attr - baseStyles[id][key] = nextStyles[key] + styles[id] = styles[id] || {} + styles[id][key] = nextStyles[key] } else if (isSubStyle(key)) { for (const sKey in nextStyles[key]) { - if (overwrite === true || !baseStyles[key] || baseStyles[key][sKey] === undefined) { - baseStyles[key] = baseStyles[key] || {} - baseStyles[key][sKey] = nextStyles[key][sKey] + if (overwrite === true || !styles[key] || styles[key][sKey] === undefined) { + styles[key] = styles[key] || {} + styles[key][sKey] = nextStyles[key][sKey] } } } else { const pseudoKey = pseudoProps[key] if (pseudoKey) { // merge in case they defined it two different ways - baseStyles[pseudoKey] = baseStyles[pseudoKey] || {} - Object.assign(baseStyles[pseudoKey], nextStyles[key]) + styles[pseudoKey] = styles[pseudoKey] || {} + Object.assign(styles[pseudoKey], nextStyles[key]) continue } @@ -564,8 +536,8 @@ export function mergeStyles( const mediaSelector = Config.mediaQueries[mediaName] if (mediaSelector) { const styleKey = key.slice(index + 1) - baseStyles[mediaSelector] = baseStyles[mediaSelector] || {} - baseStyles[mediaSelector][styleKey] = nextStyles[key] + styles[mediaSelector] = styles[mediaSelector] || {} + styles[mediaSelector][styleKey] = nextStyles[key] continue } } @@ -577,7 +549,32 @@ export function mergeStyles( } } } +} +// conditional +// const Component = gloss({ conditional: { isTall: { height: '100%' } } }) +// usage: +function getConditionalStyles(conditionalStyles: Object) { + let propStyles + for (const pKey in conditionalStyles) { + const subStyles = conditionalStyles[pKey] + propStyles = propStyles || {} + propStyles[pKey] = {} + // they can nest (media queries/psuedo), split it out, eg: + for (const sKey in subStyles) { + // key = isTall + // sKey = &:before + if (isSubStyle(sKey)) { + // keep all sub-styles on their key + propStyles[pKey] = propStyles[pKey] || {} + propStyles[pKey][sKey] = subStyles[sKey] + } else { + // we put base styles here, see 'base' check above + propStyles[pKey]['.'] = propStyles[pKey]['.'] || {} + propStyles[pKey]['.'][sKey] = subStyles[sKey] + } + } + } return propStyles } @@ -586,7 +583,16 @@ function stylesToClassNames(stylesByNs: any) { const statics: { [key: string]: ClassNames } = {} for (const ns in stylesByNs) { const styles = stylesByNs[ns] - statics[ns] = addRules('', styles, ns, false) + if (ns === 'conditional') { + for (const condition in styles) { + const next = stylesToClassNames(styles[condition]) + if (next) { + statics[condition] = next + } + } + } else { + statics[ns] = addRules('', styles, ns, false) + } } return statics } @@ -596,14 +602,15 @@ function stylesToClassNames(stylesByNs: any) { // const staticClasses: string[] | null = addStyles(glossProps.styles, depth) export function getGlossProps(allProps: GlossProps | null, parent: GlossView | null): GlossParsedProps { const { config = null, ...glossProp } = allProps || {} - const styles = { - '.': {}, - } // all the "glossProp" go onto default props let defaultProps: any = getGlossDefaultProps(allProps) - let conditionalStyles = mergeStyles('.', styles, glossProp, false, defaultProps) ?? null - const statics = stylesToClassNames(styles) + const styles = {} + mergeStyles('.', styles, glossProp, true, defaultProps) + console.log('styles', styles, glossProp?.conditional) + const hasStyles = Object.keys(styles).length + const staticStyleDesc = hasStyles ? stylesToClassNames(styles) : null + const statics = [staticStyleDesc, ...(parent?.internal.glossProps.statics ?? [])] const internalDefaultProps = defaultProps // merge parent config @@ -615,17 +622,6 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n ...defaultProps, } } - const parentPropStyles = parentGlossProps.conditionalStyles - if (parentPropStyles) { - for (const key in parentPropStyles) { - conditionalStyles = conditionalStyles || {} - conditionalStyles[key] = conditionalStyles[key] || {} - conditionalStyles[key] = { - ...parentPropStyles[key], - ...conditionalStyles[key], - } - } - } } // merge together the parent chain of static classes const curStaticClasses = addStyles(styles) || [] @@ -636,7 +632,6 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n statics, config: compileConfig(config, parent), styles, - conditionalStyles, defaultProps, internalDefaultProps, } diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index 17e301f524..509d55807a 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -5,6 +5,11 @@ import { gloss } from 'gloss' const P2 = gloss(Paragraph, { color: 'red', fontSize: 25, + conditional: { + bigger: { + fontSize: 200, + }, + }, }) // const React = require('react') @@ -24,6 +29,7 @@ export const SiteRoot = () => { > 123 + 123 ) } From 5cd16538f43a4ce3c2eaa14b1915b3e48b27641b Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 14:26:10 -0800 Subject: [PATCH 15/31] save --- packages/gloss/src/gloss.tsx | 44 ++++++++++++++---------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index a2377073b2..7366aa9a97 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -254,33 +254,12 @@ export function gloss< } const dynClassNames = [] - // const dynClassNames = addDynamicStyles( - // ThemedView.displayName, - // dynClasses.current, - // theme as any, - // themeFns, - // avoidStyles, - // ) dynClasses.current = dynClassNames const isDOMElement = typeof element === 'string' || (config ? config.isDOMElement : false) - let className = staticClassNames - if (props.className) { - className += ` ${props.className}` - } - if (dynClassNames.length) { - className += ' ' + dynClassNames.join(' ') - } - if (compiledClassName) { - className += compiledClassName - } - for (const key in props) { - if (props[key] === true && conditionalClassNames[key]) { - className += ` ${conditionalClassNames[key]} ` - continue - } + if (conditionalClassNames[key]) continue if (isDOMElement) { if (ignoreAttrs[key]) continue // TODO: need to figure out this use case: when a valid prop attr, but invalid val @@ -297,7 +276,17 @@ export function gloss< } } - finalProps.className = className + finalProps.className = getClassNames(theme, themeFns, glossProps.statics) + // let className = staticClassNames + // if (props.className) { + // className += ` ${props.className}` + // } + // if (dynClassNames.length) { + // className += ' ' + dynClassNames.join(' ') + // } + // if (compiledClassName) { + // className += compiledClassName + // } if (isDeveloping) { finalProps['data-is'] = finalProps['data-is'] || ThemedView.displayName @@ -492,11 +481,11 @@ const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' export function mergeStyles( id: string, - styles: Object, + styles: { [key: string]: any }, nextStyles?: CSSPropertySet | null | void, overwrite?: boolean, rest?: Object -): { [key: string]: any } | undefined { +): Object | undefined { if (!nextStyles) return for (const key in nextStyles) { // dont overwrite as we go down @@ -607,7 +596,6 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n const styles = {} mergeStyles('.', styles, glossProp, true, defaultProps) - console.log('styles', styles, glossProp?.conditional) const hasStyles = Object.keys(styles).length const staticStyleDesc = hasStyles ? stylesToClassNames(styles) : null const statics = [staticStyleDesc, ...(parent?.internal.glossProps.statics ?? [])] @@ -662,8 +650,9 @@ type ClassNames = { [key: string]: string | ClassNames } -function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[], depth: number) { +function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[]) { const classNames = {} + const depth = styles.length for (let i = 0; i < depth; i++) { const themeStyles = themes[i] && getStylesFromThemeFns(themes[i], props) const staticStyles = styles[i] @@ -678,6 +667,7 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[], de } } } + console.log('ends with', classNames) return Object.values(classNames) } From 46f34b0b19f5ffb546c2963bdbe71994edd05b21 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 15:37:06 -0800 Subject: [PATCH 16/31] save --- packages/gloss/src/blocks/Flex.tsx | 6 +- packages/gloss/src/gloss.tsx | 61 +++++++++----------- packages/gloss/src/themes/alphaColorTheme.ts | 3 +- packages/ui/src/text/textSizeTheme.ts | 5 +- 4 files changed, 33 insertions(+), 42 deletions(-) diff --git a/packages/gloss/src/blocks/Flex.tsx b/packages/gloss/src/blocks/Flex.tsx index b927ea63c6..c2f797696a 100644 --- a/packages/gloss/src/blocks/Flex.tsx +++ b/packages/gloss/src/blocks/Flex.tsx @@ -9,8 +9,8 @@ export type FlexProps = GlossProps export const Flex = gloss(Box, { ignoreAttrs: baseIgnoreAttrs, }).theme( - // css props - propsToStyles, - // for text opacity + // for text opacity alphaColorTheme, + // all css props + propsToStyles, ) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 7366aa9a97..4d6d7e5234 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -68,9 +68,11 @@ type GlossInternals = { } } +type ClassNamesByNs = ({ [key: string]: ClassNames } | null) + type GlossParsedProps = { staticClasses: string[] | null - statics: ({ [key: string]: ClassNames } | null)[] + statics: ClassNamesByNs[] config: GlossViewConfig | null defaultProps: Partial | null internalDefaultProps: any @@ -248,11 +250,6 @@ export function gloss< avoidStyles = config.shouldAvoidProcessingStyles(props) } - if (themeFns) { - console.log('glossProps', glossProps) - console.log('what up doe', getClassNames(theme, themeFns, [], themeFns.length)) - } - const dynClassNames = [] dynClasses.current = dynClassNames @@ -276,7 +273,9 @@ export function gloss< } } - finalProps.className = getClassNames(theme, themeFns, glossProps.statics) + const classNames = getClassNames(theme, themeFns || [], glossProps.statics) + console.log('got em', glossProps, classNames) + finalProps.className = classNames.join(' ') // let className = staticClassNames // if (props.className) { // className += ` ${props.className}` @@ -295,7 +294,7 @@ export function gloss< // hook: setting your own props const postProcessProps = config && config.postProcessProps if (postProcessProps) { - postProcessProps(props, finalProps, () => getStylesForClassNames(className)) + postProcessProps(props, finalProps, () => getStylesForClassNames(classNames)) } if (isDeveloping && shouldDebug) { @@ -345,7 +344,7 @@ export function gloss< return ThemedView as any } -function getStylesForClassNames(_className: string) { +function getStylesForClassNames(_classNames: string[]) { // TODO console.log('todo') return {} @@ -567,14 +566,14 @@ function getConditionalStyles(conditionalStyles: Object) { return propStyles } -function stylesToClassNames(stylesByNs: any) { +function stylesToClassNamesByNS(stylesByNs: any) { if (!stylesByNs) return null const statics: { [key: string]: ClassNames } = {} for (const ns in stylesByNs) { const styles = stylesByNs[ns] if (ns === 'conditional') { for (const condition in styles) { - const next = stylesToClassNames(styles[condition]) + const next = stylesToClassNamesByNS(styles[condition]) if (next) { statics[condition] = next } @@ -597,7 +596,7 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n const styles = {} mergeStyles('.', styles, glossProp, true, defaultProps) const hasStyles = Object.keys(styles).length - const staticStyleDesc = hasStyles ? stylesToClassNames(styles) : null + const staticStyleDesc = hasStyles ? stylesToClassNamesByNS(styles) : null const statics = [staticStyleDesc, ...(parent?.internal.glossProps.statics ?? [])] const internalDefaultProps = defaultProps @@ -650,12 +649,13 @@ type ClassNames = { [key: string]: string | ClassNames } -function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[]) { +function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[]): string[] { const classNames = {} const depth = styles.length for (let i = 0; i < depth; i++) { const themeStyles = themes[i] && getStylesFromThemeFns(themes[i], props) const staticStyles = styles[i] + console.log('depth', i, props['alpha'], themeStyles, themes[i], staticStyles) if (themeStyles) { for (const key in themeStyles) { mergeStyle(key, themeStyles[key], classNames) @@ -663,20 +663,19 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNames[]) { } if (staticStyles) { for (const key in staticStyles) { - mergeStyle(key, themeStyles[key], classNames) + mergeStyle(key, staticStyles[key], classNames) } } } - console.log('ends with', classNames) return Object.values(classNames) } -function mergeStyle(key: string, val: any, existing: ClassNames) { +function mergeStyle(key: string, val: any, classNames: ClassNames) { // check for validity if (validCSSAttr[key]) { // dont overwrite as we go down in importance - if (existing[key]) return - addRule(key, val, '.', existing, true, '') + if (classNames[key]) return + addRule(key, cssValue(key, val, false, cssOpts), '.', classNames, true, '') return } // will be captured next in isSubStyle @@ -684,10 +683,11 @@ function mergeStyle(key: string, val: any, existing: ClassNames) { key = pseudoProps[key] } if (isSubStyle(key)) { - existing[key] = existing[key] || {} + classNames[key] = classNames[key] || {} + console.log('what is', val) for (const skey in val[key]) { - if (existing[key][skey]) continue - addRule(skey, val, key, existing[key] as ClassNames, true, '') + if (classNames[key][skey]) continue + addRule(skey, cssValue(skey, val[key][skey], false, cssOpts), key, classNames[key] as ClassNames, true, '') } return } @@ -792,18 +792,7 @@ export function compileThemes(viewOG: GlossView) { cur = conf.parent } - // we need to make sure theme priority is either above or below the static styles, depending. - // so if: - // const Parent = gloss({ background: 'red' }).theme(changeBg) - // changeBg *should* override background - // but if: - // const Child = gloss(Parent, { background: 'green' }) - // background will *always* be green - // ALSO if changeBg.hoistTheme = true, we need to be sure its hoisted all the way up - // by putting an empty theme at the front if there are child themes, but no current theme, - // we ensure that the hoisted will always go at the top, as well as ensuring the depth/priority - // is kept as it should be - if (all.length && !hasOwnTheme) { + if (!hasOwnTheme) { all.unshift([]) } @@ -811,7 +800,7 @@ export function compileThemes(viewOG: GlossView) { if (hoisted.length) { all[0] = [ ...hoisted, - ...all[0] + ...(all[0] || []), ] } @@ -830,7 +819,9 @@ export function getStylesFromThemeFns(themeFns: ThemeFn[], themeProps: Object) { const next = themeFn(themeProps as any, styles) if (next) { styles = styles || {} - Object.assign(styles, next) + for (const key in next) { + styles[key] = styles[key] || next[key] + } } } return styles diff --git a/packages/gloss/src/themes/alphaColorTheme.ts b/packages/gloss/src/themes/alphaColorTheme.ts index 7154b50f68..c556ddbebd 100644 --- a/packages/gloss/src/themes/alphaColorTheme.ts +++ b/packages/gloss/src/themes/alphaColorTheme.ts @@ -45,7 +45,6 @@ export const alphaColorTheme: ThemeFn = (props, previous) => { if (color) { if (props.applyThemeColor) { if ( - typeof color !== 'string' && color.originalInput !== 'inherit' && typeof alpha === 'number' ) { @@ -74,6 +73,8 @@ export const alphaColorTheme: ThemeFn = (props, previous) => { } } +alphaColorTheme.hoistTheme = true + function merge( pseudoKey: string, colorKey: string, diff --git a/packages/ui/src/text/textSizeTheme.ts b/packages/ui/src/text/textSizeTheme.ts index c41f758a52..99be3f67c3 100644 --- a/packages/ui/src/text/textSizeTheme.ts +++ b/packages/ui/src/text/textSizeTheme.ts @@ -1,4 +1,5 @@ import { isDefined } from '@o/utils' +import { ThemeFn } from 'gloss' import { hasMediaQueries, mediaQueryKeysSize } from '../mediaQueryKeys' import { getTextSize } from '../Sizes' @@ -15,9 +16,8 @@ export type TextSizeProps = { marginBottom?: any } -export function textSizeTheme(props: TextSizeProps) { +export const textSizeTheme: ThemeFn = (props) => { const res = getTextSizeTheme(props) - // media query size // TODO this whole loop needs rethinking if (hasMediaQueries) { @@ -37,7 +37,6 @@ export function textSizeTheme(props: TextSizeProps) { } } } - return res } From b42263e5972cfbcbd924013f60f8752fce017c1f Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 15:38:15 -0800 Subject: [PATCH 17/31] save --- projects/site/src/SiteRoot.tsx | 117 ++++++++++++++++----------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index 509d55807a..92a17ab192 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,65 +1,64 @@ -//! -import { Paragraph, ProvideUI } from '@o/ui' -import { gloss } from 'gloss' +// //! +// import { Paragraph, ProvideUI } from '@o/ui' +// import { gloss } from 'gloss' +// const P2 = gloss(Paragraph, { +// color: 'red', +// fontSize: 25, +// conditional: { +// bigger: { +// fontSize: 200, +// }, +// }, +// }) +// // +// const React = require('react') +// const { themes } = require('./themes') +// export const SiteRoot = () => { +// return ( +// +// +// 123 +// +// 123 +// +// ) +// } +import { ErrorBoundary, ProvideUI } from '@o/ui' +import React, { StrictMode, Suspense } from 'react' +import { Router, View } from 'react-navi' + +import { Layout } from './Layout' +import { Navigation } from './Navigation' +import { SiteStoreContext } from './SiteStore' +import { themes } from './themes' -const P2 = gloss(Paragraph, { - color: 'red', - fontSize: 25, - conditional: { - bigger: { - fontSize: 200, - }, - }, -}) -// -const React = require('react') -const { themes } = require('./themes') export const SiteRoot = () => { return ( - - - 123 - - 123 + + + + {/* this key helps HMR for lazy imports... but it breaks scroll position */} + + + + + + + + + ) } -// import { ErrorBoundary, ProvideUI } from '@o/ui' -// import React, { StrictMode, Suspense } from 'react' -// import { Router, View } from 'react-navi' - -// import { Layout } from './Layout' -// import { Navigation } from './Navigation' -// import { SiteStoreContext } from './SiteStore' -// import { themes } from './themes' - -// export const SiteRoot = () => { -// return ( -// -// -// -// {/* this key helps HMR for lazy imports... but it breaks scroll position */} -// -// -// -// -// -// -// -// -// -// -// ) -// } From df3c2021a7938fba7323876dbe6d307ad188ceb2 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 15:47:43 -0800 Subject: [PATCH 18/31] save --- packages/gloss/src/gloss.tsx | 142 +----------------- packages/gloss/src/helpers/compileConfig.ts | 41 +++++ packages/gloss/src/helpers/compileThemes.tsx | 53 +++++++ .../src/{ => helpers}/createGlossIsEqual.tsx | 0 .../gloss/src/{ => helpers}/styleKeysSort.tsx | 2 +- .../src/helpers/{WeakKeys.ts => weakKey.ts} | 0 packages/gloss/src/theme/selectThemeSubset.ts | 2 +- projects/site/src/SiteRoot.tsx | 103 ++++++------- 8 files changed, 146 insertions(+), 197 deletions(-) create mode 100644 packages/gloss/src/helpers/compileConfig.ts create mode 100644 packages/gloss/src/helpers/compileThemes.tsx rename packages/gloss/src/{ => helpers}/createGlossIsEqual.tsx (100%) rename packages/gloss/src/{ => helpers}/styleKeysSort.tsx (96%) rename packages/gloss/src/helpers/{WeakKeys.ts => weakKey.ts} (100%) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 4d6d7e5234..4fda422ed6 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -3,9 +3,11 @@ import React from 'react' import { createElement, isValidElement, memo, useEffect, useRef } from 'react' import { Config } from './configureGloss' -import { createGlossIsEqual } from './createGlossIsEqual' +import { compileConfig } from './helpers/compileConfig' +import { compileThemes } from './helpers/compileThemes' +import { createGlossIsEqual } from './helpers/createGlossIsEqual' +import { styleKeysSort } from './helpers/styleKeysSort' import { validPropLoose, ValidProps } from './helpers/validProp' -import { styleKeysSort } from './styleKeysSort' import { GarbageCollector, StyleTracker } from './stylesheet/gc' import { StyleSheet } from './stylesheet/sheet' import { CompiledTheme } from './theme/createTheme' @@ -436,46 +438,9 @@ function mergePropStyles(styles: Object, propStyles: Object, props: Object) { function deregisterClassName(name: string) { // slice 2 to remove specifity - gc.deregisterClassUse(name.slice(2)) + gc.deregisterClassUse(name) } -// function addDynamicStyles( -// displayName: string = 'g', -// prevClassNames: string[] | null, -// props: GlossThemeProps, -// themeFns?: ThemeFn[][] | null, -// avoidStyles?: boolean, -// ) { -// let dynClassNames: string[] = [] - -// if (!avoidStyles && props && themeFns) { -// for (const themeFnList of themeFns) { -// const themeStyles = getStylesFromThemeFns(themeFnList, props) -// if (Object.keys(themeStyles).length) { -// // make an object for each level of theme -// const curThemeObj = { ['.']: {} } -// mergeStyles('.', curThemeObj, themeStyles, false) -// console.log('applying theme', curThemeObj) -// const next = addStyles(curThemeObj, displayName, dynClassNames, prevClassNames) -// if (next) { -// dynClassNames = [...next, dynClassNames] -// } -// } -// } -// } - -// // de-register removed classNames -// if (prevClassNames) { -// for (const className of prevClassNames) { -// if (dynClassNames.indexOf(className) === -1) { -// deregisterClassName(className) -// } -// } -// } - -// return dynClassNames -// } - const isSubStyle = (x: string) => x[0] === '&' || x[0] === '@' export function mergeStyles( @@ -718,101 +683,6 @@ function getGlossDefaultProps(props: any) { return x } -/** - * We need to compile a few things to get the config right: - * 1. get all the parents postProcessProps until: - * 2. encounter a parent with getElement (and use that isDOMElement) - * 3. stop there, don't keep going higher - */ -function compileConfig( - config: GlossViewConfig | null, - parent: GlossView | null, -): GlossViewConfig { - const compiledConf: GlossViewConfig = { ...config } - let cur = parent - while (cur?.internal) { - const parentConf = cur.internal.glossProps.config - if (parentConf) { - if (parentConf.postProcessProps) { - // merge the postProcessProps - const og = compiledConf.postProcessProps - if (parentConf.postProcessProps !== og) { - compiledConf.postProcessProps = og - ? (a, b, c) => { - og(a, b, c) - parentConf.postProcessProps!(a, b, c) - } - : parentConf.postProcessProps - } - } - // find the first getElement and break here - if (parentConf.getElement) { - compiledConf.getElement = parentConf.getElement - compiledConf.isDOMElement = parentConf.isDOMElement - break - } - } - cur = cur.internal.parent - } - return compiledConf -} - -// compile theme from parents -export function compileThemes(viewOG: GlossView) { - let cur = viewOG - const hasOwnTheme = cur.internal.themeFns - - // this is a list of a list of theme functions - // we run theme functions from parents before, working down to ours - // the parent ones have a lower priority, so we want them first - const added = new Set() - let all: ThemeFn[][] = [] - const hoisted: ThemeFn[] = [] - - // get themes in order from most important (current) to least important (grandparent) - while (cur) { - const conf = cur.internal - if (conf.themeFns) { - let curThemes: ThemeFn[] = [] - for (const fn of conf.themeFns) { - if (added.has(fn)) { - continue // prevent duplicates in parents - } - added.add(fn) - if (fn.hoistTheme) { - hoisted.push(fn) - } else { - curThemes.push(fn) - } - } - if (curThemes.length) { - all.push(curThemes) - } - } - cur = conf.parent - } - - if (!hasOwnTheme) { - all.unshift([]) - } - - // hoisted always go onto starting of the cur theme - if (hoisted.length) { - all[0] = [ - ...hoisted, - ...(all[0] || []), - ] - } - - const themes = all.filter(Boolean) - - if (!themes.length) { - return null - } - - return themes -} - export function getStylesFromThemeFns(themeFns: ThemeFn[], themeProps: Object) { let styles: CSSPropertySetLoose = {} for (const themeFn of themeFns) { @@ -953,5 +823,3 @@ function getCompiledClasses(parent: GlossView | any, compiledInfo: GlossStaticSt const replaceDepth = (className: string, depth: number) => { return className[0] === 'g' && +className[1] == +className[1] ? `g${depth}${className.slice(2)}` : className } - - diff --git a/packages/gloss/src/helpers/compileConfig.ts b/packages/gloss/src/helpers/compileConfig.ts new file mode 100644 index 0000000000..3bd12876c6 --- /dev/null +++ b/packages/gloss/src/helpers/compileConfig.ts @@ -0,0 +1,41 @@ +import { GlossView } from '../gloss' +import { GlossViewConfig } from '../types' + +/** + * We need to compile a few things to get the config right: + * 1. get all the parents postProcessProps until: + * 2. encounter a parent with getElement (and use that isDOMElement) + * 3. stop there, don't keep going higher + */ +function compileConfig( + config: GlossViewConfig | null, + parent: GlossView | null, +): GlossViewConfig { + const compiledConf: GlossViewConfig = { ...config } + let cur = parent + while (cur?.internal) { + const parentConf = cur.internal.glossProps.config + if (parentConf) { + if (parentConf.postProcessProps) { + // merge the postProcessProps + const og = compiledConf.postProcessProps + if (parentConf.postProcessProps !== og) { + compiledConf.postProcessProps = og + ? (a, b, c) => { + og(a, b, c) + parentConf.postProcessProps!(a, b, c) + } + : parentConf.postProcessProps + } + } + // find the first getElement and break here + if (parentConf.getElement) { + compiledConf.getElement = parentConf.getElement + compiledConf.isDOMElement = parentConf.isDOMElement + break + } + } + cur = cur.internal.parent + } + return compiledConf +} diff --git a/packages/gloss/src/helpers/compileThemes.tsx b/packages/gloss/src/helpers/compileThemes.tsx new file mode 100644 index 0000000000..733706e21d --- /dev/null +++ b/packages/gloss/src/helpers/compileThemes.tsx @@ -0,0 +1,53 @@ +import { GlossView, ThemeFn } from '../gloss' + +// compile theme from parents +export function compileThemes(viewOG: GlossView) { + let cur = viewOG + const hasOwnTheme = cur.internal.themeFns + + // this is a list of a list of theme functions + // we run theme functions from parents before, working down to ours + // the parent ones have a lower priority, so we want them first + const added = new Set() + let all: ThemeFn[][] = [] + const hoisted: ThemeFn[] = [] + + // get themes in order from most important (current) to least important (grandparent) + while (cur) { + const conf = cur.internal + if (conf.themeFns) { + let curThemes: ThemeFn[] = [] + for (const fn of conf.themeFns) { + if (added.has(fn)) { + continue // prevent duplicates in parents + } + added.add(fn) + if (fn.hoistTheme) { + hoisted.push(fn) + } else { + curThemes.push(fn) + } + } + if (curThemes.length) { + all.push(curThemes) + } + } + cur = conf.parent + } + + if (!hasOwnTheme) { + all.unshift([]) + } + + // hoisted always go onto starting of the cur theme + if (hoisted.length) { + all[0] = [...hoisted, ...(all[0] || [])] + } + + const themes = all.filter(Boolean) + if (!themes.length) { + return null + } + + return themes +} diff --git a/packages/gloss/src/createGlossIsEqual.tsx b/packages/gloss/src/helpers/createGlossIsEqual.tsx similarity index 100% rename from packages/gloss/src/createGlossIsEqual.tsx rename to packages/gloss/src/helpers/createGlossIsEqual.tsx diff --git a/packages/gloss/src/styleKeysSort.tsx b/packages/gloss/src/helpers/styleKeysSort.tsx similarity index 96% rename from packages/gloss/src/styleKeysSort.tsx rename to packages/gloss/src/helpers/styleKeysSort.tsx index 8f401326ef..3a5b01ecb7 100644 --- a/packages/gloss/src/styleKeysSort.tsx +++ b/packages/gloss/src/helpers/styleKeysSort.tsx @@ -1,4 +1,4 @@ -import { Config } from './configureGloss' +import { Config } from '../configureGloss' /** * Sorts styles so the pseudo keys go in their logical override diff --git a/packages/gloss/src/helpers/WeakKeys.ts b/packages/gloss/src/helpers/weakKey.ts similarity index 100% rename from packages/gloss/src/helpers/WeakKeys.ts rename to packages/gloss/src/helpers/weakKey.ts diff --git a/packages/gloss/src/theme/selectThemeSubset.ts b/packages/gloss/src/theme/selectThemeSubset.ts index 17daacba70..94c277d698 100644 --- a/packages/gloss/src/theme/selectThemeSubset.ts +++ b/packages/gloss/src/theme/selectThemeSubset.ts @@ -1,6 +1,6 @@ import { ThemeObject } from '@o/css' -import { weakKey } from '../helpers/WeakKeys' +import { weakKey } from '../helpers/weakKey' import { CompiledTheme } from './createTheme' import { ThemeSelect } from './Theme' diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index 92a17ab192..bb670116c4 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,64 +1,51 @@ -// //! -// import { Paragraph, ProvideUI } from '@o/ui' -// import { gloss } from 'gloss' -// const P2 = gloss(Paragraph, { -// color: 'red', -// fontSize: 25, -// conditional: { -// bigger: { -// fontSize: 200, -// }, -// }, -// }) -// // -// const React = require('react') -// const { themes } = require('./themes') -// export const SiteRoot = () => { -// return ( -// -// -// 123 -// -// 123 -// -// ) -// } -import { ErrorBoundary, ProvideUI } from '@o/ui' -import React, { StrictMode, Suspense } from 'react' -import { Router, View } from 'react-navi' - -import { Layout } from './Layout' -import { Navigation } from './Navigation' -import { SiteStoreContext } from './SiteStore' -import { themes } from './themes' +//! +import { ProvideUI } from '@o/ui' +import { Box, gloss } from 'gloss' +const LinkRow = gloss(Box, { + flexDirection: 'row', + flex: 1, + alignItems: 'center', + justifyContent: 'center', + zIndex: 1000000000, + position: 'relative', +}) +const React = require('react') +const { themes } = require('./themes') export const SiteRoot = () => { return ( - - - - {/* this key helps HMR for lazy imports... but it breaks scroll position */} - - - - - - - - - + + ok ) } +// import { ErrorBoundary, ProvideUI } from '@o/ui' +// import React, { StrictMode, Suspense } from 'react' +// import { Router, View } from 'react-navi' + +// import { Layout } from './Layout' +// import { Navigation } from './Navigation' +// import { SiteStoreContext } from './SiteStore' +// import { themes } from './themes' + +// export const SiteRoot = () => { +// return ( +// +// +// +// {/* this key helps HMR for lazy imports... but it breaks scroll position */} +// +// +// +// +// +// +// +// +// +// +// ) +// } From 30354c70a56a22bb2eae30ab196780c5a9f78c34 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 15:47:51 -0800 Subject: [PATCH 19/31] save --- packages/gloss/src/helpers/compileConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gloss/src/helpers/compileConfig.ts b/packages/gloss/src/helpers/compileConfig.ts index 3bd12876c6..3e57ec3de9 100644 --- a/packages/gloss/src/helpers/compileConfig.ts +++ b/packages/gloss/src/helpers/compileConfig.ts @@ -7,7 +7,7 @@ import { GlossViewConfig } from '../types' * 2. encounter a parent with getElement (and use that isDOMElement) * 3. stop there, don't keep going higher */ -function compileConfig( +export function compileConfig( config: GlossViewConfig | null, parent: GlossView | null, ): GlossViewConfig { From 78db40fe99c5ea26ecaa2feab3c065ebf751b8e5 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 15:49:13 -0800 Subject: [PATCH 20/31] save --- packages/gloss/src/gloss.tsx | 13 ------------- packages/gloss/src/types.ts | 1 - packages/ui/src/View/View.tsx | 1 - 3 files changed, 15 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 4fda422ed6..6a254b6559 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -145,7 +145,6 @@ export function gloss< const targetElement = hasGlossyParent ? target.internal.targetElement : target const glossProps = getGlossProps(glossPropsObject ?? null, hasGlossyParent ? target : null) const config = glossProps.config - const staticClassNames = glossProps.staticClasses?.join(' ') ?? '' // calc before render const ignoreAttrs = glossProps.defaultProps?.ignoreAttrs ?? (hasGlossyParent && target.ignoreAttrs) ?? baseIgnoreAttrs @@ -155,14 +154,6 @@ export function gloss< // just add conditional classnames right away, they are small const { compiledClassName, conditionalClassNames } = getCompiledClasses(target, compiledInfo || null, depth) - //! - // for (const key in glossProps.conditionalStyles) { - // const names = addStyles(glossProps.conditionalStyles[key]) - // if (names) { - // conditionalClassNames[key] = names.join(' ') - // } - // } - // put the "rest" of non-styles onto defaultProps GlossView.defaultProps = glossProps.defaultProps @@ -247,10 +238,6 @@ export function gloss< // set up final props with filtering for various attributes let finalProps: any = {} - let avoidStyles = false - if (config?.shouldAvoidProcessingStyles) { - avoidStyles = config.shouldAvoidProcessingStyles(props) - } const dynClassNames = [] dynClasses.current = dynClassNames diff --git a/packages/gloss/src/types.ts b/packages/gloss/src/types.ts index 92da0ed9b1..13828c4b97 100644 --- a/packages/gloss/src/types.ts +++ b/packages/gloss/src/types.ts @@ -86,7 +86,6 @@ export type GlossBaseProps = { export type GlossViewConfig = { displayName?: string ignoreAttrs?: { [key: string]: boolean } - shouldAvoidProcessingStyles?: (props: Props) => boolean postProcessProps?: (curProps: Props, nextProps: any, getFinalStyles: () => CSSPropertySet) => any getElement?: (props: Props) => any isDOMElement?: boolean diff --git a/packages/ui/src/View/View.tsx b/packages/ui/src/View/View.tsx index 895b9315e1..8608ad3694 100644 --- a/packages/ui/src/View/View.tsx +++ b/packages/ui/src/View/View.tsx @@ -22,7 +22,6 @@ export const View = gloss(Flex, { }, config: { - // shouldAvoidProcessingStyles: shouldRenderToMotion, postProcessProps(inProps, outProps, getStyles) { if (shouldRenderToMotion(inProps)) { let style = css(getStyles(), { snakeCase: false }) From 09575a4cce3ebd1f4cecbf7ec06d50c686a44596 Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 16:06:33 -0800 Subject: [PATCH 21/31] save --- packages/gloss/src/gloss.tsx | 108 +++---------------- packages/gloss/src/helpers/compileThemes.tsx | 17 +-- 2 files changed, 17 insertions(+), 108 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 6a254b6559..fd3f042aab 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -73,7 +73,6 @@ type GlossInternals = { type ClassNamesByNs = ({ [key: string]: ClassNames } | null) type GlossParsedProps = { - staticClasses: string[] | null statics: ClassNamesByNs[] config: GlossViewConfig | null defaultProps: Partial | null @@ -157,7 +156,7 @@ export function gloss< // put the "rest" of non-styles onto defaultProps GlossView.defaultProps = glossProps.defaultProps - let themeFns: ThemeFn[][] | null = null + let themeFns: ThemeFn[][] = [] let hasCompiled = false let shouldUpdateMap: WeakMap @@ -262,19 +261,9 @@ export function gloss< } } - const classNames = getClassNames(theme, themeFns || [], glossProps.statics) - console.log('got em', glossProps, classNames) + const classNames = getClassNames(theme, themeFns, glossProps.statics) + console.log('got em', classNames, themeFns, glossProps) finalProps.className = classNames.join(' ') - // let className = staticClassNames - // if (props.className) { - // className += ` ${props.className}` - // } - // if (dynClassNames.length) { - // className += ' ' + dynClassNames.join(' ') - // } - // if (compiledClassName) { - // className += compiledClassName - // } if (isDeveloping) { finalProps['data-is'] = finalProps['data-is'] || ThemedView.displayName @@ -354,54 +343,6 @@ function createGlossView(GlossView, config) { return res } -// takes a style object, adds it to stylesheet, returns classnames -function addStyles( - styles: any, - displayName?: string, - curClassNames?: string[], - prevClassNames?: string[] | null, -) { - const namespaces = getSortedNamespaces(styles) - let allClassNames: string[] | null = null - for (const ns of namespaces) { - const style = styles[ns] - // they may return falsy, conditional '&:hover': active ? hoverStyle : null - if (!style) continue - - const next = addRules(displayName, style, ns, true) - const nextClassNames = Object.values(next) as any - - // TODO optimize/refactor, just getting working - let classNames: string[] = [] - if (curClassNames) { - for (const cn of nextClassNames) { - const prefix = getPrefix(cn) - if (curClassNames.some(x => x.indexOf(prefix) === 0) === false) { - classNames.push(cn) - } - } - } else { - classNames = nextClassNames - } - - if (classNames.length) { - allClassNames = allClassNames || [] - // @ts-ignore - allClassNames = [...allClassNames, ...classNames] - // if this is the first mount render or we didn't previously have this class then add it as new - for (const className of classNames) { - if (!prevClassNames || !prevClassNames.includes(className)) { - gc.registerClassUse(className) - } - } - } - } - if (isDeveloping && shouldDebug) { - console.log('addStyles sorted', allClassNames, namespaces, styles) - } - return allClassNames -} - // sort pseudos into priority export const getSortedNamespaces = (styles: any) => { const keys = Object.keys(styles) @@ -539,7 +480,6 @@ function stylesToClassNamesByNS(stylesByNs: any) { // happens once at initial gloss() call, so not as perf intense // get all parent styles and merge them into a big object -// const staticClasses: string[] | null = addStyles(glossProps.styles, depth) export function getGlossProps(allProps: GlossProps | null, parent: GlossView | null): GlossParsedProps { const { config = null, ...glossProp } = allProps || {} // all the "glossProp" go onto default props @@ -562,12 +502,7 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } } - // merge together the parent chain of static classes - const curStaticClasses = addStyles(styles) || [] - const parentStaticClasses = parent?.internal?.glossProps.staticClasses || [] - const staticClasses = getUniqueStylesByClassName([...curStaticClasses, ...parentStaticClasses]) return { - staticClasses, statics, config: compileConfig(config, parent), styles, @@ -576,53 +511,36 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } -// const styleInfo = { -// borderLeftRadius: 'bLR-123213123' -// } - -// const X = gloss({ background: 'red' }) -// { background: 'bg-1231321' } -// const X2 = gloss(X, { background: 'yellow' }) -// { background: 'bg-1231312' } -// const X3 = gloss(X2).theme(() => ({ background: 'green' })) -// [{ background: 'green' }] -// const X4 = gloss(X3, { background: 'orange' }) -// - -/** - * themes: [[themeCurrent, ...themeHoisted], x, x, x] - * statics: [{ background: 'bg-123123213' }, undefined, undefined, { background: '12312321' }] - * - * const className = getClassNames(props, themes, statics, depth) - * - */ - type ClassNames = { [key: string]: string | ClassNames } function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[]): string[] { const classNames = {} - const depth = styles.length + const depth = themes.length for (let i = 0; i < depth; i++) { - const themeStyles = themes[i] && getStylesFromThemeFns(themes[i], props) + const themeStyles = !!themes[i]?.length && getStylesFromThemeFns(themes[i], props) const staticStyles = styles[i] - console.log('depth', i, props['alpha'], themeStyles, themes[i], staticStyles) + console.log('get', themeStyles, staticStyles) if (themeStyles) { for (const key in themeStyles) { mergeStyle(key, themeStyles[key], classNames) } } if (staticStyles) { - for (const key in staticStyles) { - mergeStyle(key, staticStyles[key], classNames) + for (const ns in staticStyles) { + const stylesByNs = staticStyles[ns] + for (const key in stylesByNs) { + mergeStyle(key, stylesByNs[key], classNames, ns) + } } } } return Object.values(classNames) } -function mergeStyle(key: string, val: any, classNames: ClassNames) { +function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = '.') { + console.log('merge then', key, val) // check for validity if (validCSSAttr[key]) { // dont overwrite as we go down in importance diff --git a/packages/gloss/src/helpers/compileThemes.tsx b/packages/gloss/src/helpers/compileThemes.tsx index 733706e21d..ec9032adf6 100644 --- a/packages/gloss/src/helpers/compileThemes.tsx +++ b/packages/gloss/src/helpers/compileThemes.tsx @@ -3,7 +3,6 @@ import { GlossView, ThemeFn } from '../gloss' // compile theme from parents export function compileThemes(viewOG: GlossView) { let cur = viewOG - const hasOwnTheme = cur.internal.themeFns // this is a list of a list of theme functions // we run theme functions from parents before, working down to ours @@ -14,9 +13,10 @@ export function compileThemes(viewOG: GlossView) { // get themes in order from most important (current) to least important (grandparent) while (cur) { + const curThemes: ThemeFn[] = [] + all.push(curThemes) const conf = cur.internal if (conf.themeFns) { - let curThemes: ThemeFn[] = [] for (const fn of conf.themeFns) { if (added.has(fn)) { continue // prevent duplicates in parents @@ -35,19 +35,10 @@ export function compileThemes(viewOG: GlossView) { cur = conf.parent } - if (!hasOwnTheme) { - all.unshift([]) - } - // hoisted always go onto starting of the cur theme if (hoisted.length) { - all[0] = [...hoisted, ...(all[0] || [])] - } - - const themes = all.filter(Boolean) - if (!themes.length) { - return null + all[0] = [...hoisted, ...all[0]] } - return themes + return all } From f3ae212c06a96c5bafa0cd4e255b311145dc354a Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 16:20:08 -0800 Subject: [PATCH 22/31] save --- packages/gloss/src/gloss.tsx | 15 ++++-- projects/site/src/SiteRoot.tsx | 89 +++++++++++++++++----------------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index fd3f042aab..b49787445d 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -472,7 +472,7 @@ function stylesToClassNamesByNS(stylesByNs: any) { } } } else { - statics[ns] = addRules('', styles, ns, false) + statics[ns] = addRules('', styles, ns, true) } } return statics @@ -502,6 +502,7 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } } + return { statics, config: compileConfig(config, parent), @@ -529,9 +530,15 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] } if (staticStyles) { for (const ns in staticStyles) { - const stylesByNs = staticStyles[ns] - for (const key in stylesByNs) { - mergeStyle(key, stylesByNs[key], classNames, ns) + if (ns !== '.') { + console.log('skipping ns for now until figured out', ns) + continue + } + const styleClasses = staticStyles[ns] + for (const key in styleClasses) { + if (validCSSAttr[key] && !classNames[key]) { + classNames[key] = styleClasses[key] + } } } } diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index bb670116c4..ec07dd87b2 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,51 +1,50 @@ -//! -import { ProvideUI } from '@o/ui' -import { Box, gloss } from 'gloss' +// //! +// import { ProvideUI } from '@o/ui' +// import { Box, gloss } from 'gloss' +// const LinkRow = gloss(Box, { +// flexDirection: 'row', +// flex: 1, +// alignItems: 'center', +// justifyContent: 'center', +// zIndex: 1000000000, +// position: 'relative', +// }) +// const React = require('react') +// const { themes } = require('./themes') +// export const SiteRoot = () => { +// return ( +// +// ok +// +// ) +// } +import { ErrorBoundary, ProvideUI } from '@o/ui' +import React, { StrictMode, Suspense } from 'react' +import { Router, View } from 'react-navi' + +import { Layout } from './Layout' +import { Navigation } from './Navigation' +import { SiteStoreContext } from './SiteStore' +import { themes } from './themes' -const LinkRow = gloss(Box, { - flexDirection: 'row', - flex: 1, - alignItems: 'center', - justifyContent: 'center', - zIndex: 1000000000, - position: 'relative', -}) -const React = require('react') -const { themes } = require('./themes') export const SiteRoot = () => { return ( - - ok + + + + {/* this key helps HMR for lazy imports... but it breaks scroll position */} + + + + + + + + + ) } -// import { ErrorBoundary, ProvideUI } from '@o/ui' -// import React, { StrictMode, Suspense } from 'react' -// import { Router, View } from 'react-navi' - -// import { Layout } from './Layout' -// import { Navigation } from './Navigation' -// import { SiteStoreContext } from './SiteStore' -// import { themes } from './themes' - -// export const SiteRoot = () => { -// return ( -// -// -// -// {/* this key helps HMR for lazy imports... but it breaks scroll position */} -// -// -// -// -// -// -// -// -// -// -// ) -// } From ffec8fcb978de50fcae15c8c678498ee39a5e3aa Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 19:28:21 -0800 Subject: [PATCH 23/31] save --- .../gloss-webpack/src/ast/extractStyles.ts | 2008 ++++++++--------- .../{staticStyleUtils.tsx => StaticUtils.tsx} | 3 +- packages/gloss/src/babel/addDisplayName.ts | 7 +- packages/gloss/src/babel/index.ts | 7 +- packages/gloss/src/gloss.tsx | 1 + packages/gloss/src/index.ts | 1 + projects/site/src/SiteRoot.tsx | 81 +- 7 files changed, 1050 insertions(+), 1058 deletions(-) rename packages/gloss/src/{staticStyleUtils.tsx => StaticUtils.tsx} (95%) diff --git a/packages/gloss-webpack/src/ast/extractStyles.ts b/packages/gloss-webpack/src/ast/extractStyles.ts index 08f4eec037..683f9c0a2c 100644 --- a/packages/gloss-webpack/src/ast/extractStyles.ts +++ b/packages/gloss-webpack/src/ast/extractStyles.ts @@ -1,21 +1,11 @@ import * as babel from '@babel/core' -import generate from '@babel/generator' -import traverse, { NodePath } from '@babel/traverse' import * as t from '@babel/types' -import literalToAst from 'babel-literal-to-ast' -import { getGlossProps, GlossStaticStyleDescription, GlossView, isGlossView, StaticUtils, ThemeStyleInfo, tracker, validCSSAttr } from 'gloss' -import invariant from 'invariant' +import { ThemeStyleInfo } from 'gloss' import path from 'path' -import util from 'util' import vm from 'vm' import { CacheObject, ExtractStylesOptions } from '../types' -import { evaluateAstNode, EvaluateASTNodeOptions } from './evaluateAstNode' -import { extractStaticTernaries, Ternary } from './extractStaticTernaries' -import { getPropValueFromAttributes } from './getPropValueFromAttributes' -import { getStaticBindingsForScope } from './getStaticBindingsForScope' -import { htmlAttributes } from './htmlAttributes' -import { parse, parserOptions } from './parse' +import { parserOptions } from './parse' export interface Options { cacheObject: CacheObject @@ -43,7 +33,7 @@ const GLOSS_SOURCES = { '@o/ui/test': true, } -type CSSExtracted = { filename: string, content: string } +type CSSExtracted = { filename: string; content: string } // used for later seeing how much we can extract (and to apply theme styles) // gives us information if the themes can be extracted via trackState @@ -51,1005 +41,1007 @@ const viewInformation: { [key: string]: ThemeStyleInfo } = {} let hasParsedViewInformation = false export function extractStyles( - src: string | Buffer, - sourceFileName: string, - { outPath, outRelPath }: any, - { cacheObject }: Options, - options: ExtractStylesOptions, + _src: string | Buffer, + _sourceFileName: string, + _pathOpts: any, + _opts: Options, + _options: ExtractStylesOptions, ): { js: string | Buffer css: CSSExtracted[] ast: t.File map: any // RawSourceMap from 'source-map' } { - if (typeof src !== 'string') { - throw new Error('`src` must be a string of javascript') - } - invariant( - typeof sourceFileName === 'string' && path.isAbsolute(sourceFileName), - '`sourceFileName` must be an absolute path to a .js file', - ) - invariant( - typeof cacheObject === 'object' && cacheObject !== null, - '`cacheObject` must be an object', - ) - - if (!hasParsedViewInformation) { - hasParsedViewInformation = true - for (const key in options.views) { - const view = options.views[key] - if (isGlossView(view)) { - const defaultProps = view.internal.glossProps.defaultProps ?? {} - viewInformation[key] = StaticUtils.getThemeStyles(view, options.defaultTheme, defaultProps) - } - } - } - - const mediaQueryPrefixes = options.mediaQueryKeys.map(x => `${x}-`) - const sourceDir = path.dirname(sourceFileName) - - // Using a map for (officially supported) guaranteed insertion order - const cssMap = new Map() - const ast = parse(src) - let glossSrc = false - const validComponents = {} - // default to using require syntax - let useImportSyntax = false - const shouldPrintDebug = src[0] === '/' && src[1] === '/' && src[2] === '!' - const views: { [key: string]: GlossView } = {} - const JSX_VALID_NAMES = Object.keys(options.views).filter(x => { - return options.views[x] && !!options.views[x].staticStyleConfig - }) - - // we allow things within the ui kit to avoid the more tedious config - const isInternal = options.internalViewsPaths?.some(x => sourceFileName.indexOf(x) === 0) ?? false - - let importsGloss = false - - // Find gloss require in program root - ast.program.body = ast.program.body.filter((item: t.Node) => { - if (t.isImportDeclaration(item)) { - // not imported from gloss? byeeee - if (item.source.value === 'gloss') { - importsGloss = true - } - if (!importsGloss && !isInternal && !GLOSS_SOURCES[item.source.value]) { - return true - } - glossSrc = true - useImportSyntax = true - item.specifiers = item.specifiers.filter(specifier => { - // keep the weird stuff - if ( - !t.isImportSpecifier(specifier) || - !t.isIdentifier(specifier.imported) || - !t.isIdentifier(specifier.local) - ) { - return true - } - if (specifier.local.name[0] !== specifier.local.name[0].toUpperCase()) { - return true - } - if (!JSX_VALID_NAMES.includes(specifier.local.name)) { - return true - } - views[specifier.local.name] = options.views[specifier.local.name] - validComponents[specifier.local.name] = true - if (shouldPrintDebug) { - console.log('found valid component', specifier.local.name) - } - // dont remove the import - return true - }) - } - return true - }) - - /** - * Step 1: Compiled the gloss() style views and remember if they are able to be compiled - * in step 2 - */ - const localStaticViews: { - [key: string]: { - staticDesc: GlossStaticStyleDescription, - propObject: any - defaultProps?: Object - parent?: GlossView - } - } = {} - - if (importsGloss || isInternal) { - traverse(ast, { - VariableDeclaration(path) { - const dec = path.node.declarations[0] - if (!dec || !t.isVariableDeclarator(dec) || !t.isIdentifier(dec.id)) return - const name = dec.id.name - - // traverse and find gloss call - let glossCall: t.CallExpression - let chain = dec.init - while ( - t.isCallExpression(chain) && - t.isMemberExpression(chain.callee) && - t.isCallExpression(chain.callee.object) - ) { - chain = chain.callee.object - } - // verify we found it - if ( - t.isCallExpression(chain) && - t.isIdentifier(chain.callee) && - chain.callee.name === 'gloss' - ) { - // simple gloss without .theme etc - glossCall = chain - } - - if (!glossCall || !glossCall.arguments.length) { - return - } - - validComponents[name] = true - - const localViewName = t.isIdentifier(glossCall.arguments[0]) && glossCall.arguments[0].name - let view: GlossView | null = null - - // parse style objects out and return them as array of [{ ['namespace']: 'className' }] - let staticStyleDesc: GlossStaticStyleDescription | null = null - - // this stuff is used by step 2 (theme functions) - // any props leftover after parsing gloss style props - let restDefaultProps = {} - - const evaluate = createEvaluator(path, sourceFileName) - - glossCall.arguments = glossCall.arguments.map((arg, index) => { - if ((index === 0 || index === 1) && t.isObjectExpression(arg)) { - let propObject = {} - let unevaluated: any[] = [] - try { - const opts: EvaluateASTNodeOptions = { - evaluateFunctions: false, - unevaluated: [] - } - propObject = evaluate(arg, opts) - unevaluated = opts.unevaluated - if (shouldPrintDebug) { - console.log('propObject', propObject) - } - } catch (err) { - console.log('Cant parse style object - this is ok, just de-opt', name, '>', localViewName) - if (shouldPrintDebug) { - console.log('err', err) - } - return arg - } - - // if no error, set local view info - if (localViewName) { - // extends one of our optimizable views - if (views[localViewName]) { - views[name] = options.views[localViewName] - view = views[name] - } - } - - // uses the base styles if necessary, merges just like gloss does - const depth = (view?.internal?.depth ?? -1) + 1 - const { styles, conditionalStyles, defaultProps, internalDefaultProps } = getGlossProps( - propObject, - view, - ) - if (shouldPrintDebug) { - console.log('glossCall.arguments parse gloss props', name, styles, conditionalStyles) - } - - // then put them all into an array so gloss later can use that - const out: GlossStaticStyleDescription = { - className: '', - } - - if (defaultProps.className) { - out.className = defaultProps.className - delete defaultProps.className - } - - for (const ns in styles) { - const info = StaticUtils.getStyles(styles[ns], depth, ns) - if (shouldPrintDebug) { - console.log('got static extract', name, ns, info, styles[ns]) - } - if (info) { - cssMap.set(info.className, { css: info.css, commentTexts: [] }) - out.className += ` ${info.className}` - } else { - if (shouldPrintDebug) { - console.log('no info', ns, styles) - } - } - } - - if (conditionalStyles) { - out.conditionalClassNames = {} - for (const prop in conditionalStyles) { - out.conditionalClassNames[prop] = '' - for (const ns in conditionalStyles[prop]) { - const val = conditionalStyles[prop][ns] - const info = StaticUtils.getStyles(val, depth, ns) - if (info) { - cssMap.set(info.className, { css: info.css, commentTexts: [] }) - out.conditionalClassNames[prop] += ` ${info.className}` - } - } - } - } - - localStaticViews[name] = { - staticDesc: out, - propObject, - defaultProps, - parent: view, - } - - if (out.className || out.conditionalClassNames) { - staticStyleDesc = out - } - - // keep any non-style props on the glossProps - let defaultPropsLeave = unevaluated - - if (internalDefaultProps && Object.keys(internalDefaultProps).length) { - const objectLeaveProps = literalToAst(internalDefaultProps) as t.ObjectExpression - defaultPropsLeave = [ - ...defaultPropsLeave, - ...objectLeaveProps.properties - ] - } - - // if we have defaultProps or unevaluated non-extracted items, leave them - if (defaultPropsLeave.length) { - return t.objectExpression(defaultPropsLeave) - } - - return t.nullLiteral() - } - return arg - }) - - // add it to runtime: gloss(View, null, { ...staticStyleDesc }) - if (staticStyleDesc) { - if (glossCall.arguments.length === 1) { - glossCall.arguments.push(t.nullLiteral()) - } - glossCall.arguments.push(literalToAst(staticStyleDesc)) - } - - if (Object.keys(restDefaultProps).length) { - const argIndex = t.isNullLiteral(glossCall.arguments[0]) ? 0 : 1 - console.log('restDefaultProps', restDefaultProps) - glossCall.arguments[argIndex] = literalToAst(restDefaultProps) - } - }, - }) - } - - // gloss isn't included anywhere, so let's bail - if (!glossSrc || !Object.keys(validComponents).length) { - return { - ast, - css: [], - js: src, - map: null, - } - } - - // creates an evaluator to get complex values from babel in this path - function createEvaluator(path: NodePath, sourceFileName: string, defaultOpts?: EvaluateASTNodeOptions) { - // Generate scope object at this level - const staticNamespace = getStaticBindingsForScope( - path.scope, - sourceFileName, - // per-file cache of evaluated bindings - // TODO can be per-module? - {}, - options.whitelistStaticModules, - execFile, - ) - const evalContext = vm.createContext(staticNamespace) - const evalFn = (n: t.Node) => { - // called when evaluateAstNode encounters a dynamic-looking prop - // variable - if (t.isIdentifier(n)) { - invariant( - staticNamespace[n.name], - 'identifier not in staticNamespace', - ) - return staticNamespace[n.name] - } - return vm.runInContext(`(${generate(n).code})`, evalContext) - } - return (n: t.Node, o?: EvaluateASTNodeOptions) => { - return evaluateAstNode(n, evalFn, { ...defaultOpts, ...o }) - } - } - - - /** - * Step 2: Statically extract from JSX < /> nodes - */ - traverse(ast, { - JSXElement: { - enter(traversePath: TraversePath) { - const node = traversePath.node.openingElement - if ( - // skip non-identifier opening elements (member expressions, etc.) - !t.isJSXIdentifier(node.name) || - // skip non-gloss components - !validComponents[node.name.name] - ) { - return - } - - // Remember the source component - const originalNodeName = node.name.name - const localView = localStaticViews[originalNodeName] - let view = views[originalNodeName] - // for parentView config - let extraDepth = 0 - let domNode = 'div' - - let staticStyleConfig: GlossView['staticStyleConfig'] | null = null - if (view) { - staticStyleConfig = view.staticStyleConfig - // lets us have plain functional views like Stack - if (staticStyleConfig.parentView) { - view = staticStyleConfig.parentView - extraDepth = 1 - } - domNode = view.staticStyleConfig?.tagName - ?? view.internal?.glossProps.defaultProps.tagName - ?? 'div' - } - - // Get valid css props - const cssAttributes = staticStyleConfig?.cssAttributes || validCSSAttr - - function isCSSAttribute(name: string) { - if (cssAttributes[name]) return true - if (mediaQueryPrefixes.some(x => name.indexOf(x) === 0)) return true - return false - } - - const attemptEval = evaluateAstNode //createEvaluator(traversePath as any, sourceFileName, { evaluateFunctions: false }) - - let lastSpreadIndex: number = -1 - const flattenedAttributes: (t.JSXAttribute | t.JSXSpreadAttribute)[] = [] - node.attributes.forEach(attr => { - if (t.isJSXSpreadAttribute(attr)) { - try { - const spreadValue = attemptEval(attr.argument) - - if (typeof spreadValue !== 'object' || spreadValue == null) { - lastSpreadIndex = flattenedAttributes.push(attr) - 1 - } else { - for (const k in spreadValue) { - const value = spreadValue[k] - - if (typeof value === 'number') { - flattenedAttributes.push( - t.jsxAttribute( - t.jsxIdentifier(k), - t.jsxExpressionContainer(t.numericLiteral(value)), - ), - ) - } else if (value === null) { - // why would you ever do this - flattenedAttributes.push( - t.jsxAttribute(t.jsxIdentifier(k), t.jsxExpressionContainer(t.nullLiteral())), - ) - } else { - // toString anything else - // TODO: is this a bad idea - flattenedAttributes.push( - t.jsxAttribute( - t.jsxIdentifier(k), - t.jsxExpressionContainer(t.stringLiteral('' + value)), - ), - ) - } - } - } - } catch (e) { - lastSpreadIndex = flattenedAttributes.push(attr) - 1 - } - } else { - flattenedAttributes.push(attr) - } - }) - - node.attributes = flattenedAttributes - - const staticAttributes: Record = {} - const htmlExtractedAttributes = {} - let inlinePropCount = 0 - const staticTernaries: Ternary[] = [] - const classNameObjects: (t.StringLiteral | t.Expression)[] = [] - - let shouldDeopt = false - - const ogAttributes = node.attributes - node.attributes = node.attributes.filter((attribute, idx) => { - if ( - t.isJSXSpreadAttribute(attribute) || - // keep the weirdos - !attribute.name || - // filter out JSXIdentifiers - typeof attribute.name.name !== 'string' || - // haven't hit the last spread operator - idx < lastSpreadIndex - ) { - if (shouldPrintDebug) console.log('inline prop via non normal attr') - inlinePropCount++ - return true - } - - let name = attribute.name.name - - // for fully deoptimizing certain keys - if (staticStyleConfig) { - if (staticStyleConfig.deoptProps?.includes(name)) { - shouldDeopt = true - return true - } - // for avoiding processing certain keys - if (staticStyleConfig.avoidProps?.includes(name)) { - if (shouldPrintDebug) console.log('inline prop via avoidProps') - inlinePropCount++ - return true - } - } - - let value: any = t.isJSXExpressionContainer(attribute?.value) - ? attribute.value.expression - : attribute.value - - // boolean prop / conditionals - const allConditionalClassNames = { - ...view?.internal?.compiledInfo?.conditionalClassNames ?? null, - ...localView?.staticDesc?.conditionalClassNames ?? null, - } - if (allConditionalClassNames[name]) { - // we can just extract to className - if (value === null) { - // extract but still put it onto staticAttributes for themeFn to use - staticAttributes[name] = true - return false - } - - // if dynamic value we just use it on className - classNameObjects.push( - t.conditionalExpression( - value, - t.stringLiteral(allConditionalClassNames[name]), - t.stringLiteral('') - ) - ) - return false - } - - // boolean props have null value - if (!value) { - inlinePropCount++ - return true - } - - // if one or more spread operators are present and we haven't hit the last one yet, the prop stays inline - if (lastSpreadIndex > -1 && idx <= lastSpreadIndex) { - inlinePropCount++ - return true - } - // pass ref, key, and style props through untouched - if (UNTOUCHED_PROPS[name]) { - return true - } - - if (name === 'ref') { - inlinePropCount++ - return true - } - - const trackState = viewInformation[originalNodeName]?.trackState - if (trackState) { - if (trackState?.nonCSSVariables?.has(name)) { - if (shouldPrintDebug) console.log('inline prop via nonCSSVariables') - inlinePropCount++ - return true - } - } - - if (!isCSSAttribute(name)) { - // we can safely leave html attributes - // TODO make this more customizable / per-tagname - if (htmlAttributes[name]) { - try { - htmlExtractedAttributes[name] = attemptEval(value) - } catch(err) { - // oo fancy! this basically says if we can't eval this safely, and its used by the themeFn - // then we need to deopt here. if it can be evaluated, we're good, we'll run theme here later - if (trackState?.usedProps?.has(name)) { - if (shouldPrintDebug) { - console.log('we use this in this component', name) - } - inlinePropCount++ - return true - } - // console.log('err getting html attr', name, err.message) - // ok - } - return true - } - if (shouldPrintDebug) console.log('inline prop via !isCSSAttribute') - inlinePropCount++ - return true - } - - // allow statically defining a change from one prop to another (see Stack) - if (typeof cssAttributes[name] === 'object') { - if (t.isStringLiteral(value)) { - const definition = cssAttributes[name] - name = definition.name - value = definition.value[value.value] - staticAttributes[name] = value - return false - } else { - console.log('couldnt parse a user defined cssAttribute', name, value) - inlinePropCount++ - return true - } - } - - // if value can be evaluated, extract it and filter it out - try { - staticAttributes[name] = attemptEval(value) - return false - } catch { - // ok - } - - if (t.isConditionalExpression(value)) { - // if both sides of the ternary can be evaluated, extract them - try { - const consequent = attemptEval(value.consequent) - const alternate = attemptEval(value.alternate) - staticTernaries.push({ - alternate, - consequent, - name, - test: value.test, - }) - // mark the prop as extracted - staticAttributes[name] = null - return false - } catch (e) { - // - } - } else if (t.isLogicalExpression(value)) { - // convert a simple logical expression to a ternary with a null alternate - if (value.operator === '&&') { - try { - const consequent = attemptEval(value.right) - staticTernaries.push({ - alternate: null, - consequent, - name, - test: value.left, - }) - staticAttributes[name] = null - return false - } catch (e) { - // - } - } - } - - // if we've made it this far, the prop stays inline - if (shouldPrintDebug) console.log('inline prop via no match') - inlinePropCount++ - return true - }) - - if (shouldPrintDebug) { - console.log(`ok we parsed a JSX: -name: ${node.name.name} -inlinePropCount: ${inlinePropCount} -shouldDeopt: ${shouldDeopt} -domNode: ${domNode} - `) - } - - if (shouldDeopt) { - node.attributes = ogAttributes - return - } - - let classNamePropValue: t.Expression | null = null - - const extractedStaticAttrs = Object.keys(staticAttributes).length > 0 - const classNamePropIndex = node.attributes.findIndex( - attr => !t.isJSXSpreadAttribute(attr) && attr.name && attr.name.name === 'className', - ) - if (classNamePropIndex > -1) { - classNamePropValue = getPropValueFromAttributes('className', node.attributes) - node.attributes.splice(classNamePropIndex, 1) - } - - // used later to generate classname for item - const stylesByClassName: { [key: string]: string } = {} - - const depth = (view?.internal?.depth ?? 1) + (localView?.parent?.internal?.depth ?? 1) + extraDepth - const addStyles = (styleObj: any) => { - const allStyles = StaticUtils.getAllStyles(styleObj, depth) - for (const info of allStyles) { - if (info.css) { - if (shouldPrintDebug) { - console.log('add static styles', info.className, info.css) - } - stylesByClassName[info.className] = info.css - } - } - } - - // capture views where they set it afterwards - // plus any defaults passed through gloss - const viewDefaultProps = { - ...view?.defaultProps, - ...view?.internal?.glossProps?.defaultProps, - } - - if (extractedStaticAttrs) { - const staticStyleProps = { - ...viewDefaultProps, - ...localView?.propObject, - ...htmlExtractedAttributes, - ...staticAttributes, - } - if (shouldPrintDebug) { - // ignoreAttrs is usually huge - const { ignoreAttrs, ...rest } = staticStyleProps - console.log('adding static style props', rest) - } - addStyles(staticStyleProps) - } - - // if all style props have been extracted, gloss component can be - // converted to a div or the specified component - if (inlinePropCount === 0) { - // add in any local static classes - if (localView) { - stylesByClassName[localView.staticDesc.className] = null - } - - const themeFns = view?.internal?.getConfig()?.themeFns - if (themeFns) { - // TODO we need to determine if this theme should deopt using the same proxy/tracker as gloss - try { - const props = { - ...viewDefaultProps, - ...localView?.propObject, - ...htmlExtractedAttributes, - ...staticAttributes, - } - const extracted = StaticUtils.getThemeStyles(view, options.defaultTheme, props).themeStyles - if (shouldPrintDebug) { - delete props['ignoreAttrs'] // ignore this its huge in debug output - console.log('extracting from theme', !!localView, props, extracted) - } - for (const x of extracted) { - stylesByClassName[x.className] = x.css - } - } catch(err) { - console.log('error running theme', sourceFileName, err.message) - return - } - } - - // add any default html props to tag - for (const key in viewDefaultProps) { - const val = viewDefaultProps[key] - if (key === 'className') { - classNameObjects.push(t.stringLiteral(val)) - continue - } - if (htmlAttributes[key]) { - // @ts-ignore - if (!node.attributes.some(x => x?.name?.name === key)) { - // add to start so if its spread onto later its overwritten - node.attributes.unshift( - t.jsxAttribute( - t.jsxIdentifier(key), - t.jsxExpressionContainer(literalToAst(val)) - ) - ) - } - } - } - - // add a data-is="Name" so we can debug it more easily - node.attributes.push( - t.jsxAttribute( - t.jsxIdentifier('data-is'), - t.stringLiteral(node.name.name) - ) - ) - - if (localView) { - node.name.name = domNode - } - - // if they set a staticStyleConfig.parentView (see Stack) - if (!isGlossView(view)) { - if (view?.staticStyleConfig.parentView) { - view = view.staticStyleConfig.parentView - } else { - node.name.name = domNode - } - } - - // if gloss view we may be able to optimize - if (isGlossView(view)) { - // local views we already parsed the css out - const localView = localStaticViews[node.name.name] - if (localView) { - // - } else { - const { staticClasses } = view.internal.glossProps - // internal classes - for (const className of staticClasses) { - const item = tracker.get(className.slice(2)) - const css = `${item.selector} { ${item.style} }` - stylesByClassName[className] = css - } - } - node.name.name = domNode - } - - } else { - if (lastSpreadIndex > -1) { - // if only some style props were extracted AND additional props are spread onto the component, - // add the props back with null values to prevent spread props from incorrectly overwriting the extracted prop value - Object.keys(staticAttributes).forEach(attr => { - node.attributes.push( - t.jsxAttribute(t.jsxIdentifier(attr), t.jsxExpressionContainer(t.nullLiteral())), - ) - }) - } - } - - if (traversePath.node.closingElement) { - // this seems strange - if (t.isJSXMemberExpression(traversePath.node.closingElement.name)) return - traversePath.node.closingElement.name.name = node.name.name - } - - if (shouldPrintDebug) { - console.log('stylesByClassName pre ternaries', stylesByClassName) - } - - const extractedStyleClassNames = Object.keys(stylesByClassName).join(' ') - - if (classNamePropValue) { - try { - const evaluatedValue = attemptEval(classNamePropValue) - classNameObjects.push(t.stringLiteral(evaluatedValue)) - } catch (e) { - classNameObjects.push(classNamePropValue) - } - } - - if (staticTernaries.length > 0) { - const ternaryObj = extractStaticTernaries(staticTernaries, cacheObject) - if (shouldPrintDebug) { - console.log('staticTernaries', staticTernaries, '\nternaryObj', ternaryObj) - } - // ternaryObj is null if all of the extracted ternaries have falsey consequents and alternates - if (ternaryObj !== null) { - // add extracted styles by className to existing object - Object.assign(stylesByClassName, ternaryObj.stylesByClassName) - classNameObjects.push(ternaryObj.ternaryExpression) - } - } - - if (extractedStyleClassNames) { - classNameObjects.push(t.stringLiteral(extractedStyleClassNames)) - if (shouldPrintDebug) { - console.log('extractedStyleClassNames', extractedStyleClassNames) - } - } - - const classNamePropValueForReals = classNameObjects.reduce( - (acc, val) => { - if (acc == null) { - if ( - // pass conditional expressions through - t.isConditionalExpression(val) || - // pass non-null literals through - t.isStringLiteral(val) || - t.isNumericLiteral(val) - ) { - return val - } - return t.logicalExpression('||', val, t.stringLiteral('')) - } - - let inner: t.Expression - if (t.isStringLiteral(val)) { - if (t.isStringLiteral(acc)) { - // join adjacent string literals - return t.stringLiteral(`${acc.value} ${val.value}`) - } - inner = t.stringLiteral(` ${val.value}`) - } else if (t.isLiteral(val)) { - inner = t.binaryExpression('+', t.stringLiteral(' '), val) - } else if (t.isConditionalExpression(val) || t.isBinaryExpression(val)) { - if (t.isStringLiteral(acc)) { - return t.binaryExpression('+', t.stringLiteral(`${acc.value} `), val) - } - inner = t.binaryExpression('+', t.stringLiteral(' '), val) - } else if (t.isIdentifier(val) || t.isMemberExpression(val)) { - // identifiers and member expressions make for reasonable ternaries - inner = t.conditionalExpression( - val, - t.binaryExpression('+', t.stringLiteral(' '), val), - t.stringLiteral(''), - ) - } else { - if (t.isStringLiteral(acc)) { - return t.binaryExpression( - '+', - t.stringLiteral(`${acc.value} `), - t.logicalExpression('||', val, t.stringLiteral('')), - ) - } - // use a logical expression for more complex prop values - inner = t.binaryExpression( - '+', - t.stringLiteral(' '), - t.logicalExpression('||', val, t.stringLiteral('')), - ) - } - return t.binaryExpression('+', acc, inner) - }, - null, - ) - - if (shouldPrintDebug) { - console.log('classNamePropValueForReals', classNamePropValueForReals) - } - - if (classNamePropValueForReals) { - if (t.isStringLiteral(classNamePropValueForReals)) { - node.attributes.push( - t.jsxAttribute( - t.jsxIdentifier('className'), - t.stringLiteral(classNamePropValueForReals.value), - ), - ) - } else { - node.attributes.push( - t.jsxAttribute( - t.jsxIdentifier('className'), - t.jsxExpressionContainer(classNamePropValueForReals), - ), - ) - } - } - - const lineNumbers = - node.loc && - node.loc.start.line + - (node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '') - - const comment = util.format( - '/* %s:%s (%s) */', - sourceFileName.replace(process.cwd(), '.'), - lineNumbers, - originalNodeName, - ) - - for (const className in stylesByClassName) { - if (cssMap.has(className)) { - if (comment) { - const val = cssMap.get(className)! - val.commentTexts.push(comment) - cssMap.set(className, val) - } - } else { - const css = stylesByClassName[className] - if (css) { - if (typeof css !== 'string') { - throw new Error(`CSS is not a string, for ${className}: ${JSON.stringify(stylesByClassName, null, 2)}, ${typeof css}`) - } - cssMap.set(className, { css, commentTexts: [comment] }) - } - } - } - }, - exit(traversePath: TraversePath) { - if (traversePath._complexComponentProp) { - if (t.isJSXElement(traversePath.parentPath)) { - // bump - traversePath.parentPath._complexComponentProp = [].concat( - traversePath.parentPath._complexComponentProp || [], - traversePath._complexComponentProp, - ) - } else { - // find nearest Statement - let statementPath = traversePath - do { - statementPath = statementPath.parentPath - } while (!t.isStatement(statementPath)) - - invariant(t.isStatement(statementPath), 'Could not find a statement') - - const decs = t.variableDeclaration('var', [].concat(traversePath._complexComponentProp)) - - statementPath.insertBefore(decs) - } - traversePath._complexComponentProp = null - } - }, - }, - }) - - const css: CSSExtracted[] = [] - - // Write out CSS using it's className, this gives us de-duping for shared classnames - for (const [className, entry] of cssMap.entries()) { - const content = `${entry.commentTexts.map(txt => `${txt}\n`).join('')}${entry.css}` - const name = `${className}__gloss.css` - const importPath = `${outRelPath}/${name}` - const filename = path.join(outPath, name) - // append require/import statement to the document - if (content !== '') { - css.push({ filename, content }) - if (useImportSyntax) { - ast.program.body.unshift(t.importDeclaration([], t.stringLiteral(importPath))) - } else { - ast.program.body.unshift( - t.expressionStatement( - t.callExpression(t.identifier('require'), [t.stringLiteral(importPath)]), - ), - ) - } - } - } - - const result = generate( - ast, - { - compact: 'auto', - concise: false, - filename: sourceFileName, - // @ts-ignore - quotes: 'single', - retainLines: false, - sourceFileName, - sourceMaps: true, - }, - src, - ) - - if (shouldPrintDebug) { - console.log('output >> ', result.code) - console.log('css output >>', css) - } - - return { - ast, - css, - js: result.code, - map: result.map, - } + return {} as any + // const { cacheObject } = opts + // const { outPath, outRelPath } = pathOpts + // if (typeof src !== 'string') { + // throw new Error('`src` must be a string of javascript') + // } + // invariant( + // typeof sourceFileName === 'string' && path.isAbsolute(sourceFileName), + // '`sourceFileName` must be an absolute path to a .js file', + // ) + // invariant( + // typeof cacheObject === 'object' && cacheObject !== null, + // '`cacheObject` must be an object', + // ) + + // if (!hasParsedViewInformation) { + // hasParsedViewInformation = true + // for (const key in options.views) { + // const view = options.views[key] + // if (isGlossView(view)) { + // const defaultProps = view.internal.glossProps.defaultProps ?? {} + // viewInformation[key] = StaticUtils.getThemeStyles(view, options.defaultTheme, defaultProps) + // } + // } + // } + + // const mediaQueryPrefixes = options.mediaQueryKeys.map(x => `${x}-`) + // const sourceDir = path.dirname(sourceFileName) + + // // Using a map for (officially supported) guaranteed insertion order + // const cssMap = new Map() + // const ast = parse(src) + // let glossSrc = false + // const validComponents = {} + // // default to using require syntax + // let useImportSyntax = false + // const shouldPrintDebug = src[0] === '/' && src[1] === '/' && src[2] === '!' + // const views: { [key: string]: GlossView } = {} + // const JSX_VALID_NAMES = Object.keys(options.views).filter(x => { + // return options.views[x] && !!options.views[x].staticStyleConfig + // }) + + // // we allow things within the ui kit to avoid the more tedious config + // const isInternal = options.internalViewsPaths?.some(x => sourceFileName.indexOf(x) === 0) ?? false + + // let importsGloss = false + + // // Find gloss require in program root + // ast.program.body = ast.program.body.filter((item: t.Node) => { + // if (t.isImportDeclaration(item)) { + // // not imported from gloss? byeeee + // if (item.source.value === 'gloss') { + // importsGloss = true + // } + // if (!importsGloss && !isInternal && !GLOSS_SOURCES[item.source.value]) { + // return true + // } + // glossSrc = true + // useImportSyntax = true + // item.specifiers = item.specifiers.filter(specifier => { + // // keep the weird stuff + // if ( + // !t.isImportSpecifier(specifier) || + // !t.isIdentifier(specifier.imported) || + // !t.isIdentifier(specifier.local) + // ) { + // return true + // } + // if (specifier.local.name[0] !== specifier.local.name[0].toUpperCase()) { + // return true + // } + // if (!JSX_VALID_NAMES.includes(specifier.local.name)) { + // return true + // } + // views[specifier.local.name] = options.views[specifier.local.name] + // validComponents[specifier.local.name] = true + // if (shouldPrintDebug) { + // console.log('found valid component', specifier.local.name) + // } + // // dont remove the import + // return true + // }) + // } + // return true + // }) + + // /** + // * Step 1: Compiled the gloss() style views and remember if they are able to be compiled + // * in step 2 + // */ + // const localStaticViews: { + // [key: string]: { + // staticDesc: GlossStaticStyleDescription, + // propObject: any + // defaultProps?: Object + // parent?: GlossView + // } + // } = {} + + // if (importsGloss || isInternal) { + // traverse(ast, { + // VariableDeclaration(path) { + // const dec = path.node.declarations[0] + // if (!dec || !t.isVariableDeclarator(dec) || !t.isIdentifier(dec.id)) return + // const name = dec.id.name + + // // traverse and find gloss call + // let glossCall: t.CallExpression + // let chain = dec.init + // while ( + // t.isCallExpression(chain) && + // t.isMemberExpression(chain.callee) && + // t.isCallExpression(chain.callee.object) + // ) { + // chain = chain.callee.object + // } + // // verify we found it + // if ( + // t.isCallExpression(chain) && + // t.isIdentifier(chain.callee) && + // chain.callee.name === 'gloss' + // ) { + // // simple gloss without .theme etc + // glossCall = chain + // } + + // if (!glossCall || !glossCall.arguments.length) { + // return + // } + + // validComponents[name] = true + + // const localViewName = t.isIdentifier(glossCall.arguments[0]) && glossCall.arguments[0].name + // let view: GlossView | null = null + + // // parse style objects out and return them as array of [{ ['namespace']: 'className' }] + // let staticStyleDesc: GlossStaticStyleDescription | null = null + + // // this stuff is used by step 2 (theme functions) + // // any props leftover after parsing gloss style props + // let restDefaultProps = {} + + // const evaluate = createEvaluator(path, sourceFileName) + + // glossCall.arguments = glossCall.arguments.map((arg, index) => { + // if ((index === 0 || index === 1) && t.isObjectExpression(arg)) { + // let propObject = {} + // let unevaluated: any[] = [] + // try { + // const opts: EvaluateASTNodeOptions = { + // evaluateFunctions: false, + // unevaluated: [] + // } + // propObject = evaluate(arg, opts) + // unevaluated = opts.unevaluated + // if (shouldPrintDebug) { + // console.log('propObject', propObject) + // } + // } catch (err) { + // console.log('Cant parse style object - this is ok, just de-opt', name, '>', localViewName) + // if (shouldPrintDebug) { + // console.log('err', err) + // } + // return arg + // } + + // // if no error, set local view info + // if (localViewName) { + // // extends one of our optimizable views + // if (views[localViewName]) { + // views[name] = options.views[localViewName] + // view = views[name] + // } + // } + + // // uses the base styles if necessary, merges just like gloss does + // const depth = (view?.internal?.depth ?? -1) + 1 + // const { styles, conditionalStyles, defaultProps, internalDefaultProps } = getGlossProps( + // propObject, + // view, + // ) + // if (shouldPrintDebug) { + // console.log('glossCall.arguments parse gloss props', name, styles, conditionalStyles) + // } + + // // then put them all into an array so gloss later can use that + // const out: GlossStaticStyleDescription = { + // className: '', + // } + + // if (defaultProps.className) { + // out.className = defaultProps.className + // delete defaultProps.className + // } + + // for (const ns in styles) { + // const info = StaticUtils.getStyles(styles[ns], depth, ns) + // if (shouldPrintDebug) { + // console.log('got static extract', name, ns, info, styles[ns]) + // } + // if (info) { + // cssMap.set(info.className, { css: info.css, commentTexts: [] }) + // out.className += ` ${info.className}` + // } else { + // if (shouldPrintDebug) { + // console.log('no info', ns, styles) + // } + // } + // } + + // if (conditionalStyles) { + // out.conditionalClassNames = {} + // for (const prop in conditionalStyles) { + // out.conditionalClassNames[prop] = '' + // for (const ns in conditionalStyles[prop]) { + // const val = conditionalStyles[prop][ns] + // const info = StaticUtils.getStyles(val, depth, ns) + // if (info) { + // cssMap.set(info.className, { css: info.css, commentTexts: [] }) + // out.conditionalClassNames[prop] += ` ${info.className}` + // } + // } + // } + // } + + // localStaticViews[name] = { + // staticDesc: out, + // propObject, + // defaultProps, + // parent: view, + // } + + // if (out.className || out.conditionalClassNames) { + // staticStyleDesc = out + // } + + // // keep any non-style props on the glossProps + // let defaultPropsLeave = unevaluated + + // if (internalDefaultProps && Object.keys(internalDefaultProps).length) { + // const objectLeaveProps = literalToAst(internalDefaultProps) as t.ObjectExpression + // defaultPropsLeave = [ + // ...defaultPropsLeave, + // ...objectLeaveProps.properties + // ] + // } + + // // if we have defaultProps or unevaluated non-extracted items, leave them + // if (defaultPropsLeave.length) { + // return t.objectExpression(defaultPropsLeave) + // } + + // return t.nullLiteral() + // } + // return arg + // }) + + // // add it to runtime: gloss(View, null, { ...staticStyleDesc }) + // if (staticStyleDesc) { + // if (glossCall.arguments.length === 1) { + // glossCall.arguments.push(t.nullLiteral()) + // } + // glossCall.arguments.push(literalToAst(staticStyleDesc)) + // } + + // if (Object.keys(restDefaultProps).length) { + // const argIndex = t.isNullLiteral(glossCall.arguments[0]) ? 0 : 1 + // console.log('restDefaultProps', restDefaultProps) + // glossCall.arguments[argIndex] = literalToAst(restDefaultProps) + // } + // }, + // }) + // } + + // // gloss isn't included anywhere, so let's bail + // if (!glossSrc || !Object.keys(validComponents).length) { + // return { + // ast, + // css: [], + // js: src, + // map: null, + // } + // } + + // // creates an evaluator to get complex values from babel in this path + // function createEvaluator(path: NodePath, sourceFileName: string, defaultOpts?: EvaluateASTNodeOptions) { + // // Generate scope object at this level + // const staticNamespace = getStaticBindingsForScope( + // path.scope, + // sourceFileName, + // // per-file cache of evaluated bindings + // // TODO can be per-module? + // {}, + // options.whitelistStaticModules, + // execFile, + // ) + // const evalContext = vm.createContext(staticNamespace) + // const evalFn = (n: t.Node) => { + // // called when evaluateAstNode encounters a dynamic-looking prop + // // variable + // if (t.isIdentifier(n)) { + // invariant( + // staticNamespace[n.name], + // 'identifier not in staticNamespace', + // ) + // return staticNamespace[n.name] + // } + // return vm.runInContext(`(${generate(n).code})`, evalContext) + // } + // return (n: t.Node, o?: EvaluateASTNodeOptions) => { + // return evaluateAstNode(n, evalFn, { ...defaultOpts, ...o }) + // } + // } + + // /** + // * Step 2: Statically extract from JSX < /> nodes + // */ + // traverse(ast, { + // JSXElement: { + // enter(traversePath: TraversePath) { + // const node = traversePath.node.openingElement + // if ( + // // skip non-identifier opening elements (member expressions, etc.) + // !t.isJSXIdentifier(node.name) || + // // skip non-gloss components + // !validComponents[node.name.name] + // ) { + // return + // } + + // // Remember the source component + // const originalNodeName = node.name.name + // const localView = localStaticViews[originalNodeName] + // let view = views[originalNodeName] + // // for parentView config + // let extraDepth = 0 + // let domNode = 'div' + + // let staticStyleConfig: GlossView['staticStyleConfig'] | null = null + // if (view) { + // staticStyleConfig = view.staticStyleConfig + // // lets us have plain functional views like Stack + // if (staticStyleConfig.parentView) { + // view = staticStyleConfig.parentView + // extraDepth = 1 + // } + // domNode = view.staticStyleConfig?.tagName + // ?? view.internal?.glossProps.defaultProps.tagName + // ?? 'div' + // } + + // // Get valid css props + // const cssAttributes = staticStyleConfig?.cssAttributes || validCSSAttr + + // function isCSSAttribute(name: string) { + // if (cssAttributes[name]) return true + // if (mediaQueryPrefixes.some(x => name.indexOf(x) === 0)) return true + // return false + // } + + // const attemptEval = evaluateAstNode //createEvaluator(traversePath as any, sourceFileName, { evaluateFunctions: false }) + + // let lastSpreadIndex: number = -1 + // const flattenedAttributes: (t.JSXAttribute | t.JSXSpreadAttribute)[] = [] + // node.attributes.forEach(attr => { + // if (t.isJSXSpreadAttribute(attr)) { + // try { + // const spreadValue = attemptEval(attr.argument) + + // if (typeof spreadValue !== 'object' || spreadValue == null) { + // lastSpreadIndex = flattenedAttributes.push(attr) - 1 + // } else { + // for (const k in spreadValue) { + // const value = spreadValue[k] + + // if (typeof value === 'number') { + // flattenedAttributes.push( + // t.jsxAttribute( + // t.jsxIdentifier(k), + // t.jsxExpressionContainer(t.numericLiteral(value)), + // ), + // ) + // } else if (value === null) { + // // why would you ever do this + // flattenedAttributes.push( + // t.jsxAttribute(t.jsxIdentifier(k), t.jsxExpressionContainer(t.nullLiteral())), + // ) + // } else { + // // toString anything else + // // TODO: is this a bad idea + // flattenedAttributes.push( + // t.jsxAttribute( + // t.jsxIdentifier(k), + // t.jsxExpressionContainer(t.stringLiteral('' + value)), + // ), + // ) + // } + // } + // } + // } catch (e) { + // lastSpreadIndex = flattenedAttributes.push(attr) - 1 + // } + // } else { + // flattenedAttributes.push(attr) + // } + // }) + + // node.attributes = flattenedAttributes + + // const staticAttributes: Record = {} + // const htmlExtractedAttributes = {} + // let inlinePropCount = 0 + // const staticTernaries: Ternary[] = [] + // const classNameObjects: (t.StringLiteral | t.Expression)[] = [] + + // let shouldDeopt = false + + // const ogAttributes = node.attributes + // node.attributes = node.attributes.filter((attribute, idx) => { + // if ( + // t.isJSXSpreadAttribute(attribute) || + // // keep the weirdos + // !attribute.name || + // // filter out JSXIdentifiers + // typeof attribute.name.name !== 'string' || + // // haven't hit the last spread operator + // idx < lastSpreadIndex + // ) { + // if (shouldPrintDebug) console.log('inline prop via non normal attr') + // inlinePropCount++ + // return true + // } + + // let name = attribute.name.name + + // // for fully deoptimizing certain keys + // if (staticStyleConfig) { + // if (staticStyleConfig.deoptProps?.includes(name)) { + // shouldDeopt = true + // return true + // } + // // for avoiding processing certain keys + // if (staticStyleConfig.avoidProps?.includes(name)) { + // if (shouldPrintDebug) console.log('inline prop via avoidProps') + // inlinePropCount++ + // return true + // } + // } + + // let value: any = t.isJSXExpressionContainer(attribute?.value) + // ? attribute.value.expression + // : attribute.value + + // // boolean prop / conditionals + // const allConditionalClassNames = { + // ...view?.internal?.compiledInfo?.conditionalClassNames ?? null, + // ...localView?.staticDesc?.conditionalClassNames ?? null, + // } + // if (allConditionalClassNames[name]) { + // // we can just extract to className + // if (value === null) { + // // extract but still put it onto staticAttributes for themeFn to use + // staticAttributes[name] = true + // return false + // } + + // // if dynamic value we just use it on className + // classNameObjects.push( + // t.conditionalExpression( + // value, + // t.stringLiteral(allConditionalClassNames[name]), + // t.stringLiteral('') + // ) + // ) + // return false + // } + + // // boolean props have null value + // if (!value) { + // inlinePropCount++ + // return true + // } + + // // if one or more spread operators are present and we haven't hit the last one yet, the prop stays inline + // if (lastSpreadIndex > -1 && idx <= lastSpreadIndex) { + // inlinePropCount++ + // return true + // } + // // pass ref, key, and style props through untouched + // if (UNTOUCHED_PROPS[name]) { + // return true + // } + + // if (name === 'ref') { + // inlinePropCount++ + // return true + // } + + // const trackState = viewInformation[originalNodeName]?.trackState + // if (trackState) { + // if (trackState?.nonCSSVariables?.has(name)) { + // if (shouldPrintDebug) console.log('inline prop via nonCSSVariables') + // inlinePropCount++ + // return true + // } + // } + + // if (!isCSSAttribute(name)) { + // // we can safely leave html attributes + // // TODO make this more customizable / per-tagname + // if (htmlAttributes[name]) { + // try { + // htmlExtractedAttributes[name] = attemptEval(value) + // } catch(err) { + // // oo fancy! this basically says if we can't eval this safely, and its used by the themeFn + // // then we need to deopt here. if it can be evaluated, we're good, we'll run theme here later + // if (trackState?.usedProps?.has(name)) { + // if (shouldPrintDebug) { + // console.log('we use this in this component', name) + // } + // inlinePropCount++ + // return true + // } + // // console.log('err getting html attr', name, err.message) + // // ok + // } + // return true + // } + // if (shouldPrintDebug) console.log('inline prop via !isCSSAttribute') + // inlinePropCount++ + // return true + // } + + // // allow statically defining a change from one prop to another (see Stack) + // if (typeof cssAttributes[name] === 'object') { + // if (t.isStringLiteral(value)) { + // const definition = cssAttributes[name] + // name = definition.name + // value = definition.value[value.value] + // staticAttributes[name] = value + // return false + // } else { + // console.log('couldnt parse a user defined cssAttribute', name, value) + // inlinePropCount++ + // return true + // } + // } + + // // if value can be evaluated, extract it and filter it out + // try { + // staticAttributes[name] = attemptEval(value) + // return false + // } catch { + // // ok + // } + + // if (t.isConditionalExpression(value)) { + // // if both sides of the ternary can be evaluated, extract them + // try { + // const consequent = attemptEval(value.consequent) + // const alternate = attemptEval(value.alternate) + // staticTernaries.push({ + // alternate, + // consequent, + // name, + // test: value.test, + // }) + // // mark the prop as extracted + // staticAttributes[name] = null + // return false + // } catch (e) { + // // + // } + // } else if (t.isLogicalExpression(value)) { + // // convert a simple logical expression to a ternary with a null alternate + // if (value.operator === '&&') { + // try { + // const consequent = attemptEval(value.right) + // staticTernaries.push({ + // alternate: null, + // consequent, + // name, + // test: value.left, + // }) + // staticAttributes[name] = null + // return false + // } catch (e) { + // // + // } + // } + // } + + // // if we've made it this far, the prop stays inline + // if (shouldPrintDebug) console.log('inline prop via no match') + // inlinePropCount++ + // return true + // }) + + // if (shouldPrintDebug) { + // console.log(`ok we parsed a JSX: + // name: ${node.name.name} + // inlinePropCount: ${inlinePropCount} + // shouldDeopt: ${shouldDeopt} + // domNode: ${domNode} + // `) + // } + + // if (shouldDeopt) { + // node.attributes = ogAttributes + // return + // } + + // let classNamePropValue: t.Expression | null = null + + // const extractedStaticAttrs = Object.keys(staticAttributes).length > 0 + // const classNamePropIndex = node.attributes.findIndex( + // attr => !t.isJSXSpreadAttribute(attr) && attr.name && attr.name.name === 'className', + // ) + // if (classNamePropIndex > -1) { + // classNamePropValue = getPropValueFromAttributes('className', node.attributes) + // node.attributes.splice(classNamePropIndex, 1) + // } + + // // used later to generate classname for item + // const stylesByClassName: { [key: string]: string } = {} + + // const depth = (view?.internal?.depth ?? 1) + (localView?.parent?.internal?.depth ?? 1) + extraDepth + // const addStyles = (styleObj: any) => { + // const allStyles = StaticUtils.getAllStyles(styleObj, depth) + // for (const info of allStyles) { + // if (info.css) { + // if (shouldPrintDebug) { + // console.log('add static styles', info.className, info.css) + // } + // stylesByClassName[info.className] = info.css + // } + // } + // } + + // // capture views where they set it afterwards + // // plus any defaults passed through gloss + // const viewDefaultProps = { + // ...view?.defaultProps, + // ...view?.internal?.glossProps?.defaultProps, + // } + + // if (extractedStaticAttrs) { + // const staticStyleProps = { + // ...viewDefaultProps, + // ...localView?.propObject, + // ...htmlExtractedAttributes, + // ...staticAttributes, + // } + // if (shouldPrintDebug) { + // // ignoreAttrs is usually huge + // const { ignoreAttrs, ...rest } = staticStyleProps + // console.log('adding static style props', rest) + // } + // addStyles(staticStyleProps) + // } + + // // if all style props have been extracted, gloss component can be + // // converted to a div or the specified component + // if (inlinePropCount === 0) { + // // add in any local static classes + // if (localView) { + // stylesByClassName[localView.staticDesc.className] = null + // } + + // const themeFns = view?.internal?.getConfig()?.themeFns + // if (themeFns) { + // // TODO we need to determine if this theme should deopt using the same proxy/tracker as gloss + // try { + // const props = { + // ...viewDefaultProps, + // ...localView?.propObject, + // ...htmlExtractedAttributes, + // ...staticAttributes, + // } + // const extracted = StaticUtils.getThemeStyles(view, options.defaultTheme, props).themeStyles + // if (shouldPrintDebug) { + // delete props['ignoreAttrs'] // ignore this its huge in debug output + // console.log('extracting from theme', !!localView, props, extracted) + // } + // for (const x of extracted) { + // stylesByClassName[x.className] = x.css + // } + // } catch(err) { + // console.log('error running theme', sourceFileName, err.message) + // return + // } + // } + + // // add any default html props to tag + // for (const key in viewDefaultProps) { + // const val = viewDefaultProps[key] + // if (key === 'className') { + // classNameObjects.push(t.stringLiteral(val)) + // continue + // } + // if (htmlAttributes[key]) { + // // @ts-ignore + // if (!node.attributes.some(x => x?.name?.name === key)) { + // // add to start so if its spread onto later its overwritten + // node.attributes.unshift( + // t.jsxAttribute( + // t.jsxIdentifier(key), + // t.jsxExpressionContainer(literalToAst(val)) + // ) + // ) + // } + // } + // } + + // // add a data-is="Name" so we can debug it more easily + // node.attributes.push( + // t.jsxAttribute( + // t.jsxIdentifier('data-is'), + // t.stringLiteral(node.name.name) + // ) + // ) + + // if (localView) { + // node.name.name = domNode + // } + + // // if they set a staticStyleConfig.parentView (see Stack) + // if (!isGlossView(view)) { + // if (view?.staticStyleConfig.parentView) { + // view = view.staticStyleConfig.parentView + // } else { + // node.name.name = domNode + // } + // } + + // // if gloss view we may be able to optimize + // if (isGlossView(view)) { + // // local views we already parsed the css out + // const localView = localStaticViews[node.name.name] + // if (localView) { + // // + // } else { + // const { staticClasses } = view.internal.glossProps + // // internal classes + // for (const className of staticClasses) { + // const item = tracker.get(className.slice(2)) + // const css = `${item.selector} { ${item.style} }` + // stylesByClassName[className] = css + // } + // } + // node.name.name = domNode + // } + + // } else { + // if (lastSpreadIndex > -1) { + // // if only some style props were extracted AND additional props are spread onto the component, + // // add the props back with null values to prevent spread props from incorrectly overwriting the extracted prop value + // Object.keys(staticAttributes).forEach(attr => { + // node.attributes.push( + // t.jsxAttribute(t.jsxIdentifier(attr), t.jsxExpressionContainer(t.nullLiteral())), + // ) + // }) + // } + // } + + // if (traversePath.node.closingElement) { + // // this seems strange + // if (t.isJSXMemberExpression(traversePath.node.closingElement.name)) return + // traversePath.node.closingElement.name.name = node.name.name + // } + + // if (shouldPrintDebug) { + // console.log('stylesByClassName pre ternaries', stylesByClassName) + // } + + // const extractedStyleClassNames = Object.keys(stylesByClassName).join(' ') + + // if (classNamePropValue) { + // try { + // const evaluatedValue = attemptEval(classNamePropValue) + // classNameObjects.push(t.stringLiteral(evaluatedValue)) + // } catch (e) { + // classNameObjects.push(classNamePropValue) + // } + // } + + // if (staticTernaries.length > 0) { + // const ternaryObj = extractStaticTernaries(staticTernaries, cacheObject) + // if (shouldPrintDebug) { + // console.log('staticTernaries', staticTernaries, '\nternaryObj', ternaryObj) + // } + // // ternaryObj is null if all of the extracted ternaries have falsey consequents and alternates + // if (ternaryObj !== null) { + // // add extracted styles by className to existing object + // Object.assign(stylesByClassName, ternaryObj.stylesByClassName) + // classNameObjects.push(ternaryObj.ternaryExpression) + // } + // } + + // if (extractedStyleClassNames) { + // classNameObjects.push(t.stringLiteral(extractedStyleClassNames)) + // if (shouldPrintDebug) { + // console.log('extractedStyleClassNames', extractedStyleClassNames) + // } + // } + + // const classNamePropValueForReals = classNameObjects.reduce( + // (acc, val) => { + // if (acc == null) { + // if ( + // // pass conditional expressions through + // t.isConditionalExpression(val) || + // // pass non-null literals through + // t.isStringLiteral(val) || + // t.isNumericLiteral(val) + // ) { + // return val + // } + // return t.logicalExpression('||', val, t.stringLiteral('')) + // } + + // let inner: t.Expression + // if (t.isStringLiteral(val)) { + // if (t.isStringLiteral(acc)) { + // // join adjacent string literals + // return t.stringLiteral(`${acc.value} ${val.value}`) + // } + // inner = t.stringLiteral(` ${val.value}`) + // } else if (t.isLiteral(val)) { + // inner = t.binaryExpression('+', t.stringLiteral(' '), val) + // } else if (t.isConditionalExpression(val) || t.isBinaryExpression(val)) { + // if (t.isStringLiteral(acc)) { + // return t.binaryExpression('+', t.stringLiteral(`${acc.value} `), val) + // } + // inner = t.binaryExpression('+', t.stringLiteral(' '), val) + // } else if (t.isIdentifier(val) || t.isMemberExpression(val)) { + // // identifiers and member expressions make for reasonable ternaries + // inner = t.conditionalExpression( + // val, + // t.binaryExpression('+', t.stringLiteral(' '), val), + // t.stringLiteral(''), + // ) + // } else { + // if (t.isStringLiteral(acc)) { + // return t.binaryExpression( + // '+', + // t.stringLiteral(`${acc.value} `), + // t.logicalExpression('||', val, t.stringLiteral('')), + // ) + // } + // // use a logical expression for more complex prop values + // inner = t.binaryExpression( + // '+', + // t.stringLiteral(' '), + // t.logicalExpression('||', val, t.stringLiteral('')), + // ) + // } + // return t.binaryExpression('+', acc, inner) + // }, + // null, + // ) + + // if (shouldPrintDebug) { + // console.log('classNamePropValueForReals', classNamePropValueForReals) + // } + + // if (classNamePropValueForReals) { + // if (t.isStringLiteral(classNamePropValueForReals)) { + // node.attributes.push( + // t.jsxAttribute( + // t.jsxIdentifier('className'), + // t.stringLiteral(classNamePropValueForReals.value), + // ), + // ) + // } else { + // node.attributes.push( + // t.jsxAttribute( + // t.jsxIdentifier('className'), + // t.jsxExpressionContainer(classNamePropValueForReals), + // ), + // ) + // } + // } + + // const lineNumbers = + // node.loc && + // node.loc.start.line + + // (node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '') + + // const comment = util.format( + // '/* %s:%s (%s) */', + // sourceFileName.replace(process.cwd(), '.'), + // lineNumbers, + // originalNodeName, + // ) + + // for (const className in stylesByClassName) { + // if (cssMap.has(className)) { + // if (comment) { + // const val = cssMap.get(className)! + // val.commentTexts.push(comment) + // cssMap.set(className, val) + // } + // } else { + // const css = stylesByClassName[className] + // if (css) { + // if (typeof css !== 'string') { + // throw new Error(`CSS is not a string, for ${className}: ${JSON.stringify(stylesByClassName, null, 2)}, ${typeof css}`) + // } + // cssMap.set(className, { css, commentTexts: [comment] }) + // } + // } + // } + // }, + // exit(traversePath: TraversePath) { + // if (traversePath._complexComponentProp) { + // if (t.isJSXElement(traversePath.parentPath)) { + // // bump + // traversePath.parentPath._complexComponentProp = [].concat( + // traversePath.parentPath._complexComponentProp || [], + // traversePath._complexComponentProp, + // ) + // } else { + // // find nearest Statement + // let statementPath = traversePath + // do { + // statementPath = statementPath.parentPath + // } while (!t.isStatement(statementPath)) + + // invariant(t.isStatement(statementPath), 'Could not find a statement') + + // const decs = t.variableDeclaration('var', [].concat(traversePath._complexComponentProp)) + + // statementPath.insertBefore(decs) + // } + // traversePath._complexComponentProp = null + // } + // }, + // }, + // }) + + // const css: CSSExtracted[] = [] + + // // Write out CSS using it's className, this gives us de-duping for shared classnames + // for (const [className, entry] of cssMap.entries()) { + // const content = `${entry.commentTexts.map(txt => `${txt}\n`).join('')}${entry.css}` + // const name = `${className}__gloss.css` + // const importPath = `${outRelPath}/${name}` + // const filename = path.join(outPath, name) + // // append require/import statement to the document + // if (content !== '') { + // css.push({ filename, content }) + // if (useImportSyntax) { + // ast.program.body.unshift(t.importDeclaration([], t.stringLiteral(importPath))) + // } else { + // ast.program.body.unshift( + // t.expressionStatement( + // t.callExpression(t.identifier('require'), [t.stringLiteral(importPath)]), + // ), + // ) + // } + // } + // } + + // const result = generate( + // ast, + // { + // compact: 'auto', + // concise: false, + // filename: sourceFileName, + // // @ts-ignore + // quotes: 'single', + // retainLines: false, + // sourceFileName, + // sourceMaps: true, + // }, + // src, + // ) + + // if (shouldPrintDebug) { + // console.log('output >> ', result.code) + // console.log('css output >>', css) + // } + + // return { + // ast, + // css, + // js: result.code, + // map: result.map, + // } } const execCache = {} @@ -1068,10 +1060,10 @@ const execFile = (file: string) => { // omg this fixed it... ['@babel/plugin-transform-typescript', { isTSX: true }], '@babel/plugin-transform-react-jsx', - ] + ], }).code const exported = { - exports: {} + exports: {}, } vm.runInContext(out, vm.createContext(exported)) const res = exported.exports diff --git a/packages/gloss/src/staticStyleUtils.tsx b/packages/gloss/src/StaticUtils.tsx similarity index 95% rename from packages/gloss/src/staticStyleUtils.tsx rename to packages/gloss/src/StaticUtils.tsx index 95b0d6531d..d6fe2ff9c3 100644 --- a/packages/gloss/src/staticStyleUtils.tsx +++ b/packages/gloss/src/StaticUtils.tsx @@ -1,4 +1,5 @@ -import { addRules, compileThemes, getSortedNamespaces, getStylesFromThemeFns, GlossView, mergeStyles } from './gloss' +import { addRules, getSortedNamespaces, getStylesFromThemeFns, GlossView, mergeStyles } from './gloss' +import { compileThemes } from './helpers/compileThemes' import { CompiledTheme } from './theme/createTheme' import { createThemeProxy } from './theme/createThemeProxy' import { ThemeTrackState } from './theme/useTheme' diff --git a/packages/gloss/src/babel/addDisplayName.ts b/packages/gloss/src/babel/addDisplayName.ts index 37f79fac54..2c6c6f122f 100644 --- a/packages/gloss/src/babel/addDisplayName.ts +++ b/packages/gloss/src/babel/addDisplayName.ts @@ -5,13 +5,14 @@ import { isGlossView } from './utils' export function addDisplayName(path, glossFnName: string, references, file, babel) { const { types: t, template } = babel // extra safe because babel or webpack or something can leave behind old ones of these - const buildBuiltInWithConfig = template(` - if (IDENTIFIER) { IDENTIFIER.displayName = "DISPLAY_NAME" } - `) + const buildBuiltInWithConfig = template( + `\nif (IDENTIFIER) IDENTIFIER.displayName = "DISPLAY_NAME";`, + ) for (const reference of references) { const displayName = getDisplayName(reference) const isView = isGlossView(glossFnName, reference) + console.log('check', isView, reference) if (!isView) continue path.parent.body.push( buildBuiltInWithConfig({ diff --git a/packages/gloss/src/babel/index.ts b/packages/gloss/src/babel/index.ts index c71f68dbf7..adefa9b1b0 100644 --- a/packages/gloss/src/babel/index.ts +++ b/packages/gloss/src/babel/index.ts @@ -31,14 +31,17 @@ const glossPlugin = (babel): babel.PluginObj => { } } +const defaultNames = ['gloss'] +const defaultImports = ['gloss', '../gloss'] + function traverseGlossBlocks(babel, state) { const references = new Set() const res: babel.Visitor = { ImportDeclaration(path) { const fileName = path.hub.file.opts.filename // options - const matchNames: string[] = state.opts.matchNames || ['gloss'] - const matchImports: string[] = state.opts.matchImports || ['gloss'] + const matchNames: string[] = state.opts.matchNames || defaultNames + const matchImports: string[] = state.opts.matchImports || defaultImports if (matchImports.indexOf(path.node.source.value) === -1) { return } diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index b49787445d..d008466383 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -166,6 +166,7 @@ export function gloss< hasCompiled = true shouldUpdateMap = GlossView['shouldUpdateMap'] themeFns = compileThemes(ThemedView) + GlossView['displayName'] = ThemedView.displayName } setTimeout(compile, 0) diff --git a/packages/gloss/src/index.ts b/packages/gloss/src/index.ts index dd61d1b919..a2742c25b8 100644 --- a/packages/gloss/src/index.ts +++ b/packages/gloss/src/index.ts @@ -21,6 +21,7 @@ export * from './blocks/InlineBlock' export * from './blocks/InlineFlex' // configureGloss +export { ThemeStyleInfo, StaticUtils } from './StaticUtils' export { GlossThemeProps, GlossProps, diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index ec07dd87b2..7f6fc00853 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,50 +1,43 @@ -// //! -// import { ProvideUI } from '@o/ui' -// import { Box, gloss } from 'gloss' -// const LinkRow = gloss(Box, { -// flexDirection: 'row', -// flex: 1, -// alignItems: 'center', -// justifyContent: 'center', -// zIndex: 1000000000, -// position: 'relative', -// }) -// const React = require('react') -// const { themes } = require('./themes') -// export const SiteRoot = () => { -// return ( -// -// ok -// -// ) -// } -import { ErrorBoundary, ProvideUI } from '@o/ui' -import React, { StrictMode, Suspense } from 'react' -import { Router, View } from 'react-navi' - -import { Layout } from './Layout' -import { Navigation } from './Navigation' -import { SiteStoreContext } from './SiteStore' -import { themes } from './themes' +//! +import { ProvideUI } from '@o/ui' +import { Absolute } from 'gloss' +const React = require('react') +const { themes } = require('./themes') export const SiteRoot = () => { return ( - - - - {/* this key helps HMR for lazy imports... but it breaks scroll position */} - - - - - - - - - + + ) } +// import { ErrorBoundary, ProvideUI } from '@o/ui' +// import React, { StrictMode, Suspense } from 'react' +// import { Router, View } from 'react-navi' + +// import { Layout } from './Layout' +// import { Navigation } from './Navigation' +// import { SiteStoreContext } from './SiteStore' +// import { themes } from './themes' + +// export const SiteRoot = () => { +// return ( +// +// +// +// {/* this key helps HMR for lazy imports... but it breaks scroll position */} +// +// +// +// +// +// +// +// +// +// +// ) +// } From e29e5098f790eb5670c0af8928aafe0576d7c7ee Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 19:29:22 -0800 Subject: [PATCH 24/31] save --- packages/ui/src/Surface.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/Surface.tsx b/packages/ui/src/Surface.tsx index 7a67844d08..0b19038c10 100644 --- a/packages/ui/src/Surface.tsx +++ b/packages/ui/src/Surface.tsx @@ -588,7 +588,7 @@ const SurfaceFrame = gloss(View, { }).theme(pseudoFunctionThemes, pseudoStyleTheme, (props, prev) => { // todo fix types here const marginStyle = marginTheme(props as any) - const { fontSize, lineHeight } = textSizeTheme(props) + const { fontSize, lineHeight } = textSizeTheme(props as any) if (prev && props.chromeless) { delete prev.hoverStyle From 755bd2fd4a6388655a4e594bf2e980eed42cf31e Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 19:55:11 -0800 Subject: [PATCH 25/31] save --- packages/gloss/src/babel/addDisplayName.ts | 1 - packages/ui/src/text/textSizeTheme.ts | 3 +- projects/site/src/SiteRoot.tsx | 71 +++++++++++----------- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/packages/gloss/src/babel/addDisplayName.ts b/packages/gloss/src/babel/addDisplayName.ts index 2c6c6f122f..7681c51b1c 100644 --- a/packages/gloss/src/babel/addDisplayName.ts +++ b/packages/gloss/src/babel/addDisplayName.ts @@ -12,7 +12,6 @@ export function addDisplayName(path, glossFnName: string, references, file, babe for (const reference of references) { const displayName = getDisplayName(reference) const isView = isGlossView(glossFnName, reference) - console.log('check', isView, reference) if (!isView) continue path.parent.body.push( buildBuiltInWithConfig({ diff --git a/packages/ui/src/text/textSizeTheme.ts b/packages/ui/src/text/textSizeTheme.ts index 99be3f67c3..450d5403b7 100644 --- a/packages/ui/src/text/textSizeTheme.ts +++ b/packages/ui/src/text/textSizeTheme.ts @@ -1,5 +1,4 @@ import { isDefined } from '@o/utils' -import { ThemeFn } from 'gloss' import { hasMediaQueries, mediaQueryKeysSize } from '../mediaQueryKeys' import { getTextSize } from '../Sizes' @@ -16,7 +15,7 @@ export type TextSizeProps = { marginBottom?: any } -export const textSizeTheme: ThemeFn = (props) => { +export const textSizeTheme = (props: TextSizeProps) => { const res = getTextSizeTheme(props) // media query size // TODO this whole loop needs rethinking diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index 7f6fc00853..ae16fe1c44 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,43 +1,42 @@ //! -import { ProvideUI } from '@o/ui' -import { Absolute } from 'gloss' +// import { ProvideUI } from '@o/ui' +// import { Absolute } from 'gloss' +// const React = require('react') +// const { themes } = require('./themes') +// export const SiteRoot = () => { +// return ( +// +// +// +// ) +// } +import { ErrorBoundary, ProvideUI } from '@o/ui' +import React, { StrictMode, Suspense } from 'react' +import { Router, View } from 'react-navi' + +import { Layout } from './Layout' +import { Navigation } from './Navigation' +import { SiteStoreContext } from './SiteStore' +import { themes } from './themes' -const React = require('react') -const { themes } = require('./themes') export const SiteRoot = () => { return ( - - + + + + {/* this key helps HMR for lazy imports... but it breaks scroll position */} + + + + + + + + + ) } -// import { ErrorBoundary, ProvideUI } from '@o/ui' -// import React, { StrictMode, Suspense } from 'react' -// import { Router, View } from 'react-navi' - -// import { Layout } from './Layout' -// import { Navigation } from './Navigation' -// import { SiteStoreContext } from './SiteStore' -// import { themes } from './themes' - -// export const SiteRoot = () => { -// return ( -// -// -// -// {/* this key helps HMR for lazy imports... but it breaks scroll position */} -// -// -// -// -// -// -// -// -// -// -// ) -// } From 04b39ea4ef290ed95db6a1760ecae5a2034a045c Mon Sep 17 00:00:00 2001 From: natew Date: Wed, 6 Nov 2019 19:55:46 -0800 Subject: [PATCH 26/31] save --- packages/gloss/src/gloss.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index d008466383..0ae73526b9 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -548,7 +548,6 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] } function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = '.') { - console.log('merge then', key, val) // check for validity if (validCSSAttr[key]) { // dont overwrite as we go down in importance From 570a94ff40e4a4fcfc763db8d324853029d28caa Mon Sep 17 00:00:00 2001 From: natew Date: Fri, 8 Nov 2019 01:10:35 -0800 Subject: [PATCH 27/31] save --- packages/gloss/src/gloss.tsx | 97 ++++++++++++++--------- packages/ui/src/Surface.tsx | 2 +- projects/site/src/pages/HomePage/Join.tsx | 1 + 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 0ae73526b9..5fa77d59cd 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -262,8 +262,14 @@ export function gloss< } } + if (shouldDebug) { + debugger + } const classNames = getClassNames(theme, themeFns, glossProps.statics) - console.log('got em', classNames, themeFns, glossProps) + // console.log('got em', classNames, themeFns, glossProps) + if (shouldDebug) { + console.log('classNames', classNames) + } finalProps.className = classNames.join(' ') if (isDeveloping) { @@ -523,7 +529,7 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] for (let i = 0; i < depth; i++) { const themeStyles = !!themes[i]?.length && getStylesFromThemeFns(themes[i], props) const staticStyles = styles[i] - console.log('get', themeStyles, staticStyles) + // console.log('get', themeStyles, staticStyles) if (themeStyles) { for (const key in themeStyles) { mergeStyle(key, themeStyles[key], classNames) @@ -532,12 +538,16 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] if (staticStyles) { for (const ns in staticStyles) { if (ns !== '.') { - console.log('skipping ns for now until figured out', ns) + // TODO + // console.log('skipping ns for now until figured out', ns) continue } const styleClasses = staticStyles[ns] for (const key in styleClasses) { if (validCSSAttr[key] && !classNames[key]) { + if (key === 'borderLeftRadius') { + debugger + } classNames[key] = styleClasses[key] } } @@ -552,7 +562,7 @@ function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = if (validCSSAttr[key]) { // dont overwrite as we go down in importance if (classNames[key]) return - addRule(key, cssValue(key, val, false, cssOpts), '.', classNames, true, '') + addStyleRule(key, val, '.', classNames) return } // will be captured next in isSubStyle @@ -561,15 +571,57 @@ function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = } if (isSubStyle(key)) { classNames[key] = classNames[key] || {} - console.log('what is', val) + // console.log('what is', val) for (const skey in val[key]) { if (classNames[key][skey]) continue - addRule(skey, cssValue(skey, val[key][skey], false, cssOpts), key, classNames[key] as ClassNames, true, '') + addStyleRule(skey, val[key][skey], key, classNames[key] as ClassNames) } return } } +function addStyleRule(key: string, val: string, namespace: string, classNames: ClassNames) { + const finalValue = cssValue(key, val, false, cssOpts) + if (SHORTHANDS[key]) { + for (let k of SHORTHANDS[key]) { + k = CAMEL_TO_SNAKE[k] || k + addRule(key, finalValue, namespace, classNames, true, '') + } + } else { + addRule(key, finalValue, namespace, classNames, true, '') + } +} + +function addRule(key: string, val: string, namespace: string, classNames: ClassNames, insert: any, displayName: string) { + const abbrev = cssAttributeAbbreviations[key] + key = CAMEL_TO_SNAKE[key] || key + const isMediaQuery = namespace[0] === '@' + const style = `${key}:${val};` + const className = abbrev + stringHash(`${val}`) + let selector = `.${className}` + if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { + selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') + } + const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` + if (className !== undefined) { + classNames[key] = className + if (insert === true) { + // this is the first time we've found this className + if (!tracker.has(className)) { + // insert the new style text + tracker.set(className, { + displayName, + namespace, + selector, + style, + className, + }) + sheet.insert(isMediaQuery ? namespace : selector, css) + } + } + } +} + const getPrefix = (cn: string) => cn.slice(0, cn.indexOf('-')) function getUniqueStylesByClassName(classNames: string[]) { @@ -649,39 +701,6 @@ export function addRules( // return { css, className: finalClassName } } -function addRule(key: string, val: string, namespace: string, classNames: ClassNames, insert: any, displayName: string) { - const abbrev = cssAttributeAbbreviations[key] - key = CAMEL_TO_SNAKE[key] || key - const isMediaQuery = namespace[0] === '@' - if (!abbrev) { - debugger - } - const style = `${key}:${val};` - const className = abbrev + stringHash(`${val}`) - let selector = `.${className}` - if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { - selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') - } - const css = isMediaQuery ? `${namespace} {${selector} {${style}}}` : `${selector} {${style}}` - if (className !== undefined) { - classNames[key] = className - if (insert === true) { - // this is the first time we've found this className - if (!tracker.has(className)) { - // insert the new style text - tracker.set(className, { - displayName, - namespace, - selector, - style, - className, - }) - sheet.insert(isMediaQuery ? namespace : selector, css) - } - } - } -} - // some internals we can export if (typeof window !== 'undefined') { window['gloss'] = window['gloss'] || { diff --git a/packages/ui/src/Surface.tsx b/packages/ui/src/Surface.tsx index 0b19038c10..c9675ab4ac 100644 --- a/packages/ui/src/Surface.tsx +++ b/packages/ui/src/Surface.tsx @@ -521,7 +521,7 @@ export const Surface = themeable(function Surface(direct: SurfaceProps) { } if (props.debug) { - console.log('ok', showElement, props, surfaceFrameProps) + console.log('debug surface', showElement, props, surfaceFrameProps) } return useBreadcrumbReset( diff --git a/projects/site/src/pages/HomePage/Join.tsx b/projects/site/src/pages/HomePage/Join.tsx index 4a0d475638..ee855ce7da 100644 --- a/projects/site/src/pages/HomePage/Join.tsx +++ b/projects/site/src/pages/HomePage/Join.tsx @@ -72,6 +72,7 @@ export class Join extends React.Component { name="EMAIL" id="mce-EMAIL" placeholder="Email address..." + debug flex={1} size={1.25} sizeRadius={5} From fe3ade601e19db8c5841994108be10463cc967a1 Mon Sep 17 00:00:00 2001 From: natew Date: Fri, 8 Nov 2019 01:16:24 -0800 Subject: [PATCH 28/31] save --- packages/gloss/src/gloss.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 5fa77d59cd..20a295117c 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -262,9 +262,6 @@ export function gloss< } } - if (shouldDebug) { - debugger - } const classNames = getClassNames(theme, themeFns, glossProps.statics) // console.log('got em', classNames, themeFns, glossProps) if (shouldDebug) { @@ -584,8 +581,7 @@ function addStyleRule(key: string, val: string, namespace: string, classNames: C const finalValue = cssValue(key, val, false, cssOpts) if (SHORTHANDS[key]) { for (let k of SHORTHANDS[key]) { - k = CAMEL_TO_SNAKE[k] || k - addRule(key, finalValue, namespace, classNames, true, '') + addRule(k, finalValue, namespace, classNames, true, '') } } else { addRule(key, finalValue, namespace, classNames, true, '') @@ -594,6 +590,9 @@ function addStyleRule(key: string, val: string, namespace: string, classNames: C function addRule(key: string, val: string, namespace: string, classNames: ClassNames, insert: any, displayName: string) { const abbrev = cssAttributeAbbreviations[key] + if (!abbrev) { + debugger + } key = CAMEL_TO_SNAKE[key] || key const isMediaQuery = namespace[0] === '@' const style = `${key}:${val};` From bf97173403a3832b8d5d09a45e0dc6c01680c509 Mon Sep 17 00:00:00 2001 From: natew Date: Fri, 8 Nov 2019 15:24:55 -0800 Subject: [PATCH 29/31] get tests working + fix some stlye bugs --- package.json | 2 + packages/css/src/constants.ts | 25 ++- packages/css/src/css.ts | 6 +- packages/css/src/cssNameMap.ts | 175 ------------------ packages/css/src/helpers.ts | 2 +- packages/css/src/validCSSAttributeExtra.ts | 6 - packages/gloss/jest.config.js | 1 + packages/gloss/src/gloss.tsx | 33 +++- packages/gloss/src/themes/alphaColorTheme.ts | 2 - .../gloss/src/themes/propsToStylesTheme.ts | 2 - packages/gloss/tests/transform.test.ts | 25 --- packages/gloss/tests/transform.test.tsx | 36 ++++ packages/gloss/tsconfig.test.json | 3 + packages/ui/src/text/textSizeTheme.ts | 2 - projects/site/src/SiteRoot.tsx | 25 ++- yarn.lock | 91 ++++++++- 16 files changed, 196 insertions(+), 240 deletions(-) delete mode 100644 packages/css/src/cssNameMap.ts delete mode 100644 packages/gloss/tests/transform.test.ts create mode 100644 packages/gloss/tests/transform.test.tsx create mode 100644 packages/gloss/tsconfig.test.json diff --git a/package.json b/package.json index 96181be3cc..462fe991c9 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,8 @@ "eslint-plugin-react": "7.12.4", "global": "^4.3.2", "jest": "24.8.0", + "react-test-renderer": "^16.11.0", + "@testing-library/react": "^9.3.2", "lerna": "^3.16.4", "lerna-changelog": "^0.8.2", "mobx": "^5.14.0", diff --git a/packages/css/src/constants.ts b/packages/css/src/constants.ts index 46d549fc5d..21684c6c81 100644 --- a/packages/css/src/constants.ts +++ b/packages/css/src/constants.ts @@ -14,17 +14,28 @@ export const SHORTHANDS = { borderTopRadius: ['borderTopRightRadius', 'borderTopLeftRadius'], } -const regularCSSAttributes = +export const validCSSAttr: ValidCSSPropertyMap = process.env.RENDER_TARGET === 'node' ? require('./validCSSAttribute.node').default : require('./validCSSAttribute.dom').default -export const validCSSAttr: Partial = { - ...regularCSSAttributes, - ...Object.keys(SHORTHANDS).reduce((acc, key) => { - acc[key] = true - return acc - }, {}), +// conversions +export const CAMEL_TO_SNAKE = {} +export const SNAKE_TO_CAMEL = {} +for (const camelKey in validCSSAttr) { + let snakeKey = '' + if (camelKey.indexOf('webkit') === 0) { + snakeKey += '-' + } + for (const letter of camelKey) { + if (letter.toUpperCase() === letter) { + snakeKey += `-${letter.toLowerCase()}` + } else { + snakeKey += letter + } + } + CAMEL_TO_SNAKE[camelKey] = snakeKey + SNAKE_TO_CAMEL[snakeKey] = camelKey } // css attribute key abbreviations diff --git a/packages/css/src/css.ts b/packages/css/src/css.ts index a2eb76de2f..c329dee1e1 100644 --- a/packages/css/src/css.ts +++ b/packages/css/src/css.ts @@ -1,15 +1,13 @@ import { Config } from './config' -import { BORDER_KEY, COLOR_KEYS, COMMA_JOINED, FALSE_VALUES, SHORTHANDS, TRANSFORM_KEYS_MAP, unitlessNumberProperties } from './constants' -import { CAMEL_TO_SNAKE } from './cssNameMap' +import { BORDER_KEY, CAMEL_TO_SNAKE, COLOR_KEYS, COMMA_JOINED, FALSE_VALUES, SHORTHANDS, TRANSFORM_KEYS_MAP, unitlessNumberProperties } from './constants' import { boxShadowItem, boxShadowSyntax } from './cssPropertySet' import { px, stringHash } from './helpers' // exports -export { SNAKE_TO_CAMEL, CAMEL_TO_SNAKE } from './cssNameMap' export { GlossPropertySet } from './cssPropertySet' export { configureCSS } from './config' -export { SHORTHANDS, validCSSAttr, cssAttributeAbbreviations } from './constants' +export { SNAKE_TO_CAMEL, CAMEL_TO_SNAKE, SHORTHANDS, validCSSAttr, cssAttributeAbbreviations } from './constants' export { CSSPropertySet, CSSPropertySetResolved, diff --git a/packages/css/src/cssNameMap.ts b/packages/css/src/cssNameMap.ts deleted file mode 100644 index c5a7c64a55..0000000000 --- a/packages/css/src/cssNameMap.ts +++ /dev/null @@ -1,175 +0,0 @@ -export const CAMEL_TO_SNAKE = { - pointerEvents: 'pointer-events', - WebkitAppRegion: '-webkit-app-region', - WebkitLineClamp: '-webkit-line-clamp', - WebkitBoxOrient: '-webkit-box-orient', - WebkitBackgroundClip: '-webkit-background-clip', - WebkitTextFillColor: '-webkit-text-fill-color', - WebkitTextStroke: '-webkit-text-fill-color', - WebkitTextStrokeWidth: '-webkit-text-stroke-width', - WebkitTextStrokeColor: '-webkit-text-stroke-color', - WebkitFontSmoothing: '-webkit-font-smoothing', - alignContent: 'align-content', - alignItems: 'align-items', - alignSelf: 'align-self', - animationDelay: 'animation-delay', - animationDirection: 'animation-direction', - animationDuration: 'animation-duration', - animationFillMode: 'animation-fill-mode', - animationIterationCount: 'animation-iteration-count', - animationName: 'animation-name', - animationPlayState: 'animation-play-state', - animationTimingFunction: 'animation-timing-function', - backfaceVisibility: 'backface-visibility', - backgroundAttachment: 'background-attachment', - backgroundBlendMode: 'background-blend-mode', - backgroundClip: 'background-clip', - backgroundColor: 'background-color', - backgroundImage: 'background-image', - backgroundOrigin: 'background-origin', - backgroundPosition: 'background-position', - backgroundRepeat: 'background-repeat', - backgroundSize: 'background-size', - backdropFilter: 'backdrop-filter', - borderBottom: 'border-bottom', - borderBottomColor: 'border-bottom-color', - borderBottomLeftRadius: 'border-bottom-left-radius', - borderBottomRightRadius: 'border-bottom-right-radius', - borderBottomStyle: 'border-bottom-style', - borderBottomWidth: 'border-bottom-width', - borderRadiusSize: 'border-radius-size', - borderCollapse: 'border-collapse', - borderColor: 'border-color', - borderImage: 'border-image', - borderImageOutset: 'border-image-outset', - borderImageRepeat: 'border-image-repeat', - borderImageSlice: 'border-image-slice', - borderImageSource: 'border-image-source', - borderImageWidth: 'border-image-width', - borderLeft: 'border-left', - borderLeftColor: 'border-left-color', - borderLeftStyle: 'border-left-style', - borderLeftWidth: 'border-left-width', - borderRadius: 'border-radius', - borderRight: 'border-right', - borderRightColor: 'border-right-color', - borderRightStyle: 'border-right-style', - borderRightWidth: 'border-right-width', - borderSpacing: 'border-spacing', - borderStyle: 'border-style', - borderTop: 'border-top', - borderTopColor: 'border-top-color', - borderTopLeftRadius: 'border-top-left-radius', - borderTopRightRadius: 'border-top-right-radius', - borderTopStyle: 'border-top-style', - borderTopWidth: 'border-top-width', - borderWidth: 'border-width', - boxShadow: 'box-shadow', - boxSizing: 'box-sizing', - captionSide: 'caption-side', - columnCount: 'column-count', - columnFill: 'column-fill', - columnGap: 'column-gap', - columnRule: 'column-rule', - columnRuleColor: 'column-rule-color', - columnRuleStyle: 'column-rule-style', - columnRuleWidth: 'column-rule-width', - columnSpan: 'column-span', - columnWidth: 'column-width', - counterIncrement: 'counter-increment', - counterReset: 'counter-reset', - emptyCells: 'empty-cells', - flexBasis: 'flex-basis', - flexDirection: 'flex-direction', - flexFlow: 'flex-flow', - flexGrow: 'flex-grow', - flexShrink: 'flex-shrink', - flexWrap: 'flex-wrap', - fontFamily: 'font-family', - fontSize: 'font-size', - fontSizeAdjust: 'font-size-adjust', - fontStretch: 'font-stretch', - fontStyle: 'font-style', - fontVariant: 'font-variant', - fontWeight: 'font-weight', - hangingPunctuation: 'hanging-punctuation', - justifyContent: 'justify-content', - letterSpacing: 'letter-spacing', - lineHeight: 'line-height', - listStyle: 'list-style', - listStyleImage: 'list-style-image', - listStylePosition: 'list-style-position', - listStyleType: 'list-style-type', - marginBottom: 'margin-bottom', - marginLeft: 'margin-left', - marginRight: 'margin-right', - marginTop: 'margin-top', - maxHeight: 'max-height', - maxWidth: 'max-width', - minHeight: 'min-height', - minWidth: 'min-width', - navDown: 'nav-down', - navIndex: 'nav-index', - navLeft: 'nav-left', - navRight: 'nav-right', - navUp: 'nav-up', - outlineColor: 'outline-color', - outlineOffset: 'outline-offset', - outlineStyle: 'outline-style', - outlineWidth: 'outline-width', - overflowX: 'overflow-x', - overflowY: 'overflow-y', - paddingBottom: 'padding-bottom', - paddingLeft: 'padding-left', - paddingRight: 'padding-right', - paddingTop: 'padding-top', - pageBreakAfter: 'page-break-after', - pageBreakBefore: 'page-break-before', - pageBreakInside: 'page-break-inside', - perspectiveOrigin: 'perspective-origin', - tabSize: 'tab-size', - tableLayout: 'table-layout', - textAlign: 'text-align', - textAlignLast: 'text-align-last', - textDecoration: 'text-decoration', - textDecorationColor: 'text-decoration-color', - textDecorationLine: 'text-decoration-line', - textDecorationStyle: 'text-decoration-style', - textIndent: 'text-indent', - textJustify: 'text-justify', - textOverflow: 'text-overflow', - textShadow: 'text-shadow', - textTransform: 'text-transform', - transformOrigin: 'transform-origin', - transformStyle: 'transform-style', - transitionDelay: 'transition-delay', - transitionDuration: 'transition-duration', - transitionProperty: 'transition-property', - transitionTimingFunction: 'transition-timing-function', - unicodeBidi: 'unicode-bidi', - userSelect: 'user-select', - verticalAlign: 'vertical-align', - whiteSpace: 'white-space', - wordBreak: 'word-break', - wordSpacing: 'word-spacing', - wordWrap: 'word-wrap', - zIndex: 'z-index', - gridGap: 'grid-gap', - gridTemplateColumns: 'grid-template-columns', - gridTemplateRows: 'grid-template-rows', - gridAutoRows: 'grid-auto-rows', - scrollSnapCoordinate: 'scroll-snap-coordinate', - scrollSnapDestination: 'scroll-snap-destination', - scrollSnapPointsX: 'scroll-snap-points-x', - scrollSnapPointsY: 'scroll-snap-points-y', - scrollSnapType: 'scroll-snap-type', - scrollSnapTypeX: 'scroll-snap-type-x', - scrollSnapTypeY: 'scroll-snap-type-y', - scrollSnapAlign: 'scroll-snap-align', - scrollSnapStop: 'scroll-snap-stop', -} - -export const SNAKE_TO_CAMEL = Object.keys(CAMEL_TO_SNAKE).reduce( - (acc, cur) => ({ ...acc, [CAMEL_TO_SNAKE[cur]]: cur }), - {}, -) diff --git a/packages/css/src/helpers.ts b/packages/css/src/helpers.ts index b97a35e080..1ab962431c 100644 --- a/packages/css/src/helpers.ts +++ b/packages/css/src/helpers.ts @@ -1,4 +1,4 @@ -import { CAMEL_TO_SNAKE, SNAKE_TO_CAMEL } from './cssNameMap' +import { CAMEL_TO_SNAKE, SNAKE_TO_CAMEL } from './constants' export const px = (x: number | string) => typeof x === 'number' ? `${x}px` : `${+x}` === x ? `${x}px` : x diff --git a/packages/css/src/validCSSAttributeExtra.ts b/packages/css/src/validCSSAttributeExtra.ts index 9ea5505569..2cdc701454 100644 --- a/packages/css/src/validCSSAttributeExtra.ts +++ b/packages/css/src/validCSSAttributeExtra.ts @@ -1,5 +1,3 @@ -import { CAMEL_TO_SNAKE } from './cssNameMap' - export const validCSSAttributeExtra = { borderLeftRadius: true, borderRightRadius: true, @@ -11,7 +9,3 @@ export const validCSSAttributeExtra = { src: false, size: false, } - -for (const key in CAMEL_TO_SNAKE) { - validCSSAttributeExtra[key] = true -} diff --git a/packages/gloss/jest.config.js b/packages/gloss/jest.config.js index 1aad2324ab..a2aeba692e 100644 --- a/packages/gloss/jest.config.js +++ b/packages/gloss/jest.config.js @@ -3,4 +3,5 @@ const base = require('../../jest.config.base.js') module.exports = { ...base, roots: [''], + testEnvironment: 'jsdom', } diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 20a295117c..12647ea306 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -262,7 +262,7 @@ export function gloss< } } - const classNames = getClassNames(theme, themeFns, glossProps.statics) + const classNames = getClassNames(theme, themeFns, glossProps.statics, props['debug']) // console.log('got em', classNames, themeFns, glossProps) if (shouldDebug) { console.log('classNames', classNames) @@ -507,6 +507,10 @@ export function getGlossProps(allProps: GlossProps | null, parent: GlossView | n } } + if (allProps?.['debug']) { + console.log('debug getGlossProps', { styles, staticStyleDesc, statics, defaultProps }) + } + return { statics, config: compileConfig(config, parent), @@ -520,13 +524,15 @@ type ClassNames = { [key: string]: string | ClassNames } -function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[]): string[] { +function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[], shouldDebug: boolean): string[] { const classNames = {} const depth = themes.length for (let i = 0; i < depth; i++) { const themeStyles = !!themes[i]?.length && getStylesFromThemeFns(themes[i], props) const staticStyles = styles[i] - // console.log('get', themeStyles, staticStyles) + if (shouldDebug) { + console.log('level', i, { themeStyles, staticStyles }) + } if (themeStyles) { for (const key in themeStyles) { mergeStyle(key, themeStyles[key], classNames) @@ -540,11 +546,14 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] continue } const styleClasses = staticStyles[ns] + if (shouldDebug) { + console.log('level', i, 'add', ns, styleClasses, classNames) + } for (const key in styleClasses) { - if (validCSSAttr[key] && !classNames[key]) { - if (key === 'borderLeftRadius') { - debugger - } + if (shouldDebug) { + console.log('key', key, styleClasses[key]) + } + if (!classNames[key]) { classNames[key] = styleClasses[key] } } @@ -557,8 +566,7 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = '.') { // check for validity if (validCSSAttr[key]) { - // dont overwrite as we go down in importance - if (classNames[key]) return + console.log('add', key, val, classNames[key]) addStyleRule(key, val, '.', classNames) return } @@ -594,6 +602,10 @@ function addRule(key: string, val: string, namespace: string, classNames: ClassN debugger } key = CAMEL_TO_SNAKE[key] || key + // already added + if (classNames[key]) { + return + } const isMediaQuery = namespace[0] === '@' const style = `${key}:${val};` const className = abbrev + stringHash(`${val}`) @@ -635,11 +647,12 @@ function getUniqueStylesByClassName(classNames: string[]) { return res } -// excludes gloss internal props and leaves style/html props +// excludes gloss internal + style props, leaves html function getGlossDefaultProps(props: any) { const x = {} for (const key in props) { if (key === 'conditional' || key === 'config') continue + if (validCSSAttr[key]) continue if (isSubStyle(key)) continue x[key] = props[key] } diff --git a/packages/gloss/src/themes/alphaColorTheme.ts b/packages/gloss/src/themes/alphaColorTheme.ts index c556ddbebd..4627c8a748 100644 --- a/packages/gloss/src/themes/alphaColorTheme.ts +++ b/packages/gloss/src/themes/alphaColorTheme.ts @@ -73,8 +73,6 @@ export const alphaColorTheme: ThemeFn = (props, previous) => { } } -alphaColorTheme.hoistTheme = true - function merge( pseudoKey: string, colorKey: string, diff --git a/packages/gloss/src/themes/propsToStylesTheme.ts b/packages/gloss/src/themes/propsToStylesTheme.ts index 9f5ecd74b7..87d0000388 100644 --- a/packages/gloss/src/themes/propsToStylesTheme.ts +++ b/packages/gloss/src/themes/propsToStylesTheme.ts @@ -32,8 +32,6 @@ export const propsToStyles: ThemeFn = (theme, previous) => { return styles } -propsToStyles.hoistTheme = true - export const propToStyle = (key: string, value: any) => { if (validCSSAttr[key]) { return value diff --git a/packages/gloss/tests/transform.test.ts b/packages/gloss/tests/transform.test.ts deleted file mode 100644 index b35f1c1ff3..0000000000 --- a/packages/gloss/tests/transform.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import dedent from 'dedent' - -import { transform } from '../src/transform' - -it('works', async () => { - const { cssText } = await transform( - dedent` - import { gloss } from 'gloss' - - export const View = gloss({ - background: [0,0,0], - transform: { - y: 10 - } - }) - `, - { - filename: './test.js', - outputFilename: '../.gloss-cache/test.css', - }, - ) - - expect(cssText).toBe(`background:rgb(0,0,0);transform:translateY(10px);`) - // expect(cssText).toMatchSnapshot() -}) diff --git a/packages/gloss/tests/transform.test.tsx b/packages/gloss/tests/transform.test.tsx new file mode 100644 index 0000000000..871ce0efed --- /dev/null +++ b/packages/gloss/tests/transform.test.tsx @@ -0,0 +1,36 @@ +import { cleanup, render } from '@testing-library/react' +import React from 'react' + +import { gloss } from '../_' + +afterEach(cleanup) + +it('outputs proper styles in simple case', () => { + const Box = gloss({ + display: 'flex', + boxSizing: 'border-box', + }) + const LinkRow = gloss(Box, { + flexDirection: 'row', + flex: 1, + alignItems: 'center', + background: 'red', + borderLeftRadius: 100, + }) + + render() + + const node = document.getElementById('test1') + const style = window.getComputedStyle(node) + + // inherited from Box + expect(style.display).toBe('flex') + expect(style.boxSizing).toBe('border-box') + // sets own styles + expect(style.flexDirection).toBe('row') + expect(style.flex).toBe('1') + expect(style.background).toBe('red') + expect(style.alignItems).toBe('center') + // sets shorthand styles + expect(style.borderTopLeftRadius).toBe('100px') +}) diff --git a/packages/gloss/tsconfig.test.json b/packages/gloss/tsconfig.test.json new file mode 100644 index 0000000000..3706663ecf --- /dev/null +++ b/packages/gloss/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.base" +} diff --git a/packages/ui/src/text/textSizeTheme.ts b/packages/ui/src/text/textSizeTheme.ts index 450d5403b7..ff2a58681e 100644 --- a/packages/ui/src/text/textSizeTheme.ts +++ b/packages/ui/src/text/textSizeTheme.ts @@ -39,8 +39,6 @@ export const textSizeTheme = (props: TextSizeProps) => { return res } -textSizeTheme.hoistTheme = true - const booleanToNumber = (val: boolean | number): number => { return val === true ? 1 : val === false ? 0 : val } diff --git a/projects/site/src/SiteRoot.tsx b/projects/site/src/SiteRoot.tsx index ae16fe1c44..df98846ee7 100644 --- a/projects/site/src/SiteRoot.tsx +++ b/projects/site/src/SiteRoot.tsx @@ -1,15 +1,32 @@ -//! -// import { ProvideUI } from '@o/ui' -// import { Absolute } from 'gloss' +// //! +// import { ProvideUI, SimpleText } from '@o/ui' +// import { gloss } from 'gloss' // const React = require('react') // const { themes } = require('./themes') // export const SiteRoot = () => { // return ( // -// +// +// Amazing internal tools +// // // ) // } +// const titleSize = 9 +// const TextFitTitle = gloss(SimpleText, { +// userSelect: 'text', +// lineHeight: '95%', +// fontSize: `${titleSize}vw`, +// 'lg-fontSize': titleSize * 11.5, +// }) import { ErrorBoundary, ProvideUI } from '@o/ui' import React, { StrictMode, Suspense } from 'react' import { Router, View } from 'react-navi' diff --git a/yarn.lock b/yarn.lock index d42bed1c65..f1e2f68392 100644 --- a/yarn.lock +++ b/yarn.lock @@ -805,7 +805,7 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript" "^7.6.0" -"@babel/runtime@7.4.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@~7.4.4": +"@babel/runtime@7.4.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@~7.4.4": version "7.4.5" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== @@ -2455,6 +2455,11 @@ resolved "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.2.tgz#6167c8b0a1f6d98bcdac79b8e7e2a1a450bfd072" integrity sha512-SpI8PXdcw2UjYnbgubn+GHIyb3jrQRkZlsNuzIFYnxTXBfNyAUmf7rlOYuqO9MlKiC80amL6aP+ZTi1ccTYVfA== +"@sheerun/mutationobserver-shim@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" + integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -2477,6 +2482,27 @@ dependencies: defer-to-connect "^1.0.1" +"@testing-library/dom@^6.3.0": + version "6.10.1" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-6.10.1.tgz#da5bf5065d3f9e484aef4cc495f4e1a5bea6df2e" + integrity sha512-5BPKxaO+zSJDUbVZBRNf9KrmDkm/EcjjaHSg3F9+031VZyPACKXlwLBjVzZxheunT9m72DoIq7WvyE457/Xweg== + dependencies: + "@babel/runtime" "^7.6.2" + "@sheerun/mutationobserver-shim" "^0.3.2" + "@types/testing-library__dom" "^6.0.0" + aria-query "3.0.0" + pretty-format "^24.9.0" + wait-for-expect "^3.0.0" + +"@testing-library/react@^9.3.2": + version "9.3.2" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-9.3.2.tgz#418000daa980dafd2d9420cc733d661daece9aa0" + integrity sha512-J6ftWtm218tOLS175MF9eWCxGp+X+cUXCpkPIin8KAXWtyZbr9CbqJ8M8QNd6spZxJDAGlw+leLG4MJWLlqVgg== + dependencies: + "@babel/runtime" "^7.6.0" + "@testing-library/dom" "^6.3.0" + "@types/testing-library__react" "^9.1.0" + "@tommoor/slate-edit-list@0.19.0-0": version "0.19.0-0" resolved "https://registry.npmjs.org/@tommoor/slate-edit-list/-/slate-edit-list-0.19.0-0.tgz#972a714e9ea4cdf47a530d5702a904f5547be2dd" @@ -2868,6 +2894,21 @@ resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== +"@types/testing-library__dom@*", "@types/testing-library__dom@^6.0.0": + version "6.10.0" + resolved "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.10.0.tgz#590d76e3875a7c536dc744eb530cbf51b6483404" + integrity sha512-mL/GMlyQxiZplbUuFNwA0vAI3k3uJNSf6slr5AVve9TXmfLfyefNT0uHHnxwdYuPMxYD5gI/+dgAvc/5opW9JQ== + dependencies: + pretty-format "^24.3.0" + +"@types/testing-library__react@^9.1.0": + version "9.1.2" + resolved "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-9.1.2.tgz#e33af9124c60a010fc03a34eff8f8a34a75c4351" + integrity sha512-CYaMqrswQ+cJACy268jsLAw355DZtPZGt3Jwmmotlcu8O/tkoXBI6AeZ84oZBJsIsesozPKzWzmv/0TIU+1E9Q== + dependencies: + "@types/react-dom" "*" + "@types/testing-library__dom" "*" + "@types/tmp@^0.0.33": version "0.0.33" resolved "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" @@ -3691,6 +3732,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3876,6 +3925,11 @@ assign-symbols@^1.0.0: resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types-flow@0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -5678,6 +5732,11 @@ commander@2.20.0: resolved "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.11.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@^2.12.1, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.8.1, commander@~2.20.0: version "2.20.1" resolved "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9" @@ -16728,7 +16787,7 @@ pretty-error@^2.0.2, pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^24.9.0: +pretty-format@^24.3.0, pretty-format@^24.9.0: version "24.9.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== @@ -17407,6 +17466,11 @@ react-is@^16.6.0, react-is@^16.8.1, react-is@^16.8.4: resolved "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz#984120fd4d16800e9a738208ab1fba422d23b5ab" integrity sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA== +react-is@^16.8.6: + version "16.11.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa" + integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw== + react-jss@^8.6.1: version "8.6.1" resolved "https://registry.npmjs.org/react-jss/-/react-jss-8.6.1.tgz#a06e2e1d2c4d91b4d11befda865e6c07fbd75252" @@ -17565,6 +17629,16 @@ react-switch@5.0.1: dependencies: prop-types "^15.6.2" +react-test-renderer@^16.11.0: + version "16.11.0" + resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.11.0.tgz#72574566496462c808ac449b0287a4c0a1a7d8f8" + integrity sha512-nh9gDl8R4ut+ZNNb2EeKO5VMvTKxwzurbSMuGBoKtjpjbg8JK/u3eVPVNi1h1Ue+eYK9oSzJjb+K3lzLxyA4ag== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.8.6" + scheduler "^0.17.0" + react-timer-mixin@^0.13.4: version "0.13.4" resolved "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz#75a00c3c94c13abe29b43d63b4c65a88fc8264d3" @@ -18532,6 +18606,14 @@ scheduler@^0.15.0: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@^0.17.0: + version "0.17.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe" + integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + schema-utils@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" @@ -21435,6 +21517,11 @@ w3c-xmlserializer@^1.1.2: webidl-conversions "^4.0.2" xml-name-validator "^3.0.0" +wait-for-expect@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.1.tgz#ec204a76b0038f17711e575720aaf28505ac7185" + integrity sha512-3Ha7lu+zshEG/CeHdcpmQsZnnZpPj/UsG3DuKO8FskjuDbkx3jE3845H+CuwZjA2YWYDfKMU2KhnCaXMLd3wVw== + wait-on@3.3.0-beta.0: version "3.3.0-beta.0" resolved "https://registry.npmjs.org/wait-on/-/wait-on-3.3.0-beta.0.tgz#834f906ed68644284aaa9ad074ddd32a5f7fb6c4" From 1983f7a68e3b1b711137c7567e59d27b8458722f Mon Sep 17 00:00:00 2001 From: natew Date: Sat, 9 Nov 2019 00:14:42 -0800 Subject: [PATCH 30/31] save --- packages/color/src/color.ts | 2 + packages/gloss/package.json | 9 +--- packages/gloss/src/gloss.tsx | 54 ++++++++++--------- packages/gloss/src/stylesheet/gc.ts | 4 +- .../gloss/src/themes/propsToStylesTheme.ts | 2 + packages/ui/src/text/textSizeTheme.ts | 2 + .../site/src/pages/DocsPage/DocsButton.tsx | 2 +- projects/site/src/pages/HomePage/Join.tsx | 1 - .../pages/HomePage/WelcomeBlogPostButton.tsx | 1 + 9 files changed, 41 insertions(+), 36 deletions(-) diff --git a/packages/color/src/color.ts b/packages/color/src/color.ts index 6ef94081ca..e11a99a7dc 100644 --- a/packages/color/src/color.ts +++ b/packages/color/src/color.ts @@ -4,6 +4,8 @@ import { inputToRGB } from './format-input' import { HSL, HSLA, HSV, HSVA, RGB, RGBA } from './interfaces' import { bound01, boundAlpha, clamp01 } from './util' +export * from './memoizeOne' + export interface ColorOptions { format?: ColorFormats gradientType?: string diff --git a/packages/gloss/package.json b/packages/gloss/package.json index 311a9f689b..381c87fef1 100644 --- a/packages/gloss/package.json +++ b/packages/gloss/package.json @@ -1,7 +1,7 @@ { "name": "gloss", "version": "2.6.0", - "description": "Higher order component for nicer component styles", + "description": "Dynamic style system", "main": "_/index.js", "ts:main": "src/index.ts", "sideEffects": [ @@ -25,12 +25,7 @@ }, "devDependencies": { "@babel/generator": "^7.4.4", - "cosmiconfig": "^5.2.1", - "dedent": "0.7.0", - "enhanced-resolve": "^4.1.0", - "loader-utils": "^1.2.3", - "mkdirp": "^0.5.1", - "normalize-path": "^3.0.0" + "enhanced-resolve": "^4.1.0" }, "peerDependencies": { "react": "^16.9.0", diff --git a/packages/gloss/src/gloss.tsx b/packages/gloss/src/gloss.tsx index 12647ea306..9919fd947a 100644 --- a/packages/gloss/src/gloss.tsx +++ b/packages/gloss/src/gloss.tsx @@ -1,5 +1,5 @@ -import { CAMEL_TO_SNAKE, cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, SHORTHANDS, stringHash, validCSSAttr } from '@o/css' -import React from 'react' +import { CAMEL_TO_SNAKE, cssAttributeAbbreviations, CSSPropertySet, CSSPropertySetLoose, cssValue, SHORTHANDS, SNAKE_TO_CAMEL, stringHash, validCSSAttr } from '@o/css' +import React, { useMemo } from 'react' import { createElement, isValidElement, memo, useEffect, useRef } from 'react' import { Config } from './configureGloss' @@ -263,11 +263,12 @@ export function gloss< } const classNames = getClassNames(theme, themeFns, glossProps.statics, props['debug']) - // console.log('got em', classNames, themeFns, glossProps) if (shouldDebug) { console.log('classNames', classNames) } - finalProps.className = classNames.join(' ') + finalProps.className = Object.values(classNames).join(' ') + ( + typeof props.className === 'string' ? ' ' + props.className : '' + ) if (isDeveloping) { finalProps['data-is'] = finalProps['data-is'] || ThemedView.displayName @@ -282,7 +283,7 @@ export function gloss< if (isDeveloping && shouldDebug) { const styles = finalProps.className .split(' ') - .map(x => tracker.get(x.slice(2))) + .map(x => tracker.get(x)) .filter(Boolean) console.log('styles\n', styles, '\nprops\n', props, '\noutProps\n', finalProps) shouldDebug = false @@ -326,10 +327,19 @@ export function gloss< return ThemedView as any } -function getStylesForClassNames(_classNames: string[]) { - // TODO - console.log('todo') - return {} +const CNCache = new WeakMap() +const getStylesForClassNames = (classNames: ClassNames): Object => { + if (CNCache.has(classNames)) return CNCache.get(classNames) + const style = {} + for (let key in classNames) { + const cn = classNames[key] + key = SNAKE_TO_CAMEL[key] + if (typeof cn === 'string') { + style[key] = tracker.get(cn)?.value + } + } + CNCache.set(classNames, style) + return style } function createGlossView(GlossView, config) { @@ -524,8 +534,8 @@ type ClassNames = { [key: string]: string | ClassNames } -function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[], shouldDebug: boolean): string[] { - const classNames = {} +function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[], shouldDebug: boolean): ClassNames { + const classNames: ClassNames = {} const depth = themes.length for (let i = 0; i < depth; i++) { const themeStyles = !!themes[i]?.length && getStylesFromThemeFns(themes[i], props) @@ -560,13 +570,12 @@ function getClassNames(props: any, themes: ThemeFn[][], styles: ClassNamesByNs[] } } } - return Object.values(classNames) + return classNames } function mergeStyle(key: string, val: any, classNames: ClassNames, _namespace = '.') { // check for validity if (validCSSAttr[key]) { - console.log('add', key, val, classNames[key]) addStyleRule(key, val, '.', classNames) return } @@ -589,26 +598,23 @@ function addStyleRule(key: string, val: string, namespace: string, classNames: C const finalValue = cssValue(key, val, false, cssOpts) if (SHORTHANDS[key]) { for (let k of SHORTHANDS[key]) { - addRule(k, finalValue, namespace, classNames, true, '') + addRule(k, finalValue, namespace, classNames, true) } } else { - addRule(key, finalValue, namespace, classNames, true, '') + addRule(key, finalValue, namespace, classNames, true) } } -function addRule(key: string, val: string, namespace: string, classNames: ClassNames, insert: any, displayName: string) { +function addRule(key: string, value: string, namespace: string, classNames: ClassNames, insert: any) { const abbrev = cssAttributeAbbreviations[key] - if (!abbrev) { - debugger - } key = CAMEL_TO_SNAKE[key] || key // already added if (classNames[key]) { return } const isMediaQuery = namespace[0] === '@' - const style = `${key}:${val};` - const className = abbrev + stringHash(`${val}`) + const style = `${key}:${value};` + const className = abbrev + stringHash(`${value}`) let selector = `.${className}` if (namespace[0] === '&' || namespace.indexOf('&') !== -1) { selector = namespace.split(',').map(part => `.${className} ${part.replace('&', '')}`).join(',') @@ -621,10 +627,10 @@ function addRule(key: string, val: string, namespace: string, classNames: ClassN if (!tracker.has(className)) { // insert the new style text tracker.set(className, { - displayName, namespace, selector, style, + value, className, }) sheet.insert(isMediaQuery ? namespace : selector, css) @@ -652,8 +658,8 @@ function getGlossDefaultProps(props: any) { const x = {} for (const key in props) { if (key === 'conditional' || key === 'config') continue - if (validCSSAttr[key]) continue - if (isSubStyle(key)) continue + // if (validCSSAttr[key]) continue + // if (isSubStyle(key)) continue x[key] = props[key] } return x diff --git a/packages/gloss/src/stylesheet/gc.ts b/packages/gloss/src/stylesheet/gc.ts index beb6af76e9..a7cd0fefbf 100644 --- a/packages/gloss/src/stylesheet/gc.ts +++ b/packages/gloss/src/stylesheet/gc.ts @@ -15,9 +15,7 @@ export type StyleTracker = Map< { displayName?: string namespace: string - // we can compile it later and use it for speed - // only using in the motion View.tsx stuff for now - styleObject?: Object + value: any selector: string style: string className: string diff --git a/packages/gloss/src/themes/propsToStylesTheme.ts b/packages/gloss/src/themes/propsToStylesTheme.ts index 87d0000388..9f5ecd74b7 100644 --- a/packages/gloss/src/themes/propsToStylesTheme.ts +++ b/packages/gloss/src/themes/propsToStylesTheme.ts @@ -32,6 +32,8 @@ export const propsToStyles: ThemeFn = (theme, previous) => { return styles } +propsToStyles.hoistTheme = true + export const propToStyle = (key: string, value: any) => { if (validCSSAttr[key]) { return value diff --git a/packages/ui/src/text/textSizeTheme.ts b/packages/ui/src/text/textSizeTheme.ts index ff2a58681e..450d5403b7 100644 --- a/packages/ui/src/text/textSizeTheme.ts +++ b/packages/ui/src/text/textSizeTheme.ts @@ -39,6 +39,8 @@ export const textSizeTheme = (props: TextSizeProps) => { return res } +textSizeTheme.hoistTheme = true + const booleanToNumber = (val: boolean | number): number => { return val === true ? 1 : val === false ? 0 : val } diff --git a/projects/site/src/pages/DocsPage/DocsButton.tsx b/projects/site/src/pages/DocsPage/DocsButton.tsx index 93b262d0d3..4b9a72c687 100644 --- a/projects/site/src/pages/DocsPage/DocsButton.tsx +++ b/projects/site/src/pages/DocsPage/DocsButton.tsx @@ -6,7 +6,7 @@ export let Basic = () => { return ( - +