@dinoreic/fez 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -143
- package/dist/fez.js +16 -16
- package/dist/fez.js.map +4 -4
- package/dist/log.js +5 -0
- package/dist/log.js.map +7 -0
- package/dist/rollup.js.map +2 -2
- package/package.json +6 -2
- package/src/fez/compile.js +70 -47
- package/src/fez/connect.js +4 -4
- package/src/fez/defaults.js +69 -0
- package/src/fez/instance.js +55 -32
- package/src/fez/lib/template.js +4 -0
- package/src/fez/root.js +19 -0
- package/src/fez.js +3 -35
- package/src/log.js +154 -0
- package/src/rollup.js +0 -6
package/dist/log.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
(()=>{var u=o=>{let a=o.split(/(<\/?[^>]+>)/g).map(i=>i.trim()).filter(i=>i),n=0,t=[];for(let i=0;i<a.length;i++){let e=a[i],s=a[i+1],p=a[i+2];if(e.startsWith("<"))if(!e.startsWith("</")&&!e.endsWith("/>")&&s&&!s.startsWith("<")&&p&&p.startsWith("</")){let r=Math.max(0,n);t.push(" ".repeat(r)+e+s+p),i+=2}else if(e.startsWith("</")){n--;let r=Math.max(0,n);t.push(" ".repeat(r)+e)}else if(e.endsWith("/>")||e.includes(" />")){let r=Math.max(0,n);t.push(" ".repeat(r)+e)}else{let r=Math.max(0,n);t.push(" ".repeat(r)+e),n++}else if(e){let r=Math.max(0,n);t.push(" ".repeat(r)+e)}}return t.join(`
|
|
2
|
+
`)},c=(()=>{let o=[],a=[],n=0;return t=>{if(!document.body){window.requestAnimationFrame(()=>c(t));return}t instanceof Node&&(t=u(t.outerHTML));let i=typeof t;t===void 0&&(t="undefined"),t===null&&(t="null"),Array.isArray(t)?i="array":typeof t=="object"&&t!==null&&(i="object"),typeof t!="string"&&(t=JSON.stringify(t,(r,l)=>typeof l=="function"?String(l):l,2).replaceAll("<","<")),t=t.trim(),o.push(t+`
|
|
3
|
+
|
|
4
|
+
type: ${i}`),a.push(i);let e=document.getElementById("dump-dialog");e||(e=document.body.appendChild(document.createElement("div")),e.id="dump-dialog",e.style.cssText="position:fixed;top:30px;left:30px;right:50px;bottom:50px;background:#fff;border:1px solid#333;box-shadow:0 0 10px rgba(0,0,0,0.5);padding:20px;overflow:auto;z-index:9999;font:13px/1.4 monospace;white-space:pre");let s=parseInt(localStorage.getItem("_LOG_INDEX"));!isNaN(s)&&s>=0&&s<o.length?n=s:n=o.length-1;let p=()=>{let r=o.map((l,d)=>{let f="#f0f0f0";return d!==n&&(a[d]==="object"?f="#d6e3ef":a[d]==="array"&&(f="#d8d5ef")),`<button style="padding:4px 8px;margin:0;cursor:pointer;background:${d===n?"#333":f};color:${d===n?"#fff":"#000"}" data-index="${d}">${d+1}</button>`}).join("");e.innerHTML='<div style="display:flex;flex-direction:column;height:100%"><div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:10px"><div style="display:flex;flex-wrap:wrap;gap:4px;flex:1;margin-right:10px">'+r+'</div><button style="padding:4px 8px;cursor:pointer;flex-shrink:0">×</button></div><xmp style="flex:1;overflow:auto;margin:0;padding:0;color:#000;background:#fff;font-size:14px;line-height:22px">'+o[n]+"</xmp></div>",e.querySelector('button[style*="flex-shrink:0"]').onclick=()=>e.remove(),e.querySelectorAll("button[data-index]").forEach(l=>{l.onclick=()=>{n=parseInt(l.dataset.index),localStorage.setItem("_LOG_INDEX",n),p()}})};p()}})();typeof window<"u"&&(window.LOG=c,window.LOG_PP=u);var x=c;})();
|
|
5
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/log.js"],
|
|
4
|
+
"sourcesContent": ["// pretty print HTML\nconst LOG_PP = (html) => {\n const parts = html\n .split(/(<\\/?[^>]+>)/g)\n .map(p => p.trim())\n .filter(p => p);\n\n let indent = 0;\n const lines = [];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const nextPart = parts[i + 1];\n const nextNextPart = parts[i + 2];\n\n // Check if it's a tag\n if (part.startsWith('<')) {\n // Check if this is an opening tag followed by text and then its closing tag\n if (!part.startsWith('</') && !part.endsWith('/>') && nextPart && !nextPart.startsWith('<') && nextNextPart && nextNextPart.startsWith('</')) {\n // Combine them on one line\n const actualIndent = Math.max(0, indent);\n lines.push(' '.repeat(actualIndent) + part + nextPart + nextNextPart);\n i += 2; // Skip the next two parts\n }\n // Closing tag\n else if (part.startsWith('</')) {\n indent--;\n const actualIndent = Math.max(0, indent);\n lines.push(' '.repeat(actualIndent) + part);\n }\n // Self-closing tag\n else if (part.endsWith('/>') || part.includes(' />')) {\n const actualIndent = Math.max(0, indent);\n lines.push(' '.repeat(actualIndent) + part);\n }\n // Opening tag\n else {\n const actualIndent = Math.max(0, indent);\n lines.push(' '.repeat(actualIndent) + part);\n indent++;\n }\n }\n // Text node\n else if (part) {\n const actualIndent = Math.max(0, indent);\n lines.push(' '.repeat(actualIndent) + part);\n }\n }\n\n return lines.join('\\n');\n}\n\nconst LOG = (() => {\n const logs = [];\n const logTypes = []; // Track the original type of each log\n let currentIndex = 0;\n\n return o => {\n if (!document.body) {\n window.requestAnimationFrame( () => LOG(o) )\n return\n }\n\n if (o instanceof Node) {\n o = LOG_PP(o.outerHTML)\n }\n\n // Store the original type\n let originalType = typeof o;\n\n if (o === undefined) { o = 'undefined' }\n if (o === null) { o = 'null' }\n\n if (Array.isArray(o)) {\n originalType = 'array';\n } else if (typeof o === 'object' && o !== null) {\n originalType = 'object';\n }\n\n if (typeof o != 'string') {\n o = JSON.stringify(o, (key, value) => {\n if (typeof value === 'function') {\n return String(value);\n }\n return value;\n }, 2).replaceAll('<', '<')\n }\n\n o = o.trim()\n\n logs.push(o + `\\n\\ntype: ${originalType}`);\n logTypes.push(originalType);\n\n let d = document.getElementById('dump-dialog');\n if (!d) {\n d = document.body.appendChild(document.createElement('div'));\n d.id = 'dump-dialog';\n d.style.cssText =\n 'position:fixed;top:30px;left:30px;right:50px;bottom:50px;' +\n 'background:#fff;border:1px solid#333;box-shadow:0 0 10px rgba(0,0,0,0.5);' +\n 'padding:20px;overflow:auto;z-index:9999;font:13px/1.4 monospace;white-space:pre';\n }\n\n // Check if we have a saved index and it's still valid\n const savedIndex = parseInt(localStorage.getItem('_LOG_INDEX'));\n if (!isNaN(savedIndex) && savedIndex >= 0 && savedIndex < logs.length) {\n currentIndex = savedIndex;\n } else {\n currentIndex = logs.length - 1;\n }\n\n const renderContent = () => {\n const buttons = logs.map((_, i) => {\n let bgColor = '#f0f0f0'; // default\n if (i !== currentIndex) {\n if (logTypes[i] === 'object') {\n bgColor = '#d6e3ef'; // super light blue\n } else if (logTypes[i] === 'array') {\n bgColor = '#d8d5ef'; // super light indigo\n }\n }\n return `<button style=\"padding:4px 8px;margin:0;cursor:pointer;background:${i === currentIndex ? '#333' : bgColor};color:${i === currentIndex ? '#fff' : '#000'}\" data-index=\"${i}\">${i + 1}</button>`\n }).join('');\n\n d.innerHTML =\n '<div style=\"display:flex;flex-direction:column;height:100%\">' +\n '<div style=\"display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:10px\">' +\n '<div style=\"display:flex;flex-wrap:wrap;gap:4px;flex:1;margin-right:10px\">' + buttons + '</div>' +\n '<button style=\"padding:4px 8px;cursor:pointer;flex-shrink:0\">×</button>' +\n '</div>' +\n '<xmp style=\"flex:1;overflow:auto;margin:0;padding:0;color:#000;background:#fff;font-size:14px;line-height:22px\">' + logs[currentIndex] + '</xmp>' +\n '</div>';\n\n d.querySelector('button[style*=\"flex-shrink:0\"]').onclick = () => d.remove();\n\n d.querySelectorAll('button[data-index]').forEach(btn => {\n btn.onclick = () => {\n currentIndex = parseInt(btn.dataset.index);\n localStorage.setItem('_LOG_INDEX', currentIndex);\n renderContent();\n };\n });\n };\n\n renderContent();\n };\n})();\n\nif (typeof window !== 'undefined') {\n window.LOG = LOG\n window.LOG_PP = LOG_PP\n}\n\nexport default LOG\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["LOG_PP", "html", "parts", "p", "indent", "lines", "part", "nextPart", "nextNextPart", "actualIndent", "LOG", "logs", "logTypes", "currentIndex", "o", "originalType", "key", "value", "d", "savedIndex", "renderContent", "buttons", "_", "i", "bgColor", "btn", "log_default"]
|
|
7
|
+
}
|
package/dist/rollup.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/rollup.js"],
|
|
4
|
-
"sourcesContent": ["function fezPlugin() {\n return {\n name: 'fez-plugin',\n\n transform(code, filePath) {\n const baseName = filePath.split('/').pop().split('.');\n\n if (baseName[1] === 'fez') {\n code = code.replace(/`/g, '\\\\`').replace(/\\$/g, '\\\\$');\n const transformedCode = `Fez.compile('${baseName[0]}', \\`\\n${code}\\`)`;\n\n // if (baseName[0] === 'admin-menu') {\n // console.log('Transformed code:', baseName, transformedCode);\n // }\n\n return {\n code: transformedCode,\n map: null,\n };\n }\n },\n };\n}\n\
|
|
5
|
-
"mappings": "MAAA,SAASA,GAAY,CACnB,MAAO,CACL,KAAM,aAEN,UAAUC,EAAMC,EAAU,CACxB,IAAMC,EAAWD,EAAS,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,EAEpD,GAAIC,EAAS,CAAC,IAAM,MAClB,OAAAF,EAAOA,EAAK,QAAQ,KAAM,KAAK,EAAE,QAAQ,MAAO,KAAK,EAO9C,CACL,KAPsB,gBAAgBE,EAAS,CAAC,CAAC;AAAA,EAAUF,CAAI,MAQ/D,IAAK,IACP,CAEJ,CACF,CACF,
|
|
4
|
+
"sourcesContent": ["function fezPlugin() {\n return {\n name: 'fez-plugin',\n\n transform(code, filePath) {\n const baseName = filePath.split('/').pop().split('.');\n\n if (baseName[1] === 'fez') {\n code = code.replace(/`/g, '\\\\`').replace(/\\$/g, '\\\\$');\n const transformedCode = `Fez.compile('${baseName[0]}', \\`\\n${code}\\`)`;\n\n // if (baseName[0] === 'admin-menu') {\n // console.log('Transformed code:', baseName, transformedCode);\n // }\n\n return {\n code: transformedCode,\n map: null,\n };\n }\n },\n };\n}\n\nexport default fezPlugin;\n"],
|
|
5
|
+
"mappings": "MAAA,SAASA,GAAY,CACnB,MAAO,CACL,KAAM,aAEN,UAAUC,EAAMC,EAAU,CACxB,IAAMC,EAAWD,EAAS,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,EAEpD,GAAIC,EAAS,CAAC,IAAM,MAClB,OAAAF,EAAOA,EAAK,QAAQ,KAAM,KAAK,EAAE,QAAQ,MAAO,KAAK,EAO9C,CACL,KAPsB,gBAAgBE,EAAS,CAAC,CAAC;AAAA,EAAUF,CAAI,MAQ/D,IAAK,IACP,CAEJ,CACF,CACF,CAEA,IAAOG,EAAQJ",
|
|
6
6
|
"names": ["fezPlugin", "code", "filePath", "baseName", "rollup_default"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dinoreic/fez",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Runtime custom dom elements",
|
|
5
5
|
"main": "dist/fez.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"import": "./src/rollup.js",
|
|
14
14
|
"require": "./src/rollup.js"
|
|
15
15
|
},
|
|
16
|
+
"./log": {
|
|
17
|
+
"import": "./src/log.js",
|
|
18
|
+
"require": "./src/log.js"
|
|
19
|
+
},
|
|
16
20
|
"./package.json": "./package.json"
|
|
17
21
|
},
|
|
18
22
|
"bin": {
|
|
@@ -27,7 +31,7 @@
|
|
|
27
31
|
],
|
|
28
32
|
"scripts": {
|
|
29
33
|
"build": "bun build.js b",
|
|
30
|
-
"b": "bun build
|
|
34
|
+
"b": "bun run build",
|
|
31
35
|
"watch": "bun build.js w",
|
|
32
36
|
"server": "bun run lib/server.js",
|
|
33
37
|
"dev": "bunx concurrently --kill-others \"bun run server\" \"find src demo lib | entr -c sh -c 'bun run index && bun run b'\"",
|
package/src/fez/compile.js
CHANGED
|
@@ -36,8 +36,7 @@ const compileToClass = (html) => {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (result.head) {
|
|
39
|
-
const container =
|
|
40
|
-
container.innerHTML = result.head
|
|
39
|
+
const container = Fez.domRoot(result.head)
|
|
41
40
|
|
|
42
41
|
// Process all children of the container
|
|
43
42
|
Array.from(container.children).forEach(node => {
|
|
@@ -89,66 +88,86 @@ const compileToClass = (html) => {
|
|
|
89
88
|
return klass
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Fez.compile(templateNode) # compile template node
|
|
97
|
-
// Fez.compile('ui-form', templateNode.innerHTML) # compile string
|
|
98
|
-
export default function (tagName, html) {
|
|
99
|
-
if (tagName instanceof Node) {
|
|
100
|
-
const node = tagName
|
|
91
|
+
// Handle single argument cases - compile all, compile node, or compile from URL
|
|
92
|
+
function compile_bulk(data) {
|
|
93
|
+
if (data instanceof Node) {
|
|
94
|
+
const node = data
|
|
101
95
|
node.remove()
|
|
102
96
|
|
|
103
97
|
const fezName = node.getAttribute('fez')
|
|
104
98
|
|
|
105
99
|
// Check if fezName contains dot or slash (indicates URL)
|
|
106
100
|
if (fezName && (fezName.includes('.') || fezName.includes('/'))) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Fez.log(`Loading from ${url}`)
|
|
110
|
-
|
|
111
|
-
// Load HTML content via AJAX from URL
|
|
112
|
-
Fez.fetch(url)
|
|
113
|
-
.then(htmlContent => {
|
|
114
|
-
// Check if remote HTML has template/xmp tags with fez attribute
|
|
115
|
-
const parser = new DOMParser()
|
|
116
|
-
const doc = parser.parseFromString(htmlContent, 'text/html')
|
|
117
|
-
const fezElements = doc.querySelectorAll('template[fez], xmp[fez]')
|
|
118
|
-
|
|
119
|
-
if (fezElements.length > 0) {
|
|
120
|
-
// Compile each found fez element
|
|
121
|
-
fezElements.forEach(el => {
|
|
122
|
-
const name = el.getAttribute('fez')
|
|
123
|
-
if (name && !name.includes('-') && !name.includes('.') && !name.includes('/')) {
|
|
124
|
-
console.error(`Fez: Invalid custom element name "${name}". Custom element names must contain a dash (e.g., 'my-element', 'ui-button').`)
|
|
125
|
-
}
|
|
126
|
-
const content = el.innerHTML
|
|
127
|
-
Fez.compile(name, content)
|
|
128
|
-
})
|
|
129
|
-
} else {
|
|
130
|
-
// No fez elements found, use extracted name from URL
|
|
131
|
-
const name = url.split('/').pop().split('.')[0]
|
|
132
|
-
Fez.compile(name, htmlContent)
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
.catch(error => {
|
|
136
|
-
console.error(`FEZ template load error for "${fezName}": ${error.message}`)
|
|
137
|
-
})
|
|
101
|
+
compile_from_url(fezName)
|
|
138
102
|
return
|
|
139
103
|
} else {
|
|
140
104
|
// Validate fezName format for non-URL names
|
|
141
105
|
if (fezName && !fezName.includes('-')) {
|
|
142
106
|
console.error(`Fez: Invalid custom element name "${fezName}". Custom element names must contain a dash (e.g., 'my-element', 'ui-button').`)
|
|
143
107
|
}
|
|
144
|
-
|
|
145
|
-
|
|
108
|
+
// Compile the node directly
|
|
109
|
+
return compile(fezName, node.innerHTML)
|
|
146
110
|
}
|
|
147
111
|
}
|
|
148
|
-
else
|
|
149
|
-
|
|
112
|
+
else {
|
|
113
|
+
let root = data ? Fez.domRoot(data) : document.body
|
|
114
|
+
|
|
115
|
+
root.querySelectorAll('template[fez], xmp[fez]').forEach((n) => {
|
|
116
|
+
compile_bulk(n)
|
|
117
|
+
})
|
|
118
|
+
|
|
150
119
|
return
|
|
151
120
|
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function compile_from_url(url) {
|
|
124
|
+
Fez.log(`Loading from ${url}`)
|
|
125
|
+
|
|
126
|
+
// Load HTML content via AJAX from URL
|
|
127
|
+
Fez.fetch(url)
|
|
128
|
+
.then(htmlContent => {
|
|
129
|
+
// Check if remote HTML has template/xmp tags with fez attribute
|
|
130
|
+
const parser = new DOMParser()
|
|
131
|
+
const doc = parser.parseFromString(htmlContent, 'text/html')
|
|
132
|
+
const fezElements = doc.querySelectorAll('template[fez], xmp[fez]')
|
|
133
|
+
|
|
134
|
+
if (fezElements.length > 0) {
|
|
135
|
+
// Compile each found fez element
|
|
136
|
+
fezElements.forEach(el => {
|
|
137
|
+
const name = el.getAttribute('fez')
|
|
138
|
+
if (name && !name.includes('-') && !name.includes('.') && !name.includes('/')) {
|
|
139
|
+
console.error(`Fez: Invalid custom element name "${name}". Custom element names must contain a dash (e.g., 'my-element', 'ui-button').`)
|
|
140
|
+
}
|
|
141
|
+
const content = el.innerHTML
|
|
142
|
+
compile(name, content)
|
|
143
|
+
})
|
|
144
|
+
} else {
|
|
145
|
+
// No fez elements found, use extracted name from URL
|
|
146
|
+
const name = url.split('/').pop().split('.')[0]
|
|
147
|
+
compile(name, htmlContent)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
.catch(error => {
|
|
151
|
+
console.error(`FEZ template load error for "${url}": ${error.message}`)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// <template fez="ui-form">
|
|
156
|
+
// <script>
|
|
157
|
+
// ...
|
|
158
|
+
// Fez.compile() # compile all
|
|
159
|
+
// Fez.compile(templateNode) # compile template node or string with template or xmp tags
|
|
160
|
+
// Fez.compile('ui-form', templateNode.innerHTML) # compile string
|
|
161
|
+
function compile(tagName, html) {
|
|
162
|
+
// Handle single argument cases
|
|
163
|
+
if (arguments.length === 1) {
|
|
164
|
+
return compile_bulk(tagName)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If html contains </xmp>, send to compile_bulk for processing
|
|
168
|
+
if (html && html.includes('</xmp>')) {
|
|
169
|
+
return compile_bulk(html)
|
|
170
|
+
}
|
|
152
171
|
|
|
153
172
|
// Validate element name if it's not a URL
|
|
154
173
|
if (tagName && !tagName.includes('-') && !tagName.includes('.') && !tagName.includes('/')) {
|
|
@@ -168,7 +187,8 @@ export default function (tagName, html) {
|
|
|
168
187
|
styleContainer.id = 'fez-hidden-styles'
|
|
169
188
|
document.head.appendChild(styleContainer)
|
|
170
189
|
}
|
|
171
|
-
|
|
190
|
+
const allTags = [...Object.keys(Fez.classes), tagName].sort().join(', ')
|
|
191
|
+
styleContainer.textContent = `${allTags} { display: none; }\n`
|
|
172
192
|
}
|
|
173
193
|
|
|
174
194
|
// we cant try/catch javascript modules (they use imports)
|
|
@@ -190,3 +210,6 @@ export default function (tagName, html) {
|
|
|
190
210
|
}
|
|
191
211
|
}
|
|
192
212
|
}
|
|
213
|
+
|
|
214
|
+
export { compile_from_url }
|
|
215
|
+
export default compile
|
package/src/fez/connect.js
CHANGED
|
@@ -58,8 +58,8 @@ export default function(name, klass) {
|
|
|
58
58
|
|
|
59
59
|
// wrap slot to enable reactive re-renders. It will use existing .fez-slot if found
|
|
60
60
|
klass.html = klass.html.replace(/<slot\s*\/>|<slot\s*>\s*<\/slot>/g, () => {
|
|
61
|
-
const name = klass.
|
|
62
|
-
return `<${name} class="fez-slot"></${name}>`
|
|
61
|
+
const name = klass.SLOT || 'div'
|
|
62
|
+
return `<${name} class="fez-slot" fez-keep="default-slot"></${name}>`
|
|
63
63
|
})
|
|
64
64
|
|
|
65
65
|
klass.fezHtmlFunc = createTemplate(klass.html)
|
|
@@ -163,6 +163,8 @@ function connectNode(name, node) {
|
|
|
163
163
|
fez.fezRegister();
|
|
164
164
|
;(fez.init || fez.created || fez.connect).bind(fez)(fez.props);
|
|
165
165
|
fez.render()
|
|
166
|
+
fez.firstRender = true
|
|
167
|
+
fez.onMount(fez.props)
|
|
166
168
|
|
|
167
169
|
if (fez.onSubmit) {
|
|
168
170
|
const form = fez.root.nodeName == 'FORM' ? fez.root : fez.find('form')
|
|
@@ -172,8 +174,6 @@ function connectNode(name, node) {
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
fez.onMount(fez.props)
|
|
176
|
-
|
|
177
177
|
// if onPropsChange method defined, add observer and trigger call on all attributes once component is loaded
|
|
178
178
|
if (fez.onPropsChange) {
|
|
179
179
|
observer.observe(newNode, {attributes:true})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Wrap defaults in a function to avoid immediate execution
|
|
2
|
+
const loadDefaults = () => {
|
|
3
|
+
// include fez component by name
|
|
4
|
+
//<fez-component name="some-node" :props="fez.props"></fez-component>
|
|
5
|
+
Fez('fez-component', class {
|
|
6
|
+
FAST = true
|
|
7
|
+
|
|
8
|
+
init(props) {
|
|
9
|
+
const tag = document.createElement(props.name)
|
|
10
|
+
tag.props = props.props || props['data-props'] || props
|
|
11
|
+
|
|
12
|
+
while (this.root.firstChild) {
|
|
13
|
+
this.root.parentNode.insertBefore(this.root.lastChild, tag.nextSibling);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.root.innerHTML = ''
|
|
17
|
+
this.root.appendChild(tag)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// include remote data from url
|
|
22
|
+
// <fez-include src="./demo/fez/ui-slider.html"></fez-include>
|
|
23
|
+
Fez('fez-include', class {
|
|
24
|
+
FAST = true
|
|
25
|
+
|
|
26
|
+
init(props) {
|
|
27
|
+
Fez.fetch(props.src, (data)=>{
|
|
28
|
+
const dom = Fez.domRoot(data)
|
|
29
|
+
Fez.head(dom) // include scripts and load fez components
|
|
30
|
+
this.root.innerHTML = dom.innerHTML
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// include remote data from url
|
|
36
|
+
// <fez-inline :state="{count: 0}">
|
|
37
|
+
// <button onclick="fez.state.count += 1">+</button>
|
|
38
|
+
// {{ state.count }} * {{ state.count }} = {{ state.count * state.count }}
|
|
39
|
+
// </fez-inline>
|
|
40
|
+
Fez('fez-inline', class {
|
|
41
|
+
init(props) {
|
|
42
|
+
const html = this.root.innerHTML
|
|
43
|
+
|
|
44
|
+
if (this.root.innerHTML.includes('<')) {
|
|
45
|
+
const hash = Fez.fnv1(this.root.outerHTML)
|
|
46
|
+
const nodeName = `inline-${hash}`
|
|
47
|
+
Fez(nodeName, class {
|
|
48
|
+
FAST = true
|
|
49
|
+
HTML = html
|
|
50
|
+
init() {
|
|
51
|
+
Object.assign(this.state, props.state || {})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const el = document.createElement(nodeName)
|
|
56
|
+
this.root.after(this.root.lastChild, el);
|
|
57
|
+
this.root.remove()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Only load defaults if Fez is available
|
|
64
|
+
if (typeof Fez !== 'undefined' && Fez) {
|
|
65
|
+
loadDefaults()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Export for use in tests
|
|
69
|
+
export { loadDefaults }
|
package/src/fez/instance.js
CHANGED
|
@@ -286,14 +286,48 @@ export default class FezBase {
|
|
|
286
286
|
onDestroy() {}
|
|
287
287
|
onStateChange() {}
|
|
288
288
|
onGlobalStateChange() {}
|
|
289
|
-
|
|
289
|
+
|
|
290
|
+
// component publish will search for parent component that subscribes by name
|
|
291
|
+
publish(channel, ...args) {
|
|
292
|
+
const handle_publish = (component) => {
|
|
293
|
+
if (Fez._subs && Fez._subs[channel]) {
|
|
294
|
+
const sub = Fez._subs[channel].find(([comp]) => comp === component)
|
|
295
|
+
if (sub) {
|
|
296
|
+
sub[1].bind(component)(...args)
|
|
297
|
+
return true
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return false
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Check if current component has subscription
|
|
304
|
+
if (handle_publish(this)) {
|
|
305
|
+
return true
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Bubble up to parent components
|
|
309
|
+
let parent = this.root.parentElement
|
|
310
|
+
while (parent) {
|
|
311
|
+
if (parent.fez) {
|
|
312
|
+
if (handle_publish(parent.fez)) {
|
|
313
|
+
return true
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
parent = parent.parentElement
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// If no parent handled it, fall back to global publish
|
|
320
|
+
// Fez.publish(channel, ...args)
|
|
321
|
+
return false
|
|
322
|
+
}
|
|
323
|
+
|
|
290
324
|
fezBlocks = {}
|
|
291
325
|
|
|
292
326
|
parseHtml(text) {
|
|
293
327
|
const base = this.fezHtmlRoot.replaceAll('"', '"')
|
|
294
328
|
|
|
295
329
|
text = text
|
|
296
|
-
.replace(/(['"\s;])fez
|
|
330
|
+
.replace(/([!'"\s;])fez\.(\w)/g, `$1${base}$2`)
|
|
297
331
|
.replace(/>\s+</g, '><')
|
|
298
332
|
|
|
299
333
|
return text.trim()
|
|
@@ -348,22 +382,18 @@ export default class FezBase {
|
|
|
348
382
|
newNode.innerHTML = this.parseHtml(renderedTpl)
|
|
349
383
|
}
|
|
350
384
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
this.slot(this.root, slot.parentNode)
|
|
355
|
-
slot.parentNode.removeChild(slot)
|
|
356
|
-
}
|
|
385
|
+
newNode.querySelectorAll('[fez-keep]').forEach(newEl => {
|
|
386
|
+
const key = newEl.getAttribute('fez-keep')
|
|
387
|
+
const oldEl = this.root.querySelector(`[fez-keep="${key}"]`)
|
|
357
388
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
this.slot(this.root, newSlot)
|
|
389
|
+
if (oldEl) {
|
|
390
|
+
// Keep the old element in place of the new one
|
|
391
|
+
newEl.parentNode.replaceChild(oldEl, newEl)
|
|
392
|
+
} else if (key === 'default-slot') {
|
|
393
|
+
// First render - populate the slot with current root children
|
|
394
|
+
Array.from(this.root.childNodes).forEach(child => newEl.appendChild(child))
|
|
365
395
|
}
|
|
366
|
-
}
|
|
396
|
+
})
|
|
367
397
|
|
|
368
398
|
Fez.morphdom(this.root, newNode)
|
|
369
399
|
|
|
@@ -437,8 +467,7 @@ export default class FezBase {
|
|
|
437
467
|
refresh(selector) {
|
|
438
468
|
alert('NEEDS FIX and remove htmlTemplate')
|
|
439
469
|
if (selector) {
|
|
440
|
-
const n =
|
|
441
|
-
n.innerHTML = this.class.htmlTemplate
|
|
470
|
+
const n = Fez.domRoot(this.class.htmlTemplate)
|
|
442
471
|
const tpl = n.querySelector(selector).innerHTML
|
|
443
472
|
this.render(selector, tpl)
|
|
444
473
|
} else {
|
|
@@ -511,7 +540,7 @@ export default class FezBase {
|
|
|
511
540
|
|
|
512
541
|
// get root node child nodes as array
|
|
513
542
|
childNodes(func) {
|
|
514
|
-
|
|
543
|
+
let children = Array.from(this.root.children)
|
|
515
544
|
|
|
516
545
|
if (func) {
|
|
517
546
|
// Create temporary container to avoid ancestor-parent errors
|
|
@@ -520,12 +549,11 @@ export default class FezBase {
|
|
|
520
549
|
document.body.appendChild(tmpContainer)
|
|
521
550
|
children.forEach(child => tmpContainer.appendChild(child))
|
|
522
551
|
|
|
523
|
-
|
|
552
|
+
children = Array.from(tmpContainer.children).map(func)
|
|
524
553
|
document.body.removeChild(tmpContainer)
|
|
525
|
-
return list
|
|
526
|
-
} else {
|
|
527
|
-
return children
|
|
528
554
|
}
|
|
555
|
+
|
|
556
|
+
return children
|
|
529
557
|
}
|
|
530
558
|
|
|
531
559
|
subscribe(channel, func) {
|
|
@@ -565,19 +593,14 @@ export default class FezBase {
|
|
|
565
593
|
|
|
566
594
|
fezHide() {
|
|
567
595
|
const node = this.root
|
|
596
|
+
const nodes = this.childNodes()
|
|
568
597
|
const parent = this.root.parentNode
|
|
569
|
-
const fragment = document.createDocumentFragment();
|
|
570
598
|
|
|
571
|
-
|
|
572
|
-
fragment.appendChild(node.firstChild)
|
|
573
|
-
}
|
|
599
|
+
nodes.reverse().forEach(el => parent.insertBefore(el, node.nextSibling))
|
|
574
600
|
|
|
575
|
-
|
|
576
|
-
node.parentNode.replaceChild(fragment, node);
|
|
577
|
-
// parent.classList.add('fez')
|
|
578
|
-
// parent.classList.add(`fez-${this.fezName}`)
|
|
601
|
+
this.root.remove()
|
|
579
602
|
this.root = parent
|
|
580
|
-
return
|
|
603
|
+
return nodes
|
|
581
604
|
}
|
|
582
605
|
|
|
583
606
|
reactiveStore(obj, handler) {
|
package/src/fez/lib/template.js
CHANGED
package/src/fez/root.js
CHANGED
|
@@ -475,6 +475,25 @@ Fez.store = {
|
|
|
475
475
|
}
|
|
476
476
|
};
|
|
477
477
|
|
|
478
|
+
// create dom root and return it
|
|
479
|
+
Fez.domRoot = (data, name = 'div') => {
|
|
480
|
+
if (data instanceof Node) {
|
|
481
|
+
return data
|
|
482
|
+
} else {
|
|
483
|
+
const root = document.createElement(name)
|
|
484
|
+
root.innerHTML = data
|
|
485
|
+
return root
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// add class by name to node and remove it from siblings
|
|
490
|
+
Fez.activateNode = (node, klass = 'active') => {
|
|
491
|
+
Array.from(node.parentElement.children).forEach(child => {
|
|
492
|
+
child.classList.remove(klass)
|
|
493
|
+
})
|
|
494
|
+
node.classList.add(klass)
|
|
495
|
+
}
|
|
496
|
+
|
|
478
497
|
Fez.compile = compile
|
|
479
498
|
Fez.state = state
|
|
480
499
|
|
package/src/fez.js
CHANGED
|
@@ -6,6 +6,9 @@ if (typeof window !== 'undefined') window.FezBase = FezBase
|
|
|
6
6
|
import Fez from './fez/root.js'
|
|
7
7
|
if (typeof window !== 'undefined') window.Fez = Fez
|
|
8
8
|
|
|
9
|
+
// Load defaults after Fez is properly initialized
|
|
10
|
+
import('./fez/defaults.js')
|
|
11
|
+
|
|
9
12
|
// clear all unattached nodes
|
|
10
13
|
setInterval(() => {
|
|
11
14
|
for (const [key, el] of Fez.instances) {
|
|
@@ -58,40 +61,5 @@ observer.observe(document.documentElement, {
|
|
|
58
61
|
subtree: true
|
|
59
62
|
});
|
|
60
63
|
|
|
61
|
-
// fez custom tags
|
|
62
|
-
|
|
63
|
-
// include fez component by name
|
|
64
|
-
//<fez-component name="some-node" :props="fez.props"></fez-node>
|
|
65
|
-
Fez('fez-component', class {
|
|
66
|
-
FAST = true
|
|
67
|
-
|
|
68
|
-
init(props) {
|
|
69
|
-
const tag = document.createElement(props.name)
|
|
70
|
-
tag.props = props.props || props['data-props'] || props
|
|
71
|
-
|
|
72
|
-
while (this.root.firstChild) {
|
|
73
|
-
this.root.parentNode.insertBefore(this.root.lastChild, tag.nextSibling);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.root.innerHTML = ''
|
|
77
|
-
this.root.appendChild(tag)
|
|
78
|
-
}
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// include remote data from url
|
|
82
|
-
// <fez-include src="./demo/fez/ui-slider.html"></fez-include>
|
|
83
|
-
Fez('fez-include', class {
|
|
84
|
-
FAST = true
|
|
85
|
-
|
|
86
|
-
init(props) {
|
|
87
|
-
Fez.fetch(props.src, (data)=>{
|
|
88
|
-
const dom = document.createElement('div')
|
|
89
|
-
dom.innerHTML = data
|
|
90
|
-
Fez.head(dom) // include scripts and load fez components
|
|
91
|
-
this.root.innerHTML = dom.innerHTML
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
|
|
96
64
|
export default Fez
|
|
97
65
|
export { Fez }
|