From efc357b60275b6df2f093913b65c8cacfed7f9b8 Mon Sep 17 00:00:00 2001 From: Juan Hoyos Date: Tue, 13 Aug 2024 19:26:19 -0500 Subject: [PATCH] feat(core): adds value transformation for panel and panel-section --- .../core/src/components/Panel/example.html | 3 +- packages/core/src/components/Panel/example.ts | 16 ++++-- .../core/src/components/Panel/src/Panel.ts | 50 +++++++++++++++++-- .../core/src/components/Panel/src/Section.ts | 27 +++++++++- packages/core/src/core/utils.ts | 29 ++++++----- 5 files changed, 102 insertions(+), 23 deletions(-) diff --git a/packages/core/src/components/Panel/example.html b/packages/core/src/components/Panel/example.html index 0599799..f5ddae9 100644 --- a/packages/core/src/components/Panel/example.html +++ b/packages/core/src/components/Panel/example.html @@ -49,7 +49,7 @@
- + @@ -65,6 +65,7 @@
+
diff --git a/packages/core/src/components/Panel/example.ts b/packages/core/src/components/Panel/example.ts index 2b57966..c132896 100644 --- a/packages/core/src/components/Panel/example.ts +++ b/packages/core/src/components/Panel/example.ts @@ -1,4 +1,5 @@ /* eslint-disable no-alert */ +import * as THREE from "three"; import * as BUI from "../.."; BUI.Manager.init(); @@ -24,9 +25,18 @@ for (const panel of panels) { activationButtons.append(activationButton); } -// const panelB = document.body.querySelector( -// "bim-panel[name='panelB']", -// )!; +const panelB = document.body.querySelector( + "bim-panel[name='panelB']", +)!; + +panelB.valueTransform = { + date: (value: string) => { + if (value.trim() === "") return value; + return new Date(value); + }, +}; + +console.log(panelB); // const { activationButton: panelBBtn } = panelB; // panelBBtn.labelHidden = true; // activationButtons.append(panelBBtn); diff --git a/packages/core/src/components/Panel/src/Panel.ts b/packages/core/src/components/Panel/src/Panel.ts index 66eb1f8..f93aa06 100644 --- a/packages/core/src/components/Panel/src/Panel.ts +++ b/packages/core/src/components/Panel/src/Panel.ts @@ -9,9 +9,9 @@ import { getElementValue } from "../../../core/utils"; * A custom panel web component for BIM applications. HTML tag: bim-panel */ export class Panel extends LitElement implements HasName, HasValue { - /** - * CSS styles for the component. - */ + /** + * CSS styles for the component. + */ static styles = [ styles.scrollbar, css` @@ -135,7 +135,6 @@ export class Panel extends LitElement implements HasName, HasValue { /** * The `value` getter computes and returns the current state of the panel's form elements as an object. This property is dynamic and reflects the current input values within the panel. When accessed, it traverses the panel's child elements, collecting values from those that have a `name` or `label` attribute, and constructs an object where each key corresponds to the `name` or `label` of the element, and the value is the element's value. This property is particularly useful for forms or interactive panels where the user's input needs to be retrieved programmatically. The value returned is a snapshot of the panel's state at the time of access, and it does not maintain a live link to the input elements. * - * @type {Record} * @default {} * @example * @example @@ -144,7 +143,7 @@ export class Panel extends LitElement implements HasName, HasValue { * console.log(panel.value); // Logs the current value object of the panel */ get value() { - const value = getElementValue(this); + const value = getElementValue(this, this.valueTransform); return value; } @@ -171,9 +170,50 @@ export class Panel extends LitElement implements HasName, HasValue { } } + /** + * Represents a boolean property that controls the visibility of the panel's header. + * When `true`, the header (containing the label and icon) is hidden. + * When `false`, the header is visible. + * + * @property headerHidden - The boolean value indicating whether the header should be hidden. + * @default false + * @attribute header-hidden - The attribute that reflects the `headerHidden` property to the HTML element. + * @reflect true - Indicates that the property should be reflected to the HTML attribute. + * + * @example + * // Setting the `headerHidden` property to `true` + * panel.headerHidden = true; + * + * // Setting the `header-hidden` attribute to `true` + * panel.setAttribute('header-hidden', 'true'); + * + * // Getting the `headerHidden` property value + * console.log(panel.headerHidden); // Output: true + * + * // Getting the `header-hidden` attribute value + * console.log(panel.getAttribute('header-hidden')); // Output: 'true' + */ @property({ type: Boolean, attribute: "header-hidden", reflect: true }) headerHidden = false; + /** + * A record that maps element names or labels to transformation functions. + * This record is used to transform the values from elements before they are returned as part of the `value` property. + * + * @example + * // Example usage of ValueTransform + * const valueTransform = { + * date: (value: string) => new Date(value), // Transform date value from string to Date object + * }; + * + * const panel = document.getElementById('your-bim-panel'); // should have some inputs inside + * panel.valueTransform = valueTransform; + * + * // Now, when accessing the `value` property of the panel, the values of the specified elements will be transformed accordingly + * console.log(panel.value); // Output: { date: Date object } + */ + valueTransform: Record any> = {}; + readonly activationButton: Button = document.createElement("bim-button"); connectedCallback() { diff --git a/packages/core/src/components/Panel/src/Section.ts b/packages/core/src/components/Panel/src/Section.ts index 4d92405..40738fb 100644 --- a/packages/core/src/components/Panel/src/Section.ts +++ b/packages/core/src/components/Panel/src/Section.ts @@ -3,6 +3,7 @@ import { property } from "lit/decorators.js"; import { styles } from "../../../core/Manager/src/styles"; import { HasName, HasValue } from "../../../core/types"; import { getElementValue } from "../../../core/utils"; +import { Panel } from "./Panel"; /** * A custom panel section web component for BIM applications. HTML tag: bim-panel-section @@ -141,14 +142,18 @@ export class PanelSection extends LitElement implements HasName, HasValue { /** * The `value` getter computes and returns the current state of the panel section's form elements as an object. This object's keys are the `name` or `label` attributes of the child elements, and the values are the corresponding values of these elements. This property is particularly useful for retrieving a consolidated view of the user's input or selections within the panel section. When the value of any child element changes, the returned object from this getter will reflect those changes, providing a dynamic snapshot of the panel section's state. Note that this property does not have a default value as it dynamically reflects the current state of the panel section's form elements. - * @type {Record} * @example * @example * const section = document.createElement('bim-panel-section'); * console.log(section.value); // Logs the current value object */ get value() { - const value = getElementValue(this); + const parent = this.parentElement; + let transform; + if (parent instanceof Panel) transform = parent.valueTransform; + if (Object.values(this.valueTransform).length !== 0) + transform = this.valueTransform; + const value = getElementValue(this, transform); return value; } @@ -174,6 +179,24 @@ export class PanelSection extends LitElement implements HasName, HasValue { } } + /** + * A record that maps element names or labels to transformation functions. + * This record is used to transform the values from elements before they are returned as part of the `value` property. + * + * @example + * // Example usage of ValueTransform + * const valueTransform = { + * date: (value: string) => new Date(value), // Transform date value from string to Date object + * }; + * + * const panelSection = document.getElementById('your-bim-panel-section'); // should have some inputs inside + * panelSection.valueTransform = valueTransform; + * + * // Now, when accessing the `value` property of the panelSection, the values of the specified elements will be transformed accordingly + * console.log(panelSection.value); // Output: { date: Date object } + */ + valueTransform: Record any> = {}; + private onHeaderClick() { if (this.fixed) return; this.collapsed = !this.collapsed; diff --git a/packages/core/src/core/utils.ts b/packages/core/src/core/utils.ts index 58068c0..04a3c7e 100644 --- a/packages/core/src/core/utils.ts +++ b/packages/core/src/core/utils.ts @@ -6,25 +6,30 @@ import { EntryQuery, Query, QueryCondition, QueryGroup } from "./types"; * @param recursive - Whether to recursively extract values from child elements. Default is true. * @returns An object containing the extracted values. */ -export const getElementValue = (child: HTMLElement, recursive = true) => { +export const getElementValue = ( + child: HTMLElement, + transform: Record any> = {}, + recursive = true, +) => { let value: Record = {}; for (const _child of child.children) { const child = _child as any; const key = child.getAttribute("name") || child.getAttribute("label"); + const keyTransform = transform[key]; if (key) { if ("value" in child) { const childValue = child.value; const isObject = - typeof childValue === "object" &&!Array.isArray(childValue); + typeof childValue === "object" && !Array.isArray(childValue); if (isObject && Object.keys(childValue).length === 0) continue; - value[key] = child.value; + value[key] = keyTransform ? keyTransform(child.value) : child.value; } else if (recursive) { - const childValue = getElementValue(child); + const childValue = getElementValue(child, transform); if (Object.keys(childValue).length === 0) continue; - value[key] = childValue; + value[key] = keyTransform ? keyTransform(childValue) : childValue; } } else if (recursive) { - value = {...value,...getElementValue(child) }; + value = { ...value, ...getElementValue(child, transform) }; } } return value; @@ -40,7 +45,7 @@ export const convertString = (value: string) => { return value === "true"; } // eslint-disable-next-line no-restricted-globals - if (value &&!isNaN(Number(value)) && value.trim()!== "") { + if (value && !isNaN(Number(value)) && value.trim() !== "") { return Number(value); } return value; @@ -64,7 +69,7 @@ function parseSearch(search: string) { const [key, _value] = splitQuery; const value = _value.startsWith("'") && _value.endsWith("'") - ? _value.replace(/'/g, "") + ? _value.replace(/'/g, "") : convertString(_value); const entryQuery: EntryQuery = { key, condition, value }; return entryQuery; @@ -79,11 +84,11 @@ export const getQuery = (queryString: string) => { try { const queryGroup: Query = []; const entryAndGroupQueries = queryString - .split(/&(?![^()]*\))/) - .map((value) => value.trim()); + .split(/&(?![^()]*\))/) + .map((value) => value.trim()); for (const query of entryAndGroupQueries) { - const isEntryQuery =!query.startsWith("(") &&!query.endsWith(")"); + const isEntryQuery = !query.startsWith("(") && !query.endsWith(")"); const isGroupQuery = query.startsWith("(") && query.endsWith(")"); if (isEntryQuery) { const entryQuery = parseSearch(query); @@ -164,4 +169,4 @@ export const evalCondition = ( break; } return result; -}; \ No newline at end of file +};