From 1b73551da2937177a07cae79df6cb08b2f9ad54e Mon Sep 17 00:00:00 2001 From: Ben Thomson Date: Fri, 8 Sep 2023 15:39:31 +0800 Subject: [PATCH] Initial work on components --- .../src/abstracts/ReactivePluginBase.js | 2 +- .../src/abstracts/ReactiveSingleton.js | 2 +- packages/reactivity/src/lib/components.js | 67 +++++++++++++++++++ .../shared.js => lib/reactivity.js} | 8 ++- packages/reactivity/src/lib/util.js | 34 ++++++++++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 packages/reactivity/src/lib/components.js rename packages/reactivity/src/{abstracts/shared.js => lib/reactivity.js} (97%) create mode 100644 packages/reactivity/src/lib/util.js diff --git a/packages/reactivity/src/abstracts/ReactivePluginBase.js b/packages/reactivity/src/abstracts/ReactivePluginBase.js index 484009b..a47d186 100644 --- a/packages/reactivity/src/abstracts/ReactivePluginBase.js +++ b/packages/reactivity/src/abstracts/ReactivePluginBase.js @@ -4,7 +4,7 @@ import { reactivityMount, template, mountTo, -} from './shared'; +} from '../lib/reactivity'; /** * Reactive plugin base abstract. diff --git a/packages/reactivity/src/abstracts/ReactiveSingleton.js b/packages/reactivity/src/abstracts/ReactiveSingleton.js index 13b86ca..1e31155 100644 --- a/packages/reactivity/src/abstracts/ReactiveSingleton.js +++ b/packages/reactivity/src/abstracts/ReactiveSingleton.js @@ -4,7 +4,7 @@ import { reactivityMount, template, mountTo, -} from './shared'; +} from '../lib/reactivity'; /** * Reactive singleton abstract. diff --git a/packages/reactivity/src/lib/components.js b/packages/reactivity/src/lib/components.js new file mode 100644 index 0000000..1bedaef --- /dev/null +++ b/packages/reactivity/src/lib/components.js @@ -0,0 +1,67 @@ +/** + * @typedef {import('petite-vue/dist/types/context').Context} Context + * @typedef {import('../abstracts/ReactivePluginBase').default} ReactivePluginBase + * @typedef {import('../abstracts/ReactiveSingleton').default} ReactiveSingleton + */ + +/** + * Prepares components in a mounted element or template. + * + * @param {ReactivePluginBase|ReactiveSingleton} parent + * @param {HTMLElement} mountElement + */ +function prepareComponents(parent, mountElement) { + if (!parent.components || !parent.components()) { + return; + } + + const components = parent.components(); + + Object.keys(components).forEach((componentName) => { + const componentElements = mountElement.querySelectorAll(componentName); + + // Replace each component holder with a template tag using the correct directive + componentElements.forEach((componentElement) => { + const templateElement = document.createElement('ins'); + templateElement.setAttribute('v-component', componentName); + + if (componentElement.hasAttributes()) { + const { attributes } = componentElement; + + for (let i = 0; i < attributes.length; i += 1) { + const { name, value } = attributes[i]; + templateElement.setAttribute(name, value); + } + } + + componentElement.replaceWith(templateElement); + }); + }); +} + +/** + * Cleanup/unmount component + * + * @param {Context} context + */ +function componentCleanup(/* context */) { +} + +/** + * Directive to link components to a parent. + * + * @param {Context} context + * @param {ReactivePluginBase|ReactiveSingleton} parent + */ +function componentDirective(context /* , parent */) { + // const scopeKeys = Object.getOwnPropertyNames(context.ctx.scope); + + return () => { + componentCleanup(context); + }; +} + +export { + prepareComponents, + componentDirective, +}; diff --git a/packages/reactivity/src/abstracts/shared.js b/packages/reactivity/src/lib/reactivity.js similarity index 97% rename from packages/reactivity/src/abstracts/shared.js rename to packages/reactivity/src/lib/reactivity.js index 571ae8e..879a178 100644 --- a/packages/reactivity/src/abstracts/shared.js +++ b/packages/reactivity/src/lib/reactivity.js @@ -1,4 +1,5 @@ import { createApp, reactive } from 'petite-vue'; +import { componentDirective, prepareComponents } from './components'; /** * @typedef {import('../abstracts/ReactivePluginBase').default} ReactivePluginBase @@ -98,7 +99,12 @@ function reactivityMount(element = null) { } } - this.$app = createApp(this.$data).mount(mountedElement); + // Prepare components + prepareComponents(this, mountedElement); + + this.$app = createApp(this.$data) + .directive('component', (context) => componentDirective(context, this)) + .mount(mountedElement); this.$el = mountedElement; // Import next tick into plugin diff --git a/packages/reactivity/src/lib/util.js b/packages/reactivity/src/lib/util.js new file mode 100644 index 0000000..3694c89 --- /dev/null +++ b/packages/reactivity/src/lib/util.js @@ -0,0 +1,34 @@ +/** + * Decimal to hexidecimal + * + * @param {number} dec + * @returns {string} + */ +function dec2hex(dec) { + return dec.toString(16).padStart(2, '0'); +} + +const usedIds = []; + +/** + * Generates a random ID. + * + * @param {Number} length + * @return {string} + */ +function generateId(length = 32) { + const arr = new Uint8Array(length / 2); + let value = ''; + + do { + window.crypto.getRandomValues(arr); + value = Array.from(arr, dec2hex).join(''); + } while (usedIds.includes(value)); + + return value; +} + +export { + generateId, + dec2hex, +};