@spectrum-web-components/theme
Advanced tools
Comparing version 0.3.0 to 0.3.1
@@ -6,2 +6,9 @@ # Change Log | ||
## [0.3.1](https://github.com/adobe/spectrum-web-components/compare/@spectrum-web-components/theme@0.3.0...@spectrum-web-components/theme@0.3.1) (2020-03-16) | ||
### Bug Fixes | ||
- **theme:** make typescript happy ([a9aa377](https://github.com/adobe/spectrum-web-components/commit/a9aa377)) | ||
- **theme:** track default theme values dynamically ([a0c306c](https://github.com/adobe/spectrum-web-components/commit/a0c306c)) | ||
# [0.3.0](https://github.com/adobe/spectrum-web-components/compare/@spectrum-web-components/theme@0.2.0...@spectrum-web-components/theme@0.3.0) (2020-03-11) | ||
@@ -8,0 +15,0 @@ |
@@ -18,2 +18,6 @@ { | ||
{ | ||
"name": "core", | ||
"type": "\"core\"" | ||
}, | ||
{ | ||
"name": "color", | ||
@@ -20,0 +24,0 @@ "attribute": "color", |
export * from './theme.js'; | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './theme-dark.js'; | ||
import './theme-darkest.js'; | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './scale-medium.js'; | ||
import './scale-large.js'; |
@@ -13,8 +13,8 @@ /* | ||
export * from './theme.js'; | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './theme-dark.js'; | ||
import './theme-darkest.js'; | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './scale-medium.js'; | ||
import './scale-large.js'; | ||
//# sourceMappingURL=index.js.map |
import { CSSResult } from 'lit-element'; | ||
export declare const DefaultColor = "light"; | ||
export declare const DefaultScale = "medium"; | ||
declare global { | ||
@@ -27,9 +25,15 @@ interface Window { | ||
} | ||
export declare class Theme extends HTMLElement { | ||
private static themeFragments; | ||
declare type ThemeKindProvider = { | ||
[P in FragmentType]: string; | ||
}; | ||
export declare class Theme extends HTMLElement implements ThemeKindProvider { | ||
private hasAdoptedStyles; | ||
private static themeFragmentsByKind; | ||
private static defaultFragments; | ||
private static templateElement?; | ||
static readonly observedAttributes: string[]; | ||
readonly core: 'core'; | ||
color: Color; | ||
scale: Scale; | ||
private getFragmentNameByKind; | ||
private readonly styles; | ||
@@ -43,6 +47,2 @@ private static readonly template; | ||
static registerThemeFragment(name: FragmentName, kind: FragmentType, styles: CSSResult): void; | ||
static themeFragment(name: FragmentName): { | ||
kind: FragmentType; | ||
styles: CSSResult; | ||
}; | ||
} | ||
@@ -49,0 +49,0 @@ declare global { |
141
lib/theme.js
@@ -14,8 +14,8 @@ /* | ||
import { supportsAdoptingStyleSheets } from 'lit-element'; | ||
export const DefaultColor = 'light'; | ||
export const DefaultScale = 'medium'; | ||
export class Theme extends HTMLElement { | ||
constructor() { | ||
super(); | ||
this.hasAdoptedStyles = false; | ||
this.attachShadow({ mode: 'open' }); | ||
/* istanbul ignore else */ | ||
if (this.shadowRoot) { | ||
@@ -32,11 +32,7 @@ const node = document.importNode(Theme.template.content, true); | ||
} | ||
get core() { | ||
return 'core'; | ||
} | ||
get color() { | ||
const color = this.getAttribute('color'); | ||
if (!color) | ||
return DefaultColor; | ||
const colorFragment = Theme.themeFragments.get(color); | ||
if (colorFragment && colorFragment.kind === 'color') { | ||
return color; | ||
} | ||
return DefaultColor; | ||
return this.getFragmentNameByKind('color'); | ||
} | ||
@@ -47,10 +43,3 @@ set color(newValue) { | ||
get scale() { | ||
const scale = this.getAttribute('scale'); | ||
if (!scale) | ||
return DefaultScale; | ||
const scaleFragment = Theme.themeFragments.get(scale); | ||
if (scaleFragment && scaleFragment.kind === 'scale') { | ||
return scale; | ||
} | ||
return DefaultScale; | ||
return this.getFragmentNameByKind('scale'); | ||
} | ||
@@ -60,8 +49,44 @@ set scale(newValue) { | ||
} | ||
getFragmentNameByKind(kind) { | ||
const kindFragments = Theme.themeFragmentsByKind.get(kind); | ||
/* istanbul ignore if */ | ||
if (!kindFragments) { | ||
throw new Error(`Unknown theme fragment kind '${kind}'`); | ||
/* istanbul ignore if */ | ||
} | ||
else if (kindFragments.size === 0) { | ||
throw new Error(`No theme fragments of kind '${kind}'`); | ||
} | ||
const name = this.getAttribute(kind); | ||
if (name) { | ||
const fragment = kindFragments.get(name); | ||
if (fragment) { | ||
return name; | ||
} | ||
} | ||
const defaultFragment = kindFragments.get('default'); | ||
/* istanbul ignore else */ | ||
if (defaultFragment) { | ||
return defaultFragment.name; | ||
} | ||
throw new Error(`Incorrectly configured theme fragments of kind '${kind}'`); | ||
} | ||
get styles() { | ||
return [ | ||
coreStyles, | ||
Theme.themeFragment(this.color).styles, | ||
Theme.themeFragment(this.scale).styles, | ||
const themeKinds = [ | ||
...Theme.themeFragmentsByKind.keys(), | ||
]; | ||
const styles = themeKinds.reduce((acc, kind) => { | ||
const kindFragments = Theme.themeFragmentsByKind.get(kind); | ||
const defaultStyles = kindFragments.get('default'); | ||
const { [kind]: name } = this; | ||
const currentStyles = kindFragments.get(name); | ||
if (currentStyles) { | ||
acc.push(currentStyles.styles); | ||
} | ||
else if (defaultStyles) { | ||
acc.push(defaultStyles.styles); | ||
} | ||
return acc; | ||
}, []); | ||
return [coreStyles, ...styles]; | ||
} | ||
@@ -85,4 +110,9 @@ static get template() { | ||
connectedCallback() { | ||
/* istanbul ignore if */ | ||
if (!this.hasAdoptedStyles) { | ||
this.adoptStyles(); | ||
} | ||
// Note, first update/render handles styleElement so we only call this if | ||
// connected after first update. | ||
/* istanbul ignore if */ | ||
if (window.ShadyCSS !== undefined) { | ||
@@ -107,8 +137,12 @@ window.ShadyCSS.styleElement(this); | ||
const fragmentCSS = []; | ||
for (const [name, { kind, styles }] of Theme.themeFragments) { | ||
let cssText = styles.cssText; | ||
if (!Theme.defaultFragments.has(name)) { | ||
cssText = cssText.replace(':host', `:host([${kind}='${name}'])`); | ||
for (const [kind, fragments] of Theme.themeFragmentsByKind) { | ||
for (const [name, { styles }] of fragments) { | ||
if (name === 'default') | ||
continue; | ||
let cssText = styles.cssText; | ||
if (!Theme.defaultFragments.has(name)) { | ||
cssText = cssText.replace(':host', `:host([${kind}='${name}'])`); | ||
} | ||
fragmentCSS.push(cssText); | ||
} | ||
fragmentCSS.push(cssText); | ||
} | ||
@@ -121,23 +155,22 @@ window.ShadyCSS.ScopingShim.prepareAdoptedCssText(fragmentCSS, this.localName); | ||
for (const style of styles) { | ||
if (style.styleSheet) { | ||
styleSheets.push(style.styleSheet); | ||
} | ||
styleSheets.push(style.styleSheet); | ||
} | ||
this.shadowRoot.adoptedStyleSheets = styleSheets; | ||
} | ||
else { | ||
if (this.shadowRoot) { | ||
const styleNodes = this.shadowRoot.querySelectorAll('style'); | ||
if (styleNodes) { | ||
styleNodes.forEach((element) => element.remove()); | ||
else if (this.shadowRoot) { | ||
const styleNodes = this.shadowRoot.querySelectorAll('style'); | ||
if (styleNodes) { | ||
styleNodes.forEach((element) => element.remove()); | ||
} | ||
styles.forEach((s) => { | ||
/* istanbul ignore if */ | ||
if (!this.shadowRoot) { | ||
return; | ||
} | ||
styles.forEach((s) => { | ||
const style = document.createElement('style'); | ||
style.textContent = s.cssText; | ||
if (this.shadowRoot) { | ||
this.shadowRoot.appendChild(style); | ||
} | ||
}); | ||
} | ||
const style = document.createElement('style'); | ||
style.textContent = s.cssText; | ||
this.shadowRoot.appendChild(style); | ||
}); | ||
} | ||
this.hasAdoptedStyles = true; | ||
} | ||
@@ -153,18 +186,14 @@ attributeChangedCallback() { | ||
static registerThemeFragment(name, kind, styles) { | ||
this.themeFragments.set(name, { kind, styles }); | ||
} | ||
static themeFragment(name) { | ||
const fragment = this.themeFragments.get(name); | ||
if (!fragment) { | ||
throw new Error(`Unknown theme fragment '${name}'`); | ||
const fragmentMap = this.themeFragmentsByKind.get(kind) || new Map(); | ||
if (fragmentMap.size === 0) { | ||
this.themeFragmentsByKind.set(kind, fragmentMap); | ||
// we're adding our first fragment for this kind, set as default | ||
fragmentMap.set('default', { name, styles }); | ||
Theme.defaultFragments.add(name); | ||
} | ||
return fragment; | ||
fragmentMap.set(name, { name, styles }); | ||
} | ||
} | ||
Theme.themeFragments = new Map(); | ||
Theme.defaultFragments = new Set([ | ||
'core', | ||
'light', | ||
'medium', | ||
]); | ||
Theme.themeFragmentsByKind = new Map(); | ||
Theme.defaultFragments = new Set(['core']); | ||
Theme.registerThemeFragment('core', 'core', coreStyles); | ||
@@ -171,0 +200,0 @@ /* istanbul ignore else */ |
@@ -21,3 +21,3 @@ { | ||
], | ||
"version": "0.3.0", | ||
"version": "0.3.1", | ||
"description": "", | ||
@@ -45,3 +45,3 @@ "main": "lib/index.js", | ||
}, | ||
"gitHead": "d5b370be9515cb1458c436fd4b7c8689e2bfb933" | ||
"gitHead": "05f9a0b1e25b0419276845d0c5a3c9395fea78ea" | ||
} |
@@ -11,7 +11,7 @@ ## Description | ||
``` | ||
npm install @spectrum-web-components/themes | ||
npm install @spectrum-web-components/theme | ||
# or | ||
yarn add @spectrum-web-components/themes | ||
yarn add @spectrum-web-components/theme | ||
``` | ||
@@ -29,6 +29,6 @@ | ||
// Power a site using <sp-theme color="darkest" scale="large"> | ||
import '@spectrum-web-components/themes/lib/theme-darkest.js'; | ||
import '@spectrum-web-components/themes/lib/scale-large.js'; | ||
import '@spectrum-web-components/theme/lib/theme-darkest.js'; | ||
import '@spectrum-web-components/theme/lib/scale-large.js'; | ||
import '@spectrum-web-components/themes/lib/theme.js'; | ||
import '@spectrum-web-components/theme/lib/theme.js'; | ||
``` | ||
@@ -43,4 +43,4 @@ | ||
Promise.all([ | ||
import(`@spectrum-web-components/themes/lib/theme-${color}.js`), | ||
import(`@spectrum-web-components/themes/lib/scale-${scale}.js`), | ||
import(`@spectrum-web-components/theme/lib/theme-${color}.js`), | ||
import(`@spectrum-web-components/theme/lib/scale-${scale}.js`), | ||
]).then(() => { | ||
@@ -47,0 +47,0 @@ themeElement.color = color; |
@@ -14,7 +14,7 @@ /* | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './theme-dark.js'; | ||
import './theme-darkest.js'; | ||
import './theme-light.js'; | ||
import './theme-lightest.js'; | ||
import './scale-medium.js'; | ||
import './scale-large.js'; |
170
src/theme.ts
@@ -16,5 +16,2 @@ /* | ||
export const DefaultColor = 'light'; | ||
export const DefaultScale = 'medium'; | ||
declare global { | ||
@@ -44,3 +41,3 @@ interface Window { | ||
type FragmentType = 'color' | 'scale' | 'core'; | ||
type FragmentMap = Map<string, { kind: FragmentType; styles: CSSResult }>; | ||
type FragmentMap = Map<string, { name: string; styles: CSSResult }>; | ||
export type Color = 'light' | 'lightest' | 'dark' | 'darkest'; | ||
@@ -55,10 +52,14 @@ export type Scale = 'medium' | 'large'; | ||
export class Theme extends HTMLElement { | ||
private static themeFragments: FragmentMap = new Map(); | ||
private static defaultFragments: Set<FragmentName> = new Set([ | ||
'core', | ||
'light', | ||
'medium', | ||
]); | ||
type ThemeKindProvider = { | ||
[P in FragmentType]: string; | ||
}; | ||
export class Theme extends HTMLElement implements ThemeKindProvider { | ||
private hasAdoptedStyles = false; | ||
private static themeFragmentsByKind: Map< | ||
FragmentType, | ||
FragmentMap | ||
> = new Map(); | ||
private static defaultFragments: Set<FragmentName> = new Set(['core']); | ||
private static templateElement?: HTMLTemplateElement; | ||
@@ -70,11 +71,8 @@ | ||
get core(): 'core' { | ||
return 'core'; | ||
} | ||
get color(): Color { | ||
const color = this.getAttribute('color'); | ||
if (!color) return DefaultColor; | ||
const colorFragment = Theme.themeFragments.get(color); | ||
if (colorFragment && colorFragment.kind === 'color') { | ||
return color as Color; | ||
} | ||
return DefaultColor; | ||
return this.getFragmentNameByKind('color') as Color; | ||
} | ||
@@ -87,10 +85,3 @@ | ||
get scale(): Scale { | ||
const scale = this.getAttribute('scale'); | ||
if (!scale) return DefaultScale; | ||
const scaleFragment = Theme.themeFragments.get(scale); | ||
if (scaleFragment && scaleFragment.kind === 'scale') { | ||
return scale as Scale; | ||
} | ||
return DefaultScale; | ||
return this.getFragmentNameByKind('scale') as Scale; | ||
} | ||
@@ -102,8 +93,52 @@ | ||
private getFragmentNameByKind(kind: FragmentType): string { | ||
const kindFragments = Theme.themeFragmentsByKind.get( | ||
kind | ||
) as FragmentMap; | ||
/* istanbul ignore if */ | ||
if (!kindFragments) { | ||
throw new Error(`Unknown theme fragment kind '${kind}'`); | ||
/* istanbul ignore if */ | ||
} else if (kindFragments.size === 0) { | ||
throw new Error(`No theme fragments of kind '${kind}'`); | ||
} | ||
const name = this.getAttribute(kind); | ||
if (name) { | ||
const fragment = kindFragments.get(name); | ||
if (fragment) { | ||
return name; | ||
} | ||
} | ||
const defaultFragment = kindFragments.get('default'); | ||
/* istanbul ignore else */ | ||
if (defaultFragment) { | ||
return defaultFragment.name; | ||
} | ||
throw new Error( | ||
`Incorrectly configured theme fragments of kind '${kind}'` | ||
); | ||
} | ||
private get styles(): CSSResult[] { | ||
return [ | ||
coreStyles, | ||
Theme.themeFragment(this.color).styles, | ||
Theme.themeFragment(this.scale).styles, | ||
const themeKinds: FragmentType[] = [ | ||
...Theme.themeFragmentsByKind.keys(), | ||
]; | ||
const styles = themeKinds.reduce( | ||
(acc, kind) => { | ||
const kindFragments = Theme.themeFragmentsByKind.get( | ||
kind | ||
) as FragmentMap; | ||
const defaultStyles = kindFragments.get('default'); | ||
const { [kind]: name } = this; | ||
const currentStyles = kindFragments.get(name); | ||
if (currentStyles) { | ||
acc.push(currentStyles.styles); | ||
} else if (defaultStyles) { | ||
acc.push(defaultStyles.styles); | ||
} | ||
return acc; | ||
}, | ||
[] as CSSResult[] | ||
); | ||
return [coreStyles, ...styles]; | ||
} | ||
@@ -122,2 +157,3 @@ | ||
this.attachShadow({ mode: 'open' }); | ||
/* istanbul ignore else */ | ||
if (this.shadowRoot) { | ||
@@ -143,4 +179,9 @@ const node = document.importNode(Theme.template.content, true); | ||
protected connectedCallback(): void { | ||
/* istanbul ignore if */ | ||
if (!this.hasAdoptedStyles) { | ||
this.adoptStyles(); | ||
} | ||
// Note, first update/render handles styleElement so we only call this if | ||
// connected after first update. | ||
/* istanbul ignore if */ | ||
if (window.ShadyCSS !== undefined) { | ||
@@ -169,11 +210,14 @@ window.ShadyCSS.styleElement(this); | ||
const fragmentCSS: string[] = []; | ||
for (const [name, { kind, styles }] of Theme.themeFragments) { | ||
let cssText = styles.cssText; | ||
if (!Theme.defaultFragments.has(name as FragmentName)) { | ||
cssText = cssText.replace( | ||
':host', | ||
`:host([${kind}='${name}'])` | ||
); | ||
for (const [kind, fragments] of Theme.themeFragmentsByKind) { | ||
for (const [name, { styles }] of fragments) { | ||
if (name === 'default') continue; | ||
let cssText = styles.cssText; | ||
if (!Theme.defaultFragments.has(name as FragmentName)) { | ||
cssText = cssText.replace( | ||
':host', | ||
`:host([${kind}='${name}'])` | ||
); | ||
} | ||
fragmentCSS.push(cssText); | ||
} | ||
fragmentCSS.push(cssText); | ||
} | ||
@@ -188,22 +232,21 @@ window.ShadyCSS.ScopingShim.prepareAdoptedCssText( | ||
for (const style of styles) { | ||
if (style.styleSheet) { | ||
styleSheets.push(style.styleSheet); | ||
} | ||
styleSheets.push(style.styleSheet as CSSStyleSheet); | ||
} | ||
this.shadowRoot.adoptedStyleSheets = styleSheets; | ||
} else { | ||
if (this.shadowRoot) { | ||
const styleNodes = this.shadowRoot.querySelectorAll('style'); | ||
if (styleNodes) { | ||
styleNodes.forEach((element) => element.remove()); | ||
} else if (this.shadowRoot) { | ||
const styleNodes = this.shadowRoot.querySelectorAll('style'); | ||
if (styleNodes) { | ||
styleNodes.forEach((element) => element.remove()); | ||
} | ||
styles.forEach((s) => { | ||
/* istanbul ignore if */ | ||
if (!this.shadowRoot) { | ||
return; | ||
} | ||
styles.forEach((s) => { | ||
const style = document.createElement('style'); | ||
style.textContent = s.cssText; | ||
if (this.shadowRoot) { | ||
this.shadowRoot.appendChild(style); | ||
} | ||
}); | ||
} | ||
const style = document.createElement('style'); | ||
style.textContent = s.cssText; | ||
this.shadowRoot.appendChild(style); | ||
}); | ||
} | ||
this.hasAdoptedStyles = true; | ||
} | ||
@@ -224,13 +267,10 @@ | ||
): void { | ||
this.themeFragments.set(name, { kind, styles }); | ||
} | ||
static themeFragment( | ||
name: FragmentName | ||
): { kind: FragmentType; styles: CSSResult } { | ||
const fragment = this.themeFragments.get(name); | ||
if (!fragment) { | ||
throw new Error(`Unknown theme fragment '${name}'`); | ||
const fragmentMap = this.themeFragmentsByKind.get(kind) || new Map(); | ||
if (fragmentMap.size === 0) { | ||
this.themeFragmentsByKind.set(kind, fragmentMap); | ||
// we're adding our first fragment for this kind, set as default | ||
fragmentMap.set('default', { name, styles }); | ||
Theme.defaultFragments.add(name); | ||
} | ||
return fragment; | ||
fragmentMap.set(name, { name, styles }); | ||
} | ||
@@ -237,0 +277,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
426256
1887