@semantic-ui/component
Advanced tools
Comparing version 0.1.6 to 0.1.7
@@ -15,8 +15,8 @@ { | ||
"dependencies": { | ||
"@semantic-ui/query": "^0.1.6", | ||
"@semantic-ui/reactivity": "^0.1.6", | ||
"@semantic-ui/templating": "^0.1.6", | ||
"@semantic-ui/utils": "^0.1.6" | ||
"@semantic-ui/query": "^0.1.7", | ||
"@semantic-ui/reactivity": "^0.1.7", | ||
"@semantic-ui/templating": "^0.1.7", | ||
"@semantic-ui/utils": "^0.1.7" | ||
}, | ||
"version": "0.1.6" | ||
"version": "0.1.7" | ||
} |
import { unsafeCSS } from 'lit'; | ||
import { each, noop, isServer, kebabToCamel } from '@semantic-ui/utils'; | ||
import { each, noop, isServer, isClient, kebabToCamel } from '@semantic-ui/utils'; | ||
import { TemplateCompiler, Template } from '@semantic-ui/templating'; | ||
@@ -208,2 +208,5 @@ | ||
}; | ||
if(isClient && customElements.get(tagName)) { | ||
return webComponent; | ||
} | ||
customElements.define(tagName, webComponent); | ||
@@ -210,0 +213,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { nothing } from 'lit'; | ||
import { repeat } from 'lit/directives/repeat.js'; | ||
@@ -42,5 +43,6 @@ import { directive } from 'lit/directive.js'; | ||
renderItems() { | ||
const items = this.getItems(this.eachCondition); | ||
return repeat( | ||
this.items, | ||
(item, index) => this.getItemID(item, index), | ||
items, | ||
(item, index) => (this.getItemID(item, index)), | ||
(item, index) => this.getTemplate(item, index) | ||
@@ -47,0 +49,0 @@ ); |
@@ -1,6 +0,6 @@ | ||
import { noChange } from 'lit'; | ||
import { nothing, noChange } from 'lit'; | ||
import { directive } from 'lit/directive.js'; | ||
import { AsyncDirective } from 'lit/async-directive.js'; | ||
import { Reaction } from '@semantic-ui/reactivity'; | ||
import { fatal, isString, mapObject } from '@semantic-ui/utils'; | ||
import { isString, isEqual, mapObject } from '@semantic-ui/utils'; | ||
import { Template } from '@semantic-ui/templating'; | ||
@@ -10,2 +10,3 @@ | ||
export class RenderTemplateDirective extends AsyncDirective { | ||
constructor(partInfo) { | ||
@@ -17,87 +18,104 @@ super(partInfo); | ||
} | ||
render({ getTemplate, templateName, subTemplates, data, parentTemplate }) { | ||
const unpackData = (dataObj) => { | ||
return mapObject(dataObj, (val) => val()); | ||
}; | ||
const maybeCreateTemplate = () => { | ||
this.parentTemplate = parentTemplate; | ||
this.getTemplate = getTemplate; | ||
this.subTemplates = subTemplates; | ||
this.data = data; | ||
this.ast = null; | ||
// expression can evaluate to a template or a string | ||
// in the case of a string we will pull from subtemplates | ||
let templateName; | ||
let template; | ||
this.reaction = Reaction.create((computation) => { | ||
this.maybeCreateTemplate(); // reactive reference to template | ||
const dataContext = this.unpackData(this.data); // reactive reference to data | ||
const templateOrName = getTemplate(); | ||
// find template to render | ||
if(isString(templateOrName)) { | ||
templateName = templateOrName; | ||
template = subTemplates[templateName]; | ||
if (!template) { | ||
fatal( | ||
`Could not find template named "${templateName}"`, | ||
subTemplates | ||
); | ||
return false; | ||
} | ||
// end computation if element destroyed | ||
if (!this.isConnected) { | ||
computation.stop(); | ||
return; | ||
} | ||
else if(templateOrName instanceof Template) { | ||
// support passing in full templates using expressions | ||
template = templateOrName; | ||
templateName = template.templateName; | ||
// first run handled by main path | ||
if(computation.firstRun) { | ||
return; | ||
} | ||
// avoid recreating prototype if rendered already | ||
if(templateName == this.templateName) { | ||
return false; | ||
// this is an empty template | ||
if(this.template?.ast.length == 0) { | ||
return; | ||
} | ||
this.templateName = templateName; | ||
// clone if it has changed | ||
this.template = template.clone({ templateName, subTemplates, data: unpackData(data) }); | ||
return true; | ||
}; | ||
const attachTemplate = () => { | ||
const { parentNode, startNode, endNode } = this.part || {}; // stored from update | ||
const element = this.part?.options?.host; | ||
const renderRoot = element?.renderRoot; | ||
this.template.setElement(element); | ||
this.template.attach(renderRoot, { | ||
element, | ||
parentNode, | ||
startNode, | ||
endNode, | ||
}); | ||
if (parentTemplate) { | ||
this.template.setParent(parentTemplate); | ||
} | ||
}; | ||
const renderTemplate = () => { | ||
let html = this.template.render(); | ||
return html; | ||
}; | ||
const html = this.renderTemplate(dataContext); | ||
this.setValue(html); | ||
}); | ||
if (this.reaction) { | ||
return noChange; | ||
this.maybeCreateTemplate(); | ||
// this is an empty template | ||
if(this.template?.ast.length == 0) { | ||
return nothing; | ||
} | ||
return this.renderTemplate(); | ||
} | ||
this.reaction = Reaction.create((computation) => { | ||
if (!this.isConnected) { | ||
computation.stop(); | ||
return; | ||
renderTemplate(dataContext) { | ||
this.attachTemplate(); | ||
if(!dataContext) { | ||
dataContext = this.unpackData(this.data); | ||
} | ||
this.template.setDataContext(dataContext); | ||
return this.template.render(); | ||
} | ||
maybeCreateTemplate() { | ||
// expression can evaluate to a template or a string | ||
// in the case of a string we will pull from subtemplates | ||
let templateName; | ||
let template; | ||
const templateOrName = this.getTemplate(); | ||
// find template to render | ||
if(isString(templateOrName)) { | ||
templateName = templateOrName; | ||
template = this.subTemplates[templateName]; | ||
if (!template) { | ||
return false; | ||
} | ||
} | ||
else if(templateOrName instanceof Template) { | ||
// support passing in full templates using expressions | ||
template = templateOrName; | ||
templateName = template.templateName; | ||
} | ||
const hasCreated = maybeCreateTemplate(); // reactive reference | ||
const dataContext = unpackData(data); // reactive reference | ||
if (!computation.firstRun) { | ||
attachTemplate(); | ||
this.template.setDataContext(dataContext, { rerender: true }); | ||
this.setValue(renderTemplate()); | ||
} | ||
// avoid recreating if identical template | ||
if(isEqual(template.ast, this.ast)) { | ||
return; | ||
} | ||
// use ast for unique id | ||
this.ast = template.ast; | ||
this.template = template.clone({ | ||
templateName, | ||
subTemplates: this.subTemplates, | ||
data: this.unpackData(this.data) | ||
}); | ||
maybeCreateTemplate(); | ||
attachTemplate(); | ||
this.template.setDataContext(unpackData(data)); | ||
return renderTemplate(); | ||
} | ||
attachTemplate() { | ||
const { parentNode, startNode, endNode } = this.part || {}; // stored from update | ||
const element = this.part?.options?.host; | ||
const renderRoot = element?.renderRoot; | ||
this.template.setElement(element); | ||
this.template.attach(renderRoot, { | ||
element, | ||
parentNode, | ||
startNode, | ||
endNode, | ||
}); | ||
if (this.parentTemplate) { | ||
this.template.setParent(this.parentTemplate); | ||
} | ||
} | ||
unpackData(dataObj) { | ||
return mapObject(dataObj, (val) => val()); | ||
} | ||
update(part, settings) { | ||
@@ -111,2 +129,3 @@ this.part = part; | ||
} | ||
disconnected() { | ||
@@ -113,0 +132,0 @@ if (this.template) { |
import { html, svg } from 'lit'; | ||
import { Reaction, ReactiveVar } from '@semantic-ui/reactivity'; | ||
import { each, mapObject, wrapFunction, fatal, isArray, isPlainObject, isString, isFunction } from '@semantic-ui/utils'; | ||
import { each, mapObject, hashCode, wrapFunction, fatal, isArray, isPlainObject, isString, firstMatch, isFunction } from '@semantic-ui/utils'; | ||
@@ -18,6 +18,13 @@ import { reactiveData } from './directives/reactive-data.js'; | ||
static useSubtreeCache = false; // experimental | ||
static getID({ ast, data, isSVG } = {}) { | ||
return hashCode({ ast, data, isSVG }); | ||
} | ||
constructor({ ast, data, template, subTemplates, snippets, helpers, isSVG }) { | ||
this.ast = ast || ''; | ||
this.data = data; | ||
this.renderTrees = []; | ||
this.renderTrees = {}; // stores templates but garbage collectable | ||
this.treeIDs = []; // stored content ids | ||
this.template = template; | ||
@@ -29,2 +36,3 @@ this.subTemplates = subTemplates; | ||
this.isSVG = isSVG; | ||
this.id = LitRenderer.getID({ ast, data, isSVG }); | ||
} | ||
@@ -51,2 +59,9 @@ | ||
cachedRender(data) { | ||
if(data) { | ||
this.updateData(data); | ||
} | ||
return this.litTemplate; | ||
} | ||
readAST({ ast = this.ast, data = this.data } = {}) { | ||
@@ -128,2 +143,7 @@ each(ast, (node) => { | ||
/* | ||
The conditional directive takes an each conditions | ||
with over() and content(). it needs to | ||
return reactive values from renderer | ||
*/ | ||
evaluateEach(node, data) { | ||
@@ -139,5 +159,7 @@ const directiveMap = (value, key) => { | ||
return (eachData) => { | ||
// each data is @index, this, alias from curent position | ||
data = { ...this.data, ...eachData }; | ||
return this.renderContent({ | ||
ast: value, | ||
data: { ...data, ...eachData }, | ||
data, | ||
}); | ||
@@ -183,3 +205,2 @@ }; | ||
getPackedNodeData(node, data, { inheritParent = false } = {}) { | ||
const getPackedData = (unpackedData, options = {}) => { | ||
@@ -190,7 +211,11 @@ let packedData = {}; | ||
if(isString(unpackedData)) { | ||
unpackedData = this.evaluateExpression(unpackedData, data, options); | ||
// note this is currently not reactive on the 'getData' expression | ||
// so it will be locked in when evaluated | ||
const expression = unpackedData; // this is an expression like data=getData | ||
unpackedData = this.evaluateExpression(expression, data, options); | ||
packedData = mapObject(unpackedData, wrapFunction); | ||
} | ||
// okay now we have the data in both cases, lets pack it | ||
// this is a data object like {> someTemplate data={one: someExpr, two: someExpr } } | ||
if(isPlainObject(unpackedData)) { | ||
else if(isPlainObject(unpackedData)) { | ||
// this is a data object like {> someTemplate data={one: someExpr, two: someExpr } } | ||
packedData = mapObject(unpackedData, (expression) => this.getPackedValue(expression, data, options)); | ||
@@ -205,12 +230,8 @@ } | ||
// only inherit parent data context if specified | ||
let parentData = (inheritParent) | ||
? data | ||
: {} | ||
; | ||
const packedData = { | ||
...parentData, | ||
data = { | ||
...(inheritParent) ? this.data : {}, | ||
...packedStaticData, | ||
...packedReactiveData | ||
}; | ||
return packedData; | ||
return data; | ||
} | ||
@@ -419,4 +440,11 @@ | ||
// subtrees are rendered as separate contexts | ||
// subtrees are rendered as separate contexts stored as weakrefs for gc | ||
renderContent({ ast, data, isSVG = this.isSVG } = {}) { | ||
const contentID = LitRenderer.getID({ast, data, isSVG}); | ||
const treeRef = this.renderTrees[contentID]; | ||
const existingTree = treeRef ? treeRef.deref() : undefined; | ||
// disabled for now | ||
if (LitRenderer.useSubtreeCache && existingTree) { | ||
return existingTree.cachedRender(data); | ||
} | ||
const tree = new LitRenderer({ | ||
@@ -431,16 +459,35 @@ ast, | ||
}); | ||
this.renderTrees.push(tree); | ||
this.treeIDs.push(contentID); | ||
this.renderTrees[contentID] = new WeakRef(tree); | ||
return tree.render(); | ||
} | ||
cleanup() { | ||
this.renderTrees = []; | ||
} | ||
setData(data) { | ||
this.data = data; | ||
each(this.renderTrees, (tree) => { | ||
tree.updateData(data); | ||
setData(newData) { | ||
this.updateData(newData); | ||
this.updateSubtreeData(newData); | ||
} | ||
// yeah we're going there, weakrefs | ||
updateSubtreeData(newData) { | ||
each(this.renderTrees, (ref, contentID) => { | ||
const tree = ref.deref(); | ||
if(tree) { | ||
tree.updateData(newData); | ||
} | ||
}); | ||
} | ||
updateData(data) { | ||
each(data, (value, name) => { | ||
if(this.data[name] !== undefined && this.data[name] !== value) { | ||
/* | ||
Note this is important to preserve the object reference vs clobbering | ||
const a = { foo: 'baz' }; const b = a.foo; a.foo = 'bar'; | ||
*/ | ||
updateData(newData) { | ||
each(this.data, (value, name) => { | ||
delete this.data[name]; | ||
}); | ||
each(newData, (value, name) => { | ||
if(this.data[name] !== value) { | ||
this.data[name] = value; | ||
@@ -447,0 +494,0 @@ } |
import { LitElement } from 'lit'; | ||
import { each, isFunction, kebabToCamel, keys, unique, isServer, inArray, get } from '@semantic-ui/utils'; | ||
import { each, isFunction, isClassInstance, kebabToCamel, keys, unique, isServer, inArray, get } from '@semantic-ui/utils'; | ||
import { ReactiveVar } from '@semantic-ui/reactivity'; | ||
@@ -95,5 +95,11 @@ import { $ } from '@semantic-ui/query'; | ||
// or { foo: { type: String, defaultValue: 'baz' } // expert | ||
// we cant serialize custom classes | ||
const propertySettings = { | ||
propertyOnly: isClassInstance(defaultValue) | ||
}; | ||
properties[name] = (defaultValue?.type) | ||
? settings | ||
: WebComponentBase.getPropertySettings(name, defaultValue?.constructor) | ||
: WebComponentBase.getPropertySettings(name, defaultValue?.constructor, propertySettings) | ||
; | ||
@@ -105,3 +111,3 @@ }); | ||
static getPropertySettings(name, type = String) { | ||
static getPropertySettings(name, type = String, { propertyOnly = false } = {}) { | ||
let property = { | ||
@@ -113,4 +119,7 @@ type, | ||
// functions cannot be serialized | ||
if (type == Function) { | ||
if (propertyOnly || type == Function) { | ||
property.attribute = false; | ||
property.hasChanged = (newVal, oldVal) => { | ||
return true; | ||
}; | ||
} | ||
@@ -117,0 +126,0 @@ else if (type == Boolean) { |
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
50363
1480
15
2
- Removed@semantic-ui/component@0.1.7(transitive)
Updated@semantic-ui/query@^0.1.7
Updated@semantic-ui/utils@^0.1.7