@vaadin/vaadin-themable-mixin
Advanced tools
Comparing version 22.0.0-alpha6 to 22.0.0-alpha7
{ | ||
"name": "@vaadin/vaadin-themable-mixin", | ||
"version": "22.0.0-alpha6", | ||
"version": "22.0.0-alpha7", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"description": "vaadin-themable-mixin", | ||
"license": "Apache-2.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/vaadin/web-components.git", | ||
"directory": "packages/vaadin-themable-mixin" | ||
}, | ||
"author": "Vaadin Ltd", | ||
"homepage": "https://vaadin.com/elements", | ||
"bugs": { | ||
"url": "https://github.com/vaadin/vaadin-themable-mixin/issues" | ||
}, | ||
"main": "vaadin-themable-mixin.js", | ||
"module": "vaadin-themable-mixin.js", | ||
"repository": "vaadin/vaadin-themable-mixin", | ||
"files": [ | ||
"*.d.ts", | ||
"register-styles.js", | ||
"vaadin-*.js" | ||
], | ||
"keywords": [ | ||
@@ -14,26 +32,12 @@ "Vaadin", | ||
], | ||
"author": "Vaadin Ltd", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/vaadin/vaadin-themable-mixin/issues" | ||
}, | ||
"homepage": "https://vaadin.com/elements", | ||
"files": [ | ||
"*.d.ts", | ||
"vaadin-*.js", | ||
"register-styles.js" | ||
], | ||
"dependencies": { | ||
"@polymer/polymer": "^3.0.0", | ||
"lit": "^2.0.0-rc.1" | ||
"lit": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"@esm-bundle/chai": "^4.3.4", | ||
"@polymer/polymer": "^3.0.0", | ||
"@vaadin/testing-helpers": "^0.3.0", | ||
"sinon": "^9.2.4" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"gitHead": "4b136b1c7da8942960e7255f40c27859125b3a45" | ||
"gitHead": "8e89419c6b44a1d225d5859e180d7b35e47ddb52" | ||
} |
@@ -1,11 +0,6 @@ | ||
import { CSSResultGroup } from 'lit'; | ||
export { css, unsafeCSS } from 'lit'; | ||
/** | ||
* Registers CSS styles for a component type. Make sure to register the styles before | ||
* the first instance of a component of the type is attached to DOM. | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
declare function registerStyles(themeFor: string | null, styles: CSSResultGroup, options?: object | null): void; | ||
export { registerStyles }; | ||
export { registerStyles, css, unsafeCSS } from './vaadin-themable-mixin.js'; |
@@ -1,80 +0,6 @@ | ||
import '@polymer/polymer/lib/elements/dom-module.js'; | ||
import { CSSResult } from 'lit'; | ||
import { stylesFromTemplate } from '@polymer/polymer/lib/utils/style-gather.js'; | ||
export { css, unsafeCSS } from 'lit'; | ||
let moduleIdIndex = 0; | ||
// Map of <CSSResult, Polymer.DomModule> pairs. | ||
const styleMap = {}; | ||
function recursiveFlattenStyles(styles, result = []) { | ||
if (styles instanceof CSSResult) { | ||
result.push(styles); | ||
} else if (Array.isArray(styles)) { | ||
styles.forEach((style) => recursiveFlattenStyles(style, result)); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Registers CSS styles for a component type. Make sure to register the styles before | ||
* the first instance of a component of the type is attached to DOM. | ||
* | ||
* @param {String} themeFor The local/tag name of the component type to register the styles for | ||
* @param {CSSResultGroup} styles The CSS style rules to be registered for the component type | ||
* matching themeFor and included in the local scope of each component instance | ||
* @param {Object=} options Additional options | ||
* @return {void} | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
export const registerStyles = (themeFor, styles, options) => { | ||
if (options && options.include && !options.suppressDeprecationWarning) { | ||
console.warn( | ||
`The "include" option in registerStyles is deprecated. Instead, include an imported CSSResult in the styles array.` | ||
); | ||
} | ||
const themeId = (options && options.moduleId) || `custom-style-module-${moduleIdIndex++}`; | ||
if (!Array.isArray(styles)) { | ||
styles = styles ? [styles] : []; | ||
} | ||
styles = recursiveFlattenStyles(styles); | ||
const processedStyles = styles.map((cssResult) => { | ||
if (!(cssResult instanceof CSSResult)) { | ||
throw new Error('An item in styles is not of type CSSResult. Use `unsafeCSS` or `css`.'); | ||
} | ||
if (!styleMap[cssResult]) { | ||
const template = document.createElement('template'); | ||
template.innerHTML = `<style>${cssResult.toString()}</style>`; | ||
styleMap[cssResult] = stylesFromTemplate(template)[0]; | ||
} | ||
return styleMap[cssResult].textContent; | ||
}); | ||
const themeModuleElement = document.createElement('dom-module'); | ||
if (themeFor) { | ||
const elementClass = customElements.get(themeFor); | ||
if (elementClass && Object.prototype.hasOwnProperty.call(elementClass, '__finalized')) { | ||
console.warn(`The custom element definition for "${themeFor}" | ||
was finalized before a style module was registered. | ||
Make sure to add component specific style modules before | ||
importing the corresponding custom element.`); | ||
} | ||
themeModuleElement.setAttribute('theme-for', themeFor); | ||
} | ||
const moduleIncludes = (options && options.include) || []; | ||
themeModuleElement.innerHTML = ` | ||
<template> | ||
${moduleIncludes.map((include) => `<style include=${include}></style>`)} | ||
${processedStyles.length ? `<style>${processedStyles.join('\n')}</style>` : ''} | ||
</template> | ||
`; | ||
themeModuleElement.register(themeId); | ||
}; | ||
export { registerStyles, css, unsafeCSS } from './vaadin-themable-mixin.js'; |
@@ -0,1 +1,7 @@ | ||
/** | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
import { CSSResult, CSSResultGroup } from 'lit'; | ||
import { ThemePropertyMixin, ThemePropertyMixinConstructor } from './vaadin-theme-property-mixin.js'; | ||
@@ -14,2 +20,22 @@ | ||
export { ThemableMixin, ThemableMixinConstructor }; | ||
/** | ||
* Registers CSS styles for a component type. Make sure to register the styles before | ||
* the first instance of a component of the type is attached to DOM. | ||
*/ | ||
declare function registerStyles(themeFor: string | null, styles: CSSResultGroup, options?: object | null): void; | ||
type Theme = { | ||
themeFor: string; | ||
styles: CSSResult[]; | ||
moduleId?: string; | ||
include?: string | string[]; | ||
}; | ||
/** | ||
* For internal purposes only. | ||
*/ | ||
declare const __themeRegistry: Theme[]; | ||
export { css, unsafeCSS } from 'lit'; | ||
export { ThemableMixin, ThemableMixinConstructor, registerStyles, __themeRegistry }; |
@@ -1,5 +0,185 @@ | ||
import { DomModule } from '@polymer/polymer/lib/elements/dom-module.js'; | ||
/** | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
import { CSSResult, css, unsafeCSS } from 'lit'; | ||
import { ThemePropertyMixin } from './vaadin-theme-property-mixin.js'; | ||
export { css, unsafeCSS }; | ||
/** | ||
* @typedef {Object} Theme | ||
* @property {string} themeFor | ||
* @property {CSSResult[]} styles | ||
* @property {string | string[]} [include] | ||
* @property {string} [moduleId] | ||
* | ||
* @typedef {CSSResult[] | CSSResult} CSSResultGroup | ||
*/ | ||
/** | ||
* @type {Theme[]} | ||
*/ | ||
const themeRegistry = []; | ||
/** | ||
* Registers CSS styles for a component type. Make sure to register the styles before | ||
* the first instance of a component of the type is attached to DOM. | ||
* | ||
* @param {string} themeFor The local/tag name of the component type to register the styles for | ||
* @param {CSSResultGroup} styles The CSS style rules to be registered for the component type | ||
* matching themeFor and included in the local scope of each component instance | ||
* @param {{moduleId?: string, include?: string | string[]}} options Additional options | ||
* @return {void} | ||
*/ | ||
export function registerStyles(themeFor, styles, options = {}) { | ||
if (themeFor) { | ||
const elementClass = customElements.get(themeFor); | ||
if (elementClass && Object.prototype.hasOwnProperty.call(elementClass, '__finalized')) { | ||
console.warn(`The custom element definition for "${themeFor}" | ||
was finalized before a style module was registered. | ||
Make sure to add component specific style modules before | ||
importing the corresponding custom element.`); | ||
} | ||
} | ||
styles = recursiveFlattenStyles(styles); | ||
if (window.Vaadin && window.Vaadin.styleModules) { | ||
window.Vaadin.styleModules.registerStyles(themeFor, styles, options); | ||
} else { | ||
themeRegistry.push({ | ||
themeFor, | ||
styles, | ||
include: options.include, | ||
moduleId: options.moduleId | ||
}); | ||
} | ||
} | ||
/** | ||
* Returns all registered themes. By default the themeRegistry is returend as is. | ||
* In case the style-modules adapter is imported, the themes are obtained from there instead | ||
* @returns {Theme[]} | ||
*/ | ||
function getAllThemes() { | ||
if (window.Vaadin && window.Vaadin.styleModules) { | ||
return window.Vaadin.styleModules.getAllThemes(); | ||
} else { | ||
return themeRegistry; | ||
} | ||
} | ||
/** | ||
* Returns true if the themeFor string matches the tag name | ||
* @param {string} themeFor | ||
* @param {string} tagName | ||
* @returns {boolean} | ||
*/ | ||
function matchesThemeFor(themeFor, tagName) { | ||
return (themeFor || '').split(' ').some((themeForToken) => { | ||
return new RegExp('^' + themeForToken.split('*').join('.*') + '$').test(tagName); | ||
}); | ||
} | ||
/** | ||
* Maps the moduleName to an include priority number which is used for | ||
* determining the order in which styles are applied. | ||
* @param {string} moduleName | ||
* @returns {number} | ||
*/ | ||
function getIncludePriority(moduleName = '') { | ||
let includePriority = 0; | ||
if (moduleName.indexOf('lumo-') === 0 || moduleName.indexOf('material-') === 0) { | ||
includePriority = 1; | ||
} else if (moduleName.indexOf('vaadin-') === 0) { | ||
includePriority = 2; | ||
} | ||
return includePriority; | ||
} | ||
/** | ||
* Flattens the styles into a single array of styles. | ||
* @param {CSSResultGroup} styles | ||
* @param {CSSResult[]} result | ||
* @returns {CSSResult[]} | ||
*/ | ||
function recursiveFlattenStyles(styles = [], result = []) { | ||
if (styles instanceof CSSResult) { | ||
result.push(styles); | ||
} else if (Array.isArray(styles)) { | ||
styles.forEach((style) => recursiveFlattenStyles(style, result)); | ||
} else { | ||
console.warn('An item in styles is not of type CSSResult. Use `unsafeCSS` or `css`.'); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Gets an array of CSSResults matching the include property of the theme. | ||
* @param {Theme} theme | ||
* @returns {CSSResult[]} | ||
*/ | ||
function getIncludedStyles(theme) { | ||
const includedStyles = []; | ||
if (theme.include) { | ||
[].concat(theme.include).forEach((includeModuleId) => { | ||
const includedTheme = getAllThemes().find((s) => s.moduleId === includeModuleId); | ||
if (includedTheme) { | ||
includedStyles.push(...getIncludedStyles(includedTheme), ...includedTheme.styles); | ||
} else { | ||
console.warn(`Included moduleId ${includeModuleId} not found in style registry`); | ||
} | ||
}, theme.styles); | ||
} | ||
return includedStyles; | ||
} | ||
/** | ||
* Includes the styles to the template. | ||
* @param {CSSResult[]} styles | ||
* @param {HTMLTemplateElement} template | ||
*/ | ||
function addStylesToTemplate(styles, template) { | ||
const styleEl = document.createElement('style'); | ||
styleEl.innerHTML = styles | ||
// Remove duplicates so that the last occurrence remains | ||
.filter((style, index) => index === styles.lastIndexOf(style)) | ||
.map((style) => style.cssText) | ||
.join('\n'); | ||
template.content.appendChild(styleEl); | ||
} | ||
/** | ||
* Returns an array of themes that should be used for styling a component matching | ||
* the tag name. The array is sorted by the include order. | ||
* @param {string} tagName | ||
* @returns {Theme[]} | ||
*/ | ||
function getThemes(tagName) { | ||
const defaultModuleName = tagName + '-default-theme'; | ||
const themes = getAllThemes() | ||
// Filter by matching themeFor properties | ||
.filter((theme) => theme.moduleId !== defaultModuleName && matchesThemeFor(theme.themeFor, tagName)) | ||
.map((theme) => ({ | ||
...theme, | ||
// Prepend styles from included themes | ||
styles: [...getIncludedStyles(theme), ...theme.styles], | ||
// Map moduleId to includePriority | ||
includePriority: getIncludePriority(theme.moduleId) | ||
})) | ||
// Sort by includePriority | ||
.sort((themeA, themeB) => themeB.includePriority - themeA.includePriority); | ||
if (themes.length > 0) { | ||
return themes; | ||
} else { | ||
// No theme modules found, return the default module if it exists | ||
return getAllThemes().filter((theme) => theme.moduleId === defaultModuleName); | ||
} | ||
} | ||
/** | ||
* @polymerMixin | ||
@@ -10,3 +190,6 @@ * @mixes ThemePropertyMixin | ||
class VaadinThemableMixin extends ThemePropertyMixin(superClass) { | ||
/** @protected */ | ||
/** | ||
* Covers PolymerElement based component styling | ||
* @protected | ||
*/ | ||
static finalize() { | ||
@@ -16,70 +199,34 @@ super.finalize(); | ||
const template = this.prototype._template; | ||
if (!template || template.__themes) { | ||
return; | ||
} | ||
const inheritedTemplate = Object.getPrototypeOf(this.prototype)._template; | ||
if (inheritedTemplate) { | ||
// Include the theme modules from the inherited template | ||
Array.from(inheritedTemplate.content.querySelectorAll('style[include]')).forEach((s) => { | ||
this._includeStyle(s.getAttribute('include'), template); | ||
}); | ||
} | ||
const inheritedThemes = (inheritedTemplate ? inheritedTemplate.__themes : []) || []; | ||
this._includeMatchingThemes(template); | ||
} | ||
template.__themes = [...inheritedThemes, ...getThemes(this.is)]; | ||
/** @private */ | ||
static _includeMatchingThemes(template) { | ||
const domModule = DomModule; | ||
const modules = domModule.prototype.modules; | ||
let hasThemes = false; | ||
const defaultModuleName = this.is + '-default-theme'; | ||
Object.keys(modules) | ||
.sort((moduleNameA, moduleNameB) => { | ||
const vaadinA = moduleNameA.indexOf('vaadin-') === 0; | ||
const vaadinB = moduleNameB.indexOf('vaadin-') === 0; | ||
const vaadinThemePrefixes = ['lumo-', 'material-']; | ||
const vaadinThemeA = vaadinThemePrefixes.filter((prefix) => moduleNameA.indexOf(prefix) === 0).length > 0; | ||
const vaadinThemeB = vaadinThemePrefixes.filter((prefix) => moduleNameB.indexOf(prefix) === 0).length > 0; | ||
if (vaadinA !== vaadinB) { | ||
// Include vaadin core styles first | ||
return vaadinA ? -1 : 1; | ||
} else if (vaadinThemeA !== vaadinThemeB) { | ||
// Include vaadin theme styles after that | ||
return vaadinThemeA ? -1 : 1; | ||
} else { | ||
// Lastly include custom styles so they override all vaadin styles | ||
return 0; | ||
} | ||
}) | ||
.forEach((moduleName) => { | ||
if (moduleName !== defaultModuleName) { | ||
const themeFor = modules[moduleName].getAttribute('theme-for'); | ||
if (themeFor) { | ||
themeFor.split(' ').forEach((themeForToken) => { | ||
if (new RegExp('^' + themeForToken.split('*').join('.*') + '$').test(this.is)) { | ||
hasThemes = true; | ||
this._includeStyle(moduleName, template); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
if (!hasThemes && modules[defaultModuleName]) { | ||
// No theme modules found, include the default module if it exists | ||
this._includeStyle(defaultModuleName, template); | ||
} | ||
// Get flattened styles array | ||
const styles = template.__themes.reduce((styles, theme) => [...styles, ...theme.styles], []); | ||
addStylesToTemplate(styles, template); | ||
} | ||
/** @private */ | ||
static _includeStyle(moduleName, template) { | ||
if (template && !template.content.querySelector(`style[include="${moduleName}"]`)) { | ||
const styleEl = document.createElement('style'); | ||
styleEl.setAttribute('include', moduleName); | ||
template.content.appendChild(styleEl); | ||
} | ||
/** | ||
* Covers LitElement based component styling | ||
* | ||
* NOTE: This is not yet an offically supported API! | ||
* | ||
* TODO: Add tests (run a variation of themable-mixin.test.js where the components get created as LitElements) | ||
* @protected | ||
*/ | ||
static finalizeStyles(styles) { | ||
return ( | ||
getThemes(this.is) | ||
// Get flattened styles array | ||
.reduce((styles, theme) => [...styles, ...theme.styles], []) | ||
.concat(styles) | ||
); | ||
} | ||
}; | ||
export { themeRegistry as __themeRegistry }; |
@@ -0,1 +1,6 @@ | ||
/** | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
declare function ThemePropertyMixin<T extends new (...args: any[]) => {}>(base: T): T & ThemePropertyMixinConstructor; | ||
@@ -2,0 +7,0 @@ |
/** | ||
* @license | ||
* Copyright (c) 2021 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
*/ | ||
/** | ||
* @polymerMixin | ||
@@ -3,0 +8,0 @@ */ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
23678
1
323
0
4
1
- Removed@polymer/polymer@^3.0.0
- Removed@polymer/polymer@3.5.2(transitive)
- Removed@webcomponents/shadycss@1.11.2(transitive)
Updatedlit@^2.0.0