New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

hybrids

Package Overview
Dependencies
Maintainers
2
Versions
149
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hybrids - npm Package Compare versions

Comparing version 8.0.10 to 8.1.0

docs/component-model/layout-engine.md

1

docs/_sidebar.md

@@ -7,2 +7,3 @@ - [Getting Started](/getting-started.md)

- [Templates](/component-model/templates.md)
- [Layout Engine](/component-model/layout-engine.md)
- [Localization](/component-model/localization.md)

@@ -9,0 +10,0 @@ - [Events](/component-model/events.md)

@@ -5,2 +5,15 @@ # Changelog

## [8.1.0](https://github.com/hybridsjs/hybrids/compare/v8.0.10...v8.1.0) (2022-08-26)
### Features
* **layout:** Add support for layout engine ([#194](https://github.com/hybridsjs/hybrids/issues/194)) ([fe6e424](https://github.com/hybridsjs/hybrids/commit/fe6e424a63a036593ba4f8a35a1c7429fc350ba4))
### Bug Fixes
* **store:** clear nested internal object by null value ([1310f47](https://github.com/hybridsjs/hybrids/commit/1310f47dc99f18f75171c08cd924ec8948edead7))
* **store:** improved error message for value with validation ([3ce6da9](https://github.com/hybridsjs/hybrids/commit/3ce6da9d00d61849e82f88845520ac4cbbbf942f))
### [8.0.10](https://github.com/hybridsjs/hybrids/compare/v8.0.9...v8.0.10) (2022-07-15)

@@ -7,0 +20,0 @@

119

docs/component-model/templates.md

@@ -424,3 +424,3 @@ # Templates

In the above example `submit` function creates a template with an `fn` callback. The main template can use this function in the expression with a custom callback. If so, the nested template with a button is rendered in the form element.
In the above example `submit` function creates a template with an `fn` callback. The main template can use this function in the expression with a custom callback. If so, the nested template with a button is rendered in the form element.

@@ -431,61 +431,19 @@ The child template propagates element instance context from the parent. The `host` argument of the `myCallback` is the same as it would be with a function used directly in the main template. This pattern allows creating template parts, that can be easily defined in separate files and re-use across different custom elements.

To style your custom element, you can create `<style>` elements directly in the template, use a nested template with styles, or pass the text content of the CSS file.
For styling template content you can create `<style>` elements directly in the template, or pass the content of the CSS.
### Style Element
### CSS Content
Create `<style>` element inside of the main template using the Shadow DOM:
The preferred way to put styles is to use `css` helper from the result of the `html` or `svg` function:
```javascript
define({
tag: "my-element",
render: () => html`
<div>...</div>
<style>
div { color: red }
</style>
`,
});
```typescript
html`...`.css`div { color: ${value}; }`: Function
```
Styles are scoped and apply only to the elements in the `shadowRoot` for default render property configuration.
* **arguments**:
* CSS content in tagged template literals
* `value` - dynamic values concatenated with the template literal
* **returns**:
* an update function compatible with content expression
In the browser, which doesn't support Shadow DOM, ShadyCSS is used to create scoped CSS. The shim moves out the `<style>` element from the template, scopes selectors, and puts styles into the head of the document. It is done once, and before expressions are calculated, so expressions inside the style element cannot be processed correctly.
Expressions inside of the `<style>` element are only supported in the native implementation of the Shadow DOM. Although, creating dynamic styles can be inefficient (styles are not shared between elements instances) and opens the possibility for an XSS attack (for insecure inputs).
##### Breaks template: (using ShadyCSS) <!-- omit in toc -->
```javascript
html`
<style>
div { color: ${user ? 'blue' : 'black'}; }
</style>
<div>Color text</div>
`;
```
##### Works fine: <!-- omit in toc -->
```javascript
html`
<style>
div { color: black; }
div.user { color: blue; }
</style>
<div class="${{ user }}">Color text</div>
`
```
### Nested Template with Styles
Create a [nested template](nested-templates.md) with `<style>` element for sharing styles between custom elements (the style elements still are created for each instance of the custom element):
```javascript
const commonStyles = html`
<style>
:host { display: block }
:host[hidden] { display: none }
</style>
`;
define({

@@ -495,6 +453,4 @@ tag: "my-element",

<div>...</div>
${commonStyles}
<style>
div { color: red }
</style>
`.css`
div { color: red }
`,

@@ -504,5 +460,5 @@ });

### CSS Stylesheet
It is the most performant way, as if the Constructable Stylesheets are supported (currently, only Safari not yet supported it), generated CSS is shared between all of the elements with the same style.
For CSS content generated outside of the template, use `style` or `css` helper method from the result of the `html` or `svg` function:
For CSS content generated outside of the template, use `style` helper method:

@@ -518,16 +474,5 @@ ```typescript

```typescript
html`...`.css`div { color: ${value}; }`: Function
```
* **arguments**:
* CSS content in tagged template literals
* `value` - dynamic values concatenated with the template literal
* **returns**:
* an update function compatible with content expression
Style helpers work the best with bundlers, which support importing text content of the CSS files (for [webpack](https://github.com/webpack/webpack), use [raw-loader](https://github.com/webpack-contrib/raw-loader)). Still, you can create a string input in place, and pass it to the style helper.
```javascript
// `styles` should contain text content of the CSS
// `globals` and `styles` should contain text content of the CSS
// importing `.css` files requires a bundler feature, e.g. Vite
import globals from '../globals.css';

@@ -547,16 +492,4 @@ import styles from './MyElement.css';

});
// using css helper
define({
tag: "other-element",
render: () => html`
<div>...</div>
`.css`
div { color: red }
`,
});
```
The style helper supports passing the `CSSStyleSheet` instance, but it will work only for the mode described below. Do not use it if you target multiple environments, where it might not be yet supported.
#### Constructable Stylesheets

@@ -571,2 +504,20 @@

The style helper supports passing the `CSSStyleSheet` instance, but it will work only for the mode described above. Do not use it if you target multiple environments, where it might not be yet supported.
### Style Element
Create `<style>` element inside of the main template using the Shadow DOM:
```javascript
define({
tag: "my-element",
render: () => html`
<div>...</div>
<style>
div { color: red }
</style>
`,
});
```
## Limitations

@@ -573,0 +524,0 @@

{
"name": "hybrids",
"version": "8.0.10",
"version": "8.1.0",
"description": "A JavaScript framework for creating fully-featured web applications, components libraries, and single web components with unique declarative and functional architecture",

@@ -5,0 +5,0 @@ "type": "module",

@@ -26,2 +26,12 @@ /* eslint-disable no-undef */

if (!("document" in global)) {
Object.defineProperty(global, "document", {
value: {
importNode: () => {
throw Error("Current context does not support importing nodes");
},
},
});
}
return global;

@@ -28,0 +38,0 @@ }

@@ -1,2 +0,1 @@

import { compileTemplate } from "./template/core.js";
import { getPlaceholder } from "./template/utils.js";

@@ -6,2 +5,3 @@

import { probablyDevMode } from "./utils.js";
import { compile } from "./template/index.js";

@@ -168,39 +168,22 @@ const dictionary = new Map();

const htmlTemplates = new Map();
msg.html = function html(parts, ...args) {
const input = getString(parts, args);
return (host, target = host) => {
let render = htmlTemplates.get(input);
if (!render) {
render = compileTemplate(
input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
false,
true,
);
htmlTemplates.set(input, render);
}
render(host, target, args);
};
return compile(
input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
args,
false,
true,
);
};
const svgTemplates = new Map();
msg.svg = function svg(parts, ...args) {
const input = getString(parts, args);
return (host, target = host) => {
const id = input;
let render = svgTemplates.get(id);
if (!render) {
render = compileTemplate(
input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
true,
true,
);
svgTemplates.set(id, render);
}
render(host, target, args);
};
return compile(
input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
args,
true,
true,
);
};

@@ -672,6 +672,6 @@ /* eslint-disable no-use-before-define */

if (hasOwnProperty.call(data, key)) {
model[key] = nestedConfig.create(
data[key],
lastModel && lastModel[key],
);
model[key] =
data[key] === null
? nestedConfig.create({})
: nestedConfig.create(data[key], lastModel && lastModel[key]);
} else {

@@ -1419,3 +1419,3 @@ model[key] = lastModel ? lastModel[key] : nestedConfig.create({});

throw TypeError(
`Default value must be a string or a number: ${typeof defaultValue}`,
`Default value must be a string, number or boolean: ${typeof defaultValue}`,
);

@@ -1422,0 +1422,0 @@ }

@@ -5,2 +5,3 @@ import global from "../global.js";

import * as layout from "./layout.js";
import {

@@ -101,16 +102,2 @@ getMeta,

const styleSheetsMap = new Map();
function getCSSStyleSheet(style) {
let styleSheet = style;
if (!(styleSheet instanceof global.CSSStyleSheet)) {
styleSheet = styleSheetsMap.get(style);
if (!styleSheet) {
styleSheet = new global.CSSStyleSheet();
styleSheet.replaceSync(style);
styleSheetsMap.set(style, styleSheet);
}
}
return styleSheet;
}
function setupStyleUpdater(target) {

@@ -122,29 +109,37 @@ if (target.adoptedStyleSheets) {

if (styleSheets !== prevStyleSheets) {
if (styleSheets) {
styleSheets = styleSheets.map(getCSSStyleSheet);
if (styleSheets) {
styleSheets = styleSheets.map((style) => {
let styleSheet = style;
if (!(styleSheet instanceof global.CSSStyleSheet)) {
styleSheet = styleSheetsMap.get(style);
if (!styleSheet) {
styleSheet = new global.CSSStyleSheet();
styleSheet.replaceSync(style);
styleSheetsMap.set(style, styleSheet);
}
}
let isNotEqual =
!prevStyleSheets ||
prevStyleSheets.some(
(styleSheet, i) => styleSheet !== styleSheets[i],
);
return styleSheet;
});
if (isNotEqual) {
target.adoptedStyleSheets = (
prevStyleSheets
? adoptedStyleSheets.filter(
(styleSheet) => !prevStyleSheets.includes(styleSheet),
)
: adoptedStyleSheets
).concat(styleSheets);
}
} else {
target.adoptedStyleSheets = adoptedStyleSheets.filter(
(styleSheet) => !prevStyleSheets.includes(styleSheet),
);
if (
!prevStyleSheets ||
prevStyleSheets.some((styleSheet, i) => styleSheet !== styleSheets[i])
) {
// TODO: this might change order of already applied styles
target.adoptedStyleSheets = (
prevStyleSheets
? adoptedStyleSheets.filter(
(styleSheet) => !prevStyleSheets.includes(styleSheet),
)
: adoptedStyleSheets
).concat(styleSheets);
}
} else if (prevStyleSheets) {
target.adoptedStyleSheets = adoptedStyleSheets.filter(
(styleSheet) => !prevStyleSheets.includes(styleSheet),
);
}
prevStyleSheets = styleSheets;
}
prevStyleSheets = styleSheets;
};

@@ -177,7 +172,8 @@ }

export function compileTemplate(rawParts, isSVG, isMsg = false) {
const template = global.document.createElement("template");
export function compileTemplate(rawParts, isSVG, isMsg, useLayout) {
let template = global.document.createElement("template");
const parts = {};
const signature = isMsg ? rawParts : createSignature(rawParts);
template.innerHTML = isSVG ? `<svg>${signature}</svg>` : signature;

@@ -193,2 +189,31 @@

let hostLayout;
const layoutTemplate = template.content.children[0];
if (layoutTemplate instanceof global.HTMLTemplateElement) {
Array.from(layoutTemplate.attributes).forEach((attr) => {
const value = attr.value.trim();
if (attr.name.startsWith("layout") && value) {
if (value.match(PLACEHOLDER_REGEXP_ALL)) {
throw Error("Layout attribute cannot contain expressions");
}
hostLayout = layout.insertRule(
layoutTemplate,
attr.name.substr(6),
value,
true,
);
}
});
if (hostLayout !== undefined && template.content.children.length > 1) {
throw Error(
"Template, which uses layout system must have only the '<template>' root element",
);
}
useLayout = true;
template = layoutTemplate;
}
const compileWalker = createWalker(template.content);

@@ -332,2 +357,15 @@ const notDefinedElements = [];

const name = attr.name;
if (useLayout && name.startsWith("layout") && value) {
if (value.match(PLACEHOLDER_REGEXP_ALL)) {
throw Error("Layout attribute cannot contain expressions");
}
const className = layout.insertRule(node, name.substr(6), value);
node.removeAttribute(name);
node.classList.add(className);
return;
}
const equal = value.match(PLACEHOLDER_REGEXP_EQUAL);

@@ -396,4 +434,3 @@ if (equal) {

const partsKeys = Object.keys(parts);
return function updateTemplateInstance(host, target, args, styleSheets) {
return function updateTemplateInstance(host, target, args, styles) {
let meta = getMeta(target);

@@ -426,2 +463,6 @@

if (meta.hostLayout) {
host.classList.remove(meta.hostLayout);
}
removeTemplate(target);

@@ -448,7 +489,15 @@

} else {
if (useLayout) {
const className = `${hostLayout}-${host === target ? "c" : "s"}`;
host.classList.add(className);
meta.hostLayout = className;
}
target.appendChild(fragment);
}
if (useLayout) layout.inject(target);
}
meta.styles(styleSheets);
meta.styles(styles);

@@ -463,3 +512,3 @@ meta.markers.forEach((marker) => {

try {
marker.fn(host, marker.node, value, prevValue);
marker.fn(host, marker.node, value, prevValue, useLayout);
} catch (error) {

@@ -466,0 +515,0 @@ // eslint-disable-next-line no-console

@@ -6,5 +6,6 @@ import { compileTemplate } from "./core.js";

const PLACEHOLDER = getPlaceholder();
const PLACEHOLDER_SVG = getPlaceholder("svg");
const PLACEHOLDER_MSG = getPlaceholder("msg");
const PLACEHOLDER_LAYOUT = getPlaceholder("layout");
const styleMap = new WeakMap();
const methods = {

@@ -16,16 +17,11 @@ key(id) {

style(...styles) {
let list = styleMap.get(this);
if (!list) styleMap.set(this, (list = []));
this.styleSheets = this.styleSheets || [];
this.styleSheets.push(...styles);
styles.forEach((style) => {
if (style) list.push(style);
});
return this;
},
css(parts, ...args) {
let set = styleMap.get(this);
if (!set) styleMap.set(this, (set = []));
this.styleSheets = this.styleSheets || [];
set.push(
this.styleSheets.push(
parts.reduce(

@@ -42,34 +38,28 @@ (acc, part, index) =>

const htmlTemplates = new Map();
export function html(parts, ...args) {
function compile(host, target = host) {
let id = parts.join(PLACEHOLDER);
const templates = new Map();
export function compile(parts, args, isSVG, isMsg) {
function template(host, target = host) {
let id = isMsg ? parts + PLACEHOLDER_MSG : parts.join(PLACEHOLDER);
if (isSVG) id += PLACEHOLDER_SVG;
const useLayout = template.useLayout;
if (useLayout) id += PLACEHOLDER_LAYOUT;
let render = htmlTemplates.get(id);
let render = templates.get(id);
if (!render) {
render = compileTemplate(parts);
htmlTemplates.set(id, render);
render = compileTemplate(parts, isSVG, isMsg, useLayout);
templates.set(id, render);
}
render(host, target, args, styleMap.get(compile));
render(host, target, args, template.styleSheets);
}
return Object.assign(compile, methods);
return Object.assign(template, methods);
}
const svgTemplates = new Map();
export function html(parts, ...args) {
return compile(parts, args, false, false);
}
export function svg(parts, ...args) {
function compile(host, target = host) {
let id = parts.join(PLACEHOLDER);
let render = svgTemplates.get(id);
if (!render) {
render = compileTemplate(parts, true);
svgTemplates.set(id, render);
}
render(host, target, args, styleMap.get(compile));
}
return Object.assign(compile, methods);
return compile(parts, args, true, false);
}

@@ -76,0 +66,0 @@

@@ -23,3 +23,9 @@ import global from "../../global.js";

export default function resolveArray(host, target, value, resolveValue) {
export default function resolveArray(
host,
target,
value,
resolveValue,
isLayout,
) {
let lastEntries = arrayMap.get(target);

@@ -74,3 +80,9 @@ const entries = value.map((item, index) => ({

if (matchedEntry.value !== entry.value) {
resolveValue(host, entry.placeholder, entry.value, matchedEntry.value);
resolveValue(
host,
entry.placeholder,
entry.value,
matchedEntry.value,
isLayout,
);
}

@@ -83,3 +95,3 @@ } else {

);
resolveValue(host, entry.placeholder, entry.value);
resolveValue(host, entry.placeholder, entry.value, undefined, isLayout);
}

@@ -86,0 +98,0 @@

@@ -17,3 +17,9 @@ import global from "../../global.js";

export default function resolveValue(host, target, value, lastValue) {
export default function resolveValue(
host,
target,
value,
lastValue,
useLayout,
) {
const type = typeOf(value);

@@ -40,2 +46,3 @@ const lastType = typeOf(lastValue);

case "function":
if (useLayout) value.useLayout = true;
value(host, target);

@@ -42,0 +49,0 @@ break;

@@ -280,3 +280,3 @@ declare module "hybrids" {

...args: unknown[]
): UpdateFunction<E>;
): UpdateFunctionWithMethods<E>;

@@ -286,3 +286,3 @@ function svg<E>(

...args: unknown[]
): UpdateFunction<E>;
): UpdateFunctionWithMethods<E>;
}

@@ -304,2 +304,3 @@

css: (parts: TemplateStringsArray, ...args: unknown[]) => this;
layout: (rules: string = "column") => this;
}

@@ -306,0 +307,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc