rasti 4.1.2 → 4.1.3-alpha.0
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 +2 -2
- package/dist/rasti.js +26 -5
- package/dist/rasti.js.map +1 -1
- package/dist/rasti.min.js +1 -1
- package/dist/rasti.min.js.map +1 -1
- package/es/Component.js +4 -2
- package/es/Component.js.map +1 -1
- package/es/View.js +4 -2
- package/es/View.js.map +1 -1
- package/es/index.js +1 -0
- package/es/index.js.map +1 -1
- package/es/utils/defineOwn.js +17 -0
- package/es/utils/defineOwn.js.map +1 -0
- package/es/utils/parseHTML.js +5 -1
- package/es/utils/parseHTML.js.map +1 -1
- package/lib/Component.cjs +4 -2
- package/lib/Component.cjs.map +1 -1
- package/lib/View.cjs +4 -2
- package/lib/View.cjs.map +1 -1
- package/lib/index.cjs +1 -0
- package/lib/index.cjs.map +1 -1
- package/lib/utils/defineOwn.cjs +19 -0
- package/lib/utils/defineOwn.cjs.map +1 -0
- package/lib/utils/parseHTML.cjs +5 -1
- package/lib/utils/parseHTML.cjs.map +1 -1
- package/package.json +1 -1
- package/src/Component.js +4 -2
- package/src/View.js +4 -2
- package/src/utils/defineOwn.js +16 -0
- package/src/utils/parseHTML.js +5 -1
- package/types/Component.d.ts +45 -13
- package/types/Model.d.ts +19 -9
- package/types/View.d.ts +70 -20
- package/types/index.d.ts +1 -1
package/es/View.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"View.js","sources":["../src/View.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport validateListener from './utils/validateListener.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport __DEV__ from './utils/dev.js';\n\n/*\n * These option keys will be extended on the view instance.\n */\nconst viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];\n\n/**\n * - Listens for changes and renders the UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A `View` is an atomic unit of the user interface that can render data from a specific model or multiple models.\n * However, views can also be independent and have no associated data. \n * Models must be unaware of views. Views, on the other hand, may render model data and listen to the change events \n * emitted by the models to re-render themselves based on changes. \n * Each `View` has a root element, `this.el`, which is used for event delegation. \n * All element lookups are scoped to this element, and any rendering or DOM manipulations should be done inside it. \n * If `this.el` is not present, an element will be created using `this.tag` (defaulting to `div`) and `this.attributes`.\n * @module\n * @extends Emitter\n * @param {object} options Object containing options. The following keys will be merged into the view instance: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.\n * @property {node|Function} el Every view has a root DOM element stored at `this.el`. If not present, it will be created. If `this.el` is a function, it will be called to get the element at `this.ensureElement`, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {string|Function} tag If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. Default is `div`. If it is a function, it will be called to get the tag, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} attributes If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. If it is a function, it will be called to get the attributes object, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.\n * @property {object} model A model or any object containing data and business logic.\n * @property {Function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}. \n * @property {string} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.\n * @example\n * import { View, Model } from 'rasti';\n * \n * class Timer extends View {\n * constructor(options) {\n * super(options);\n * // Create model to store internal state. Set `seconds` attribute to 0.\n * this.model = new Model({ seconds : 0 });\n * // Listen to changes in model `seconds` attribute and re-render.\n * this.model.on('change:seconds', this.render.bind(this));\n * // Increment model `seconds` attribute every 1000 milliseconds.\n * this.interval = setInterval(() => this.model.seconds++, 1000);\n * }\n *\n * template(model) {\n * return `Seconds: <span>${View.sanitize(model.seconds)}</span>`;\n * }\n *\n * render() {\n * if (this.template) {\n * this.el.innerHTML = this.template(this.model);\n * }\n * return this;\n * }\n * }\n * // Render view and append view's element into the body.\n * document.body.appendChild(new Timer().render().el);\n */\nexport default class View extends Emitter {\n constructor(options = {}) {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Store delegated event listeners,\n // so they can be unbound later.\n this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n this.children = [];\n // Mutable array to store handlers to be called on destroy.\n this.destroyQueue = [];\n this.viewOptions = [];\n // Extend \"this\" with options.\n viewOptions.forEach(key => {\n if (key in options) {\n this[key] = options[key];\n this.viewOptions.push(key);\n }\n });\n // Ensure that the view has a unique id at `this.uid`.\n this.ensureUid();\n // Ensure that the view has a root element at `this.el`.\n this.ensureElement();\n }\n\n /**\n * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.\n * @param {object} options The view options.\n */\n preinitialize() {}\n\n /**\n * Returns the first element that matches the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node} Element matching selector within the view's root element (`this.el`).\n */\n $(selector) {\n return this.el.querySelector(selector);\n }\n\n /**\n * Returns a list of elements that match the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node[]} List of elements matching selector within the view's root element (`this.el`).\n */\n $$(selector) {\n return this.el.querySelectorAll(selector);\n }\n\n /**\n * Destroy the view.\n * Destroy children views if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.\n * @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.\n * @return {View} Return `this` for chaining.\n */\n destroy() {\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate `this.el` event listeners\n this.undelegateEvents();\n // Stop listening to events.\n this.stopListening();\n // Unbind `this` events.\n this.off();\n // Call destroy queue.\n this.destroyQueue.forEach(fn => fn());\n this.destroyQueue = [];\n // Call onDestroy lifecycle method\n this.onDestroy.apply(this, arguments);\n // Set destroyed flag.\n this.destroyed = true;\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `onDestroy` lifecycle method is called after the view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {View} child\n * @return {View}\n */\n addChild(child) {\n this.children.push(child);\n return child;\n }\n\n /**\n * Call destroy method on children views.\n */\n destroyChildren() {\n this.children.forEach(child => child.destroy());\n this.children = [];\n }\n\n /**\n * Ensure that the view has a unique id at `this.uid`.\n */\n ensureUid() {\n if (!this.uid) this.uid = `r${++View.uid}`;\n }\n\n /**\n * Ensure that the view has a root element at `this.el`.\n * You shouldn't call this method directly. It's called from the constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n ensureElement() {\n // Element is already present.\n if (this.el) {\n // If \"this.el\" is a function, call it to get the element.\n this.el = getResult(this.el, this);\n } else {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n const tag = getResult(this.tag, this);\n const attrs = getResult(this.attributes, this);\n this.el = this.createElement(tag, attrs);\n }\n // Delegate events on element.\n this.delegateEvents();\n }\n\n /**\n * Create an element.\n * Called from the constructor if `this.el` is undefined, to ensure\n * the view has a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attributes Attributes for the element.\n * @return {node} The created element.\n */\n createElement(tag = 'div', attributes = {}) {\n // Create DOM element.\n let el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attributes)\n .forEach(key => el.setAttribute(key, attributes[key]));\n\n return el;\n }\n\n /**\n * Remove `this.el` from the DOM.\n * @return {View} Return `this` for chaining.\n */\n removeElement() {\n this.el.parentNode.removeChild(this.el);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Provide declarative listeners for DOM events within a view. If an events object is not provided, \n * it defaults to using `this.events`. If `this.events` is a function, it will be called to get the events object.\n * \n * The events object should follow the format `{'event selector': 'listener'}`:\n * - `event`: The type of event (e.g., 'click').\n * - `selector`: A CSS selector to match the event target. If omitted, the event is bound to the root element.\n * - `listener`: A function or a string representing a method name on the view. The method will be called with `this` bound to the view instance.\n * \n * By default, `delegateEvents` is called within the View's constructor. If you have a simple events object, \n * all of your DOM events will be connected automatically, and you will not need to call this function manually.\n * \n * All attached listeners are bound to the view, ensuring that `this` refers to the view object when the listeners are invoked.\n * When `delegateEvents` is called again, possibly with a different events object, all previous listeners are removed and delegated afresh.\n * \n * **Listener signature:** `(event, view, matched)`\n * - `event`: The native DOM event object.\n * - `view`: The current view instance (`this`).\n * - `matched`: The element that satisfies the selector. If no selector is provided, it will be the view's root element (`this.el`).\n *\n * If more than one ancestor between `event.target` and the view's root element matches the selector, the listener will be\n * invoked **once for each matched element** (from inner to outer).\n *\n * @param {object} [events] Object in the format `{'event selector' : 'listener'}`. Used to bind delegated event listeners to the root element.\n * @return {View} Returns `this` for chaining.\n * @example\n * // Using prototype (recommended for static events)\n * class Modal extends View {\n * onClickOk(event, view, matched) {\n * // matched === the button.ok element that was clicked\n * this.close();\n * }\n * \n * onClickCancel() {\n * this.destroy();\n * }\n * }\n * Modal.prototype.events = {\n * 'click button.ok': 'onClickOk',\n * 'click button.cancel': 'onClickCancel',\n * 'submit form': 'onSubmit'\n * };\n * \n * // Using a function for dynamic events\n * class DynamicView extends View {\n * events() {\n * return {\n * [`click .${this.model.buttonClass}`]: 'onButtonClick',\n * 'click': 'onRootClick'\n * };\n * }\n * }\n */\n delegateEvents(events) {\n if (!events) events = getResult(this.events, this);\n if (!events) return this;\n\n if (this.delegatedEventListeners.length) this.undelegateEvents();\n\n // Store events by type i.e.: \"click\", \"submit\", etc.\n const eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const match = key.match(/^(\\w+)(?:\\s+(.+))*$/);\n\n if (!match) {\n const message = `Invalid event format: ${key}`;\n throw new Error(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n // Extract type and selector from the event key.\n const [,type, selector] = match;\n\n let listener = events[key];\n // Listener may be a string representing a method name on the view, or a function.\n if (typeof listener === 'string') listener = this[listener];\n // Validate listener is a function.\n validateListener(listener);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push([selector, listener]);\n });\n\n Object.keys(eventTypes).forEach(type => {\n // Listener for the type of event.\n const typeListener = (event) => {\n let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node) {\n if (node.matches) {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(([selector, listener]) => {\n if ((node === this.el && !selector) || (node !== this.el && node.matches(selector))) {\n listener.call(this, event, this, node);\n }\n });\n }\n // Continue traversing ancestors until reaching the view root (`this.el`) or stopping propagation.\n node = node === this.el || event.cancelBubble ? null : node.parentElement;\n }\n };\n // Store the type and listener in the delegated event listeners array.\n this.delegatedEventListeners.push([type, typeListener]);\n // Add the event listener to the element.\n this.el.addEventListener(type, typeListener);\n });\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Removes all of the view's delegated events. \n * Useful if you want to disable or remove a view from the DOM temporarily. \n * Called automatically when the view is destroyed and when `delegateEvents` is called again.\n * @return {View} Return `this` for chaining.\n */\n undelegateEvents() {\n this.delegatedEventListeners.forEach(([type, listener]) => {\n this.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `render` is the core function that your view should override, in order to populate its element (`this.el`), with the appropriate HTML. The convention is for `render` to always return `this`. \n * Views are low-level building blocks for creating user interfaces. For most use cases, we recommend using {@link #module_component Component} instead, which provides a more declarative template syntax, automatic DOM updates, and a more efficient render pipeline. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering.\n * \n * @return {View} Returns `this` for chaining.\n * @example\n * class UserView extends View {\n * render() {\n * if (this.template) {\n * const model = this.model;\n * // Sanitize model attributes to prevent XSS attacks.\n * const safeData = {\n * name : View.sanitize(model.name),\n * email : View.sanitize(model.email),\n * bio : View.sanitize(model.bio)\n * };\n * this.el.innerHTML = this.template(safeData);\n * }\n * return this;\n * }\n * }\n */\n render() {\n return this;\n }\n\n /**\n * Escape HTML entities in a string.\n * Use this method to sanitize user-generated content before inserting it into the DOM.\n * Override this method to provide a custom escape function.\n * This method is inherited by {@link #module_component Component} and used to escape template interpolations.\n * @static\n * @param {string} value String to escape.\n * @return {string} Escaped string.\n */\n static sanitize(value) {\n return `${value}`.replace(/[&<>\"']/g, match => ({\n '&' : '&',\n '<' : '<',\n '>' : '>',\n '\"' : '"',\n '\\'' : '''\n }[match]));\n }\n\n /**\n * Reset the unique ID counter to 0.\n * This is useful for server-side rendering scenarios where you want to ensure that\n * the generated unique IDs match those on the client, enabling seamless hydration of components.\n * This method is inherited by {@link #module_component Component}.\n * @static\n */\n static resetUid() {\n View.uid = 0;\n }\n}\n\n/**\n * Counter for generating unique IDs for view instances. \n * This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like \n * generating element IDs. \n * {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration. \n * For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated \n * unique IDs match those on the client, enabling seamless hydration of components. \n * @static\n * @memberof module:View\n * @name uid\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"View.js","sources":["../src/View.js"],"sourcesContent":["import Emitter from './Emitter.js';\nimport getResult from './utils/getResult.js';\nimport defineOwn from './utils/defineOwn.js';\nimport validateListener from './utils/validateListener.js';\nimport createDevelopmentErrorMessage from './utils/createDevelopmentErrorMessage.js';\nimport createProductionErrorMessage from './utils/createProductionErrorMessage.js';\nimport __DEV__ from './utils/dev.js';\n\n/*\n * These option keys will be extended on the view instance.\n */\nconst viewOptions = ['el', 'tag', 'attributes', 'events', 'model', 'template', 'onDestroy'];\n\n/**\n * - Listens for changes and renders the UI.\n * - Handles user input and interactivity.\n * - Sends captured input to the model.\n *\n * A `View` is an atomic unit of the user interface that can render data from a specific model or multiple models.\n * However, views can also be independent and have no associated data. \n * Models must be unaware of views. Views, on the other hand, may render model data and listen to the change events \n * emitted by the models to re-render themselves based on changes. \n * Each `View` has a root element, `this.el`, which is used for event delegation. \n * All element lookups are scoped to this element, and any rendering or DOM manipulations should be done inside it. \n * If `this.el` is not present, an element will be created using `this.tag` (defaulting to `div`) and `this.attributes`.\n * @module\n * @extends Emitter\n * @param {object} options Object containing options. The following keys will be merged into the view instance: `el`, `tag`, `attributes`, `events`, `model`, `template`, `onDestroy`.\n * @property {node|Function} el Every view has a root DOM element stored at `this.el`. If not present, it will be created. If `this.el` is a function, it will be called to get the element at `this.ensureElement`, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {string|Function} tag If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. Default is `div`. If it is a function, it will be called to get the tag, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} attributes If `this.el` is not present, an element will be created using `this.tag` and `this.attributes`. If it is a function, it will be called to get the attributes object, bound to the view instance. See {@link module_view__ensureelement View.ensureElement}.\n * @property {object|Function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.\n * @property {object} model A model or any object containing data and business logic.\n * @property {Function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}. \n * @property {string} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.\n * @example\n * import { View, Model } from 'rasti';\n * \n * class Timer extends View {\n * constructor(options) {\n * super(options);\n * // Create model to store internal state. Set `seconds` attribute to 0.\n * this.model = new Model({ seconds : 0 });\n * // Listen to changes in model `seconds` attribute and re-render.\n * this.model.on('change:seconds', this.render.bind(this));\n * // Increment model `seconds` attribute every 1000 milliseconds.\n * this.interval = setInterval(() => this.model.seconds++, 1000);\n * }\n *\n * template(model) {\n * return `Seconds: <span>${View.sanitize(model.seconds)}</span>`;\n * }\n *\n * render() {\n * if (this.template) {\n * this.el.innerHTML = this.template(this.model);\n * }\n * return this;\n * }\n * }\n * // Render view and append view's element into the body.\n * document.body.appendChild(new Timer().render().el);\n */\nexport default class View extends Emitter {\n constructor(options = {}) {\n super();\n // Call preinitialize.\n this.preinitialize.apply(this, arguments);\n // Store delegated event listeners,\n // so they can be unbound later.\n this.delegatedEventListeners = [];\n // Store child views,\n // so they can be destroyed.\n this.children = [];\n // Mutable array to store handlers to be called on destroy.\n this.destroyQueue = [];\n this.viewOptions = [];\n // Extend \"this\" with options, as own properties so an option overrides\n // a getter declared by a subclass instead of being assigned through it.\n viewOptions.forEach(key => {\n if (key in options) {\n defineOwn(this, key, options[key]);\n this.viewOptions.push(key);\n }\n });\n // Ensure that the view has a unique id at `this.uid`.\n this.ensureUid();\n // Ensure that the view has a root element at `this.el`.\n this.ensureElement();\n }\n\n /**\n * If you define a preinitialize method, it will be invoked when the view is first created, before any instantiation logic is run.\n * @param {object} options The view options.\n */\n preinitialize() {}\n\n /**\n * Returns the first element that matches the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node} Element matching selector within the view's root element (`this.el`).\n */\n $(selector) {\n return this.el.querySelector(selector);\n }\n\n /**\n * Returns a list of elements that match the selector, \n * scoped to DOM elements within the current view's root element (`this.el`).\n * @param {string} selector CSS selector.\n * @return {node[]} List of elements matching selector within the view's root element (`this.el`).\n */\n $$(selector) {\n return this.el.querySelectorAll(selector);\n }\n\n /**\n * Destroy the view.\n * Destroy children views if any, undelegate events, stop listening to events, call `onDestroy` lifecycle method.\n * @param {object} options Options object or any arguments passed to `destroy` method will be passed to `onDestroy` method.\n * @return {View} Return `this` for chaining.\n */\n destroy() {\n // Call destroy on children.\n this.destroyChildren();\n // Undelegate `this.el` event listeners\n this.undelegateEvents();\n // Stop listening to events.\n this.stopListening();\n // Unbind `this` events.\n this.off();\n // Call destroy queue.\n this.destroyQueue.forEach(fn => fn());\n this.destroyQueue = [];\n // Call onDestroy lifecycle method\n this.onDestroy.apply(this, arguments);\n // Set destroyed flag.\n this.destroyed = true;\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `onDestroy` lifecycle method is called after the view is destroyed.\n * Override with your code. Useful to stop listening to model's events.\n * @param {object} options Options object or any arguments passed to `destroy` method.\n */\n onDestroy() {}\n\n /**\n * Add a view as a child.\n * Children views are stored at `this.children`, and destroyed when the parent is destroyed.\n * Returns the child for chaining.\n * @param {View} child\n * @return {View}\n */\n addChild(child) {\n this.children.push(child);\n return child;\n }\n\n /**\n * Call destroy method on children views.\n */\n destroyChildren() {\n this.children.forEach(child => child.destroy());\n this.children = [];\n }\n\n /**\n * Ensure that the view has a unique id at `this.uid`.\n */\n ensureUid() {\n if (!this.uid) this.uid = `r${++View.uid}`;\n }\n\n /**\n * Ensure that the view has a root element at `this.el`.\n * You shouldn't call this method directly. It's called from the constructor.\n * You may override it if you want to use a different logic or to \n * postpone element creation.\n */\n ensureElement() {\n // Element is already present.\n if (this.el) {\n // If \"this.el\" is a function, call it to get the element.\n this.el = getResult(this.el, this);\n } else {\n // If \"this.el\" is not present,\n // create a new element according \"this.tag\"\n // and \"this.attributes\".\n const tag = getResult(this.tag, this);\n const attrs = getResult(this.attributes, this);\n this.el = this.createElement(tag, attrs);\n }\n // Delegate events on element.\n this.delegateEvents();\n }\n\n /**\n * Create an element.\n * Called from the constructor if `this.el` is undefined, to ensure\n * the view has a root element.\n * @param {string} tag Tag for the element. Default to `div`\n * @param {object} attributes Attributes for the element.\n * @return {node} The created element.\n */\n createElement(tag = 'div', attributes = {}) {\n // Create DOM element.\n let el = document.createElement(tag);\n // Add element attributes.\n Object.keys(attributes)\n .forEach(key => el.setAttribute(key, attributes[key]));\n\n return el;\n }\n\n /**\n * Remove `this.el` from the DOM.\n * @return {View} Return `this` for chaining.\n */\n removeElement() {\n this.el.parentNode.removeChild(this.el);\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Provide declarative listeners for DOM events within a view. If an events object is not provided, \n * it defaults to using `this.events`. If `this.events` is a function, it will be called to get the events object.\n * \n * The events object should follow the format `{'event selector': 'listener'}`:\n * - `event`: The type of event (e.g., 'click').\n * - `selector`: A CSS selector to match the event target. If omitted, the event is bound to the root element.\n * - `listener`: A function or a string representing a method name on the view. The method will be called with `this` bound to the view instance.\n * \n * By default, `delegateEvents` is called within the View's constructor. If you have a simple events object, \n * all of your DOM events will be connected automatically, and you will not need to call this function manually.\n * \n * All attached listeners are bound to the view, ensuring that `this` refers to the view object when the listeners are invoked.\n * When `delegateEvents` is called again, possibly with a different events object, all previous listeners are removed and delegated afresh.\n * \n * **Listener signature:** `(event, view, matched)`\n * - `event`: The native DOM event object.\n * - `view`: The current view instance (`this`).\n * - `matched`: The element that satisfies the selector. If no selector is provided, it will be the view's root element (`this.el`).\n *\n * If more than one ancestor between `event.target` and the view's root element matches the selector, the listener will be\n * invoked **once for each matched element** (from inner to outer).\n *\n * @param {object} [events] Object in the format `{'event selector' : 'listener'}`. Used to bind delegated event listeners to the root element.\n * @return {View} Returns `this` for chaining.\n * @example\n * // Using prototype (recommended for static events)\n * class Modal extends View {\n * onClickOk(event, view, matched) {\n * // matched === the button.ok element that was clicked\n * this.close();\n * }\n * \n * onClickCancel() {\n * this.destroy();\n * }\n * }\n * Modal.prototype.events = {\n * 'click button.ok': 'onClickOk',\n * 'click button.cancel': 'onClickCancel',\n * 'submit form': 'onSubmit'\n * };\n * \n * // Using a function for dynamic events\n * class DynamicView extends View {\n * events() {\n * return {\n * [`click .${this.model.buttonClass}`]: 'onButtonClick',\n * 'click': 'onRootClick'\n * };\n * }\n * }\n */\n delegateEvents(events) {\n if (!events) events = getResult(this.events, this);\n if (!events) return this;\n\n if (this.delegatedEventListeners.length) this.undelegateEvents();\n\n // Store events by type i.e.: \"click\", \"submit\", etc.\n const eventTypes = {};\n\n Object.keys(events).forEach(key => {\n const match = key.match(/^(\\w+)(?:\\s+(.+))*$/);\n\n if (!match) {\n const message = `Invalid event format: ${key}`;\n throw new Error(__DEV__ ? createDevelopmentErrorMessage(message) : createProductionErrorMessage(message));\n }\n // Extract type and selector from the event key.\n const [,type, selector] = match;\n\n let listener = events[key];\n // Listener may be a string representing a method name on the view, or a function.\n if (typeof listener === 'string') listener = this[listener];\n // Validate listener is a function.\n validateListener(listener);\n\n if (!eventTypes[type]) eventTypes[type] = [];\n\n eventTypes[type].push([selector, listener]);\n });\n\n Object.keys(eventTypes).forEach(type => {\n // Listener for the type of event.\n const typeListener = (event) => {\n let node = event.target;\n // Traverse ancestors until reaching the view root (`this.el`).\n while (node) {\n if (node.matches) {\n // Iterate and run every individual listener if the selector matches.\n eventTypes[type].forEach(([selector, listener]) => {\n if ((node === this.el && !selector) || (node !== this.el && node.matches(selector))) {\n listener.call(this, event, this, node);\n }\n });\n }\n // Continue traversing ancestors until reaching the view root (`this.el`) or stopping propagation.\n node = node === this.el || event.cancelBubble ? null : node.parentElement;\n }\n };\n // Store the type and listener in the delegated event listeners array.\n this.delegatedEventListeners.push([type, typeListener]);\n // Add the event listener to the element.\n this.el.addEventListener(type, typeListener);\n });\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * Removes all of the view's delegated events. \n * Useful if you want to disable or remove a view from the DOM temporarily. \n * Called automatically when the view is destroyed and when `delegateEvents` is called again.\n * @return {View} Return `this` for chaining.\n */\n undelegateEvents() {\n this.delegatedEventListeners.forEach(([type, listener]) => {\n this.el.removeEventListener(type, listener);\n });\n\n this.delegatedEventListeners = [];\n // Return `this` for chaining.\n return this;\n }\n\n /**\n * `render` is the core function that your view should override, in order to populate its element (`this.el`), with the appropriate HTML. The convention is for `render` to always return `this`. \n * Views are low-level building blocks for creating user interfaces. For most use cases, we recommend using {@link #module_component Component} instead, which provides a more declarative template syntax, automatic DOM updates, and a more efficient render pipeline. \n * If you add any child views, you should call `this.destroyChildren` before re-rendering.\n * \n * @return {View} Returns `this` for chaining.\n * @example\n * class UserView extends View {\n * render() {\n * if (this.template) {\n * const model = this.model;\n * // Sanitize model attributes to prevent XSS attacks.\n * const safeData = {\n * name : View.sanitize(model.name),\n * email : View.sanitize(model.email),\n * bio : View.sanitize(model.bio)\n * };\n * this.el.innerHTML = this.template(safeData);\n * }\n * return this;\n * }\n * }\n */\n render() {\n return this;\n }\n\n /**\n * Escape HTML entities in a string.\n * Use this method to sanitize user-generated content before inserting it into the DOM.\n * Override this method to provide a custom escape function.\n * This method is inherited by {@link #module_component Component} and used to escape template interpolations.\n * @static\n * @param {string} value String to escape.\n * @return {string} Escaped string.\n */\n static sanitize(value) {\n return `${value}`.replace(/[&<>\"']/g, match => ({\n '&' : '&',\n '<' : '<',\n '>' : '>',\n '\"' : '"',\n '\\'' : '''\n }[match]));\n }\n\n /**\n * Reset the unique ID counter to 0.\n * This is useful for server-side rendering scenarios where you want to ensure that\n * the generated unique IDs match those on the client, enabling seamless hydration of components.\n * This method is inherited by {@link #module_component Component}.\n * @static\n */\n static resetUid() {\n View.uid = 0;\n }\n}\n\n/**\n * Counter for generating unique IDs for view instances. \n * This is primarily used to assign unique identifiers to each view instance (`this.uid`), which can be helpful for tasks like \n * generating element IDs. \n * {@link #module_component Component}s use `this.uid` to generate data attributes for their elements, to be looked up on hydration. \n * For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated \n * unique IDs match those on the client, enabling seamless hydration of components. \n * @static\n * @memberof module:View\n * @name uid\n * @type {number}\n * @default 0\n */\nView.uid = 0;\n"],"names":[],"mappings":"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"}
|
package/es/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import './utils/padEnd.js';
|
|
|
9
9
|
import './utils/createProductionErrorMessage.js';
|
|
10
10
|
import './utils/dev.js';
|
|
11
11
|
import './utils/getResult.js';
|
|
12
|
+
import './utils/defineOwn.js';
|
|
12
13
|
import './core/SafeHTML.js';
|
|
13
14
|
import './core/Partial.js';
|
|
14
15
|
import './core/EventsManager.js';
|
package/es/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Define `key` on `target` as an own data property. Unlike an assignment, it does not go
|
|
3
|
+
* through an accessor declared on the prototype, so it shadows a getter with no setter
|
|
4
|
+
* instead of throwing.
|
|
5
|
+
* @param {object} target Object to define the property on.
|
|
6
|
+
* @param {string} key Property name.
|
|
7
|
+
* @param {any} value Property value.
|
|
8
|
+
* @return {object} The target object.
|
|
9
|
+
* @module
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
const defineOwn = (target, key, value) => Object.defineProperty(target, key, {
|
|
13
|
+
value, writable : true, enumerable : true, configurable : true
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export { defineOwn as default };
|
|
17
|
+
//# sourceMappingURL=defineOwn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineOwn.js","sources":["../../src/utils/defineOwn.js"],"sourcesContent":["/**\n * Define `key` on `target` as an own data property. Unlike an assignment, it does not go\n * through an accessor declared on the prototype, so it shadows a getter with no setter\n * instead of throwing.\n * @param {object} target Object to define the property on.\n * @param {string} key Property name.\n * @param {any} value Property value.\n * @return {object} The target object.\n * @module\n * @private\n */\nconst defineOwn = (target, key, value) => Object.defineProperty(target, key, {\n value, writable : true, enumerable : true, configurable : true\n});\n\nexport default defineOwn;\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACK,MAAC,SAAS,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,KAAK,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE;AAC7E,IAAI,KAAK,EAAE,QAAQ,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,YAAY,GAAG;AAC9D,CAAC;;;;"}
|
package/es/utils/parseHTML.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Parse HTML string to a DocumentFragment.
|
|
3
|
+
*
|
|
4
|
+
* The string is parsed as written. A component's markup already reaches here
|
|
5
|
+
* without surrounding whitespace, and the content of an interpolation is the
|
|
6
|
+
* rendered value itself, whose leading and trailing whitespace is significant.
|
|
3
7
|
* @param {string} html The HTML string to parse.
|
|
4
8
|
* @return {DocumentFragment} The parsed DocumentFragment.
|
|
5
9
|
* @module
|
|
@@ -7,7 +11,7 @@
|
|
|
7
11
|
*/
|
|
8
12
|
function parseHTML(html) {
|
|
9
13
|
const fragment = document.createElement('template');
|
|
10
|
-
fragment.innerHTML = `${html}
|
|
14
|
+
fragment.innerHTML = `${html}`;
|
|
11
15
|
return fragment.content;
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseHTML.js","sources":["../../src/utils/parseHTML.js"],"sourcesContent":["/**\n * Parse HTML string to a DocumentFragment.\n * @param {string} html The HTML string to parse.\n * @return {DocumentFragment} The parsed DocumentFragment.\n * @module\n * @private\n */\nexport default function parseHTML(html) {\n const fragment = document.createElement('template');\n fragment.innerHTML = `${html}
|
|
1
|
+
{"version":3,"file":"parseHTML.js","sources":["../../src/utils/parseHTML.js"],"sourcesContent":["/**\n * Parse HTML string to a DocumentFragment.\n *\n * The string is parsed as written. A component's markup already reaches here\n * without surrounding whitespace, and the content of an interpolation is the\n * rendered value itself, whose leading and trailing whitespace is significant.\n * @param {string} html The HTML string to parse.\n * @return {DocumentFragment} The parsed DocumentFragment.\n * @module\n * @private\n */\nexport default function parseHTML(html) {\n const fragment = document.createElement('template');\n fragment.innerHTML = `${html}`;\n return fragment.content;\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACe,SAAS,SAAS,CAAC,IAAI,EAAE;AACxC,IAAI,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC;AACvD,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;AAClC,IAAI,OAAO,QAAQ,CAAC,OAAO;AAC3B;;;;"}
|
package/lib/Component.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var core_Element = require('./core/Element.cjs');
|
|
|
10
10
|
var core_Interpolation = require('./core/Interpolation.cjs');
|
|
11
11
|
var utils_validateListener = require('./utils/validateListener.cjs');
|
|
12
12
|
var utils_getResult = require('./utils/getResult.cjs');
|
|
13
|
+
var utils_defineOwn = require('./utils/defineOwn.cjs');
|
|
13
14
|
var utils_deepFlat = require('./utils/deepFlat.cjs');
|
|
14
15
|
var utils_parseHTML = require('./utils/parseHTML.cjs');
|
|
15
16
|
var utils_findComment = require('./utils/findComment.cjs');
|
|
@@ -515,10 +516,11 @@ class Component extends View {
|
|
|
515
516
|
constructor(options = {}) {
|
|
516
517
|
super(...arguments);
|
|
517
518
|
this.componentOptions = [];
|
|
518
|
-
// Extend "this" with options
|
|
519
|
+
// Extend "this" with options, as own properties so an option overrides
|
|
520
|
+
// a getter declared by a subclass instead of being assigned through it.
|
|
519
521
|
componentOptions.forEach(key => {
|
|
520
522
|
if (key in options) {
|
|
521
|
-
this
|
|
523
|
+
utils_defineOwn(this, key, options[key]);
|
|
522
524
|
this.componentOptions.push(key);
|
|
523
525
|
}
|
|
524
526
|
});
|