Skip to content

Commit

Permalink
feat(core): adds value transformation for panel and panel-section
Browse files Browse the repository at this point in the history
  • Loading branch information
HoyosJuan committed Aug 14, 2024
1 parent 900d879 commit efc357b
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 23 deletions.
3 changes: 2 additions & 1 deletion packages/core/src/components/Panel/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
<div id="panels">
<bim-panel name="panelA" label="Panel A" icon="fluent:puzzle-cube-piece-20-filled">
<!-- bim-panel usually have bim-panel-section as children, but it can be any other form of section you want -->
<bim-panel-section label="asdasd">
<bim-panel-section label="Section">
<bim-number-input></bim-number-input>
<bim-dropdown></bim-dropdow>
</bim-panel-section>
Expand All @@ -65,6 +65,7 @@
<bim-panel name="panelB" label="Panel B" icon="solar:archive-up-minimlistic-bold">
<div class="section">
<div style="display: flex; flex-direction: column; gap: 0.375rem">
<bim-text-input label="Pick a date" name="date" type="date"></bim-text-input>
<bim-color-input name="selectionColor" label="Selection Color" color="#ffffff"></bim-color-input>
<bim-color-input name="hoverColor" label="Hover Color" color="#ffff00"></bim-color-input>
</div>
Expand Down
16 changes: 13 additions & 3 deletions packages/core/src/components/Panel/example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable no-alert */
import * as THREE from "three";
import * as BUI from "../..";

BUI.Manager.init();
Expand All @@ -24,9 +25,18 @@ for (const panel of panels) {
activationButtons.append(activationButton);
}

// const panelB = document.body.querySelector<BUI.Panel>(
// "bim-panel[name='panelB']",
// )!;
const panelB = document.body.querySelector<BUI.Panel>(
"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);
50 changes: 45 additions & 5 deletions packages/core/src/components/Panel/src/Panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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<string, any>}
* @default {}
* @example <bim-panel></bim-panel> <!-- Access via JavaScript to get value -->
* @example
Expand All @@ -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;
}

Expand All @@ -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<string, (value: any) => any> = {};

readonly activationButton: Button = document.createElement("bim-button");

connectedCallback() {
Expand Down
27 changes: 25 additions & 2 deletions packages/core/src/components/Panel/src/Section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<string, any>}
* @example <bim-panel-section></bim-panel-section> <!-- Usage in HTML not directly applicable as this is a getter -->
* @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;
}

Expand All @@ -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<string, (value: any) => any> = {};

private onHeaderClick() {
if (this.fixed) return;
this.collapsed = !this.collapsed;
Expand Down
29 changes: 17 additions & 12 deletions packages/core/src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, (value: any) => any> = {},
recursive = true,
) => {
let value: Record<string, any> = {};
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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -164,4 +169,4 @@ export const evalCondition = (
break;
}
return result;
};
};

0 comments on commit efc357b

Please sign in to comment.