From 23f39e35dc737ac446b8c3623bec1c85299791b3 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 10 Aug 2025 23:19:43 +0200 Subject: [PATCH] INIT: Project --- aktionsKonsole.js | 134 ++++++++++++++++++++++ comutingArea.js | 36 ++++++ content.js | 90 +++++++++++++++ dataImportArea.js | 262 +++++++++++++++++++++++++++++++++++++++++++ fieldForCopyValue.js | 78 +++++++++++++ fun.js | 16 +++ index.html | 105 +++++++++++++++++ index.js | 28 +++++ package.json | 21 ++++ processingList.js | 118 +++++++++++++++++++ processingRules.js | 37 ++++++ sampleData.txt | 40 +++++++ theming.js | 43 +++++++ 13 files changed, 1008 insertions(+) create mode 100644 aktionsKonsole.js create mode 100644 comutingArea.js create mode 100644 content.js create mode 100644 dataImportArea.js create mode 100644 fieldForCopyValue.js create mode 100644 fun.js create mode 100644 index.html create mode 100644 index.js create mode 100644 package.json create mode 100644 processingList.js create mode 100644 processingRules.js create mode 100644 sampleData.txt create mode 100644 theming.js diff --git a/aktionsKonsole.js b/aktionsKonsole.js new file mode 100644 index 0000000..7cb26a4 --- /dev/null +++ b/aktionsKonsole.js @@ -0,0 +1,134 @@ +/** + * Der Bereich in dem der aktuell geladene/bearbeitete Eintrag angezeigt wird. + * @returns {Component} + */ +function valueRow() { + return builder.row() + .addStyleClass("current-entry-area") + .modifier( + new Modifier() + .fillMaxWidth() + .background(Colors.gold_1) + .clip(Shapes.RoundedCorner.all(6)) + .padding(6) + ) +} + +/** + * + * @param {HTMLElement} entryElement + * @param {HTMLElement} to das Elternelement in welches das Eintragselement verschoben werden soll + * @returns {HTMLElement} + */ +function moveEntry(entryElement, to) { + entryElement.remove(); + [...entryElement.querySelectorAll('.material-icons')] + .forEach(e => { + e.setAttribute("hidden", "hidden"); + e.setAttribute("visibility", "hidden"); + e.style.visibility = "hidden"; + }); + + to.insertAdjacentElement( + "afterbegin", + entryElement + ); + return entryElement; +} + +function moveCurrent(reverse = false) { + let currentArea = document.querySelector('div.current-entry-area'); + if (currentArea.children.length === 0) { + return + } + + let current = currentArea.children[0]; + return moveEntry( + current, + document.querySelector('div.plist' + (reverse + ? '-upcoming' + : '-done' + )) + ); +} + +function loadNext() { + moveCurrent(); + let upcomingArea = document.querySelector('div.plist-upcoming'); + if (upcomingArea.children.length === 0) { + return + } + let upcoming = upcomingArea.children[0]; + + upcoming = moveEntry( + upcoming, + document.querySelector('div.current-entry-area') + ); + + [...upcoming.querySelectorAll('.material-icons')] + .forEach(e => { + e.removeAttribute("hidden"); + e.removeAttribute("visibility"); + e.style.visibility = ""; + }); +} + +function loadPrev() { + moveCurrent(true); + let prevArea = document.querySelector('div.plist-done'); + if (prevArea.children.length === 0) { + return + } + let prev = prevArea.children[0]; + + prev = moveEntry( + prev, + document.querySelector('div.current-entry-area') + ); + + [...prev.querySelectorAll('.material-icons')] + .forEach(e => { + e.removeAttribute("hidden"); + e.removeAttribute("visibility"); + e.style.visibility = ""; + }); +} + +function aktionsKonsole(modifier = new Modifier()) { + return builder.column() + .modifier(componentModifiers.majorArea) + .modifier( + new Modifier() + .fillMaxWidth() + .background(colorModifiers.active.secondary) + .padding(32) + ) + .modifier(modifier) + .addStyleClass("area") + .addStyleClass("area-aktiv") + .childContext([ + builder.div() + .modifier( + new Modifier() + .fillMaxSize() + .margin().bottom(6) + ) + .alignment(Alignment.SPACE_EVENLY) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext([ + builder.button().text("NEXT") + .addEventListener( + "click", + loadNext + ) + , + builder.button().text("PREV") + .addEventListener( + "click", + loadPrev + ) + ]) + , + valueRow() + ]) +} diff --git a/comutingArea.js b/comutingArea.js new file mode 100644 index 0000000..66dcdbd --- /dev/null +++ b/comutingArea.js @@ -0,0 +1,36 @@ + +/** + * Ein Knopf oder Kippschalter um den Klickselect zu (de-)aktivieren. + * Wenn aktiviert kann werden in einem generiertem feld, + * die angeklickten Felder referenziert. + */ +function clickFormulateToggle() { + return builder.span() +} + + +function infoDisplayField(){ + return builder.column() + .childContext([ + builder.inputTags.checkbox({"name": "use_field"}) + , + builder.row() + .childContext([ + builder.inputTags.text() + , + builder.extensions.iconMIO(ICONS.EDIT) + ]) + ]) +} + +function computedArea() { + return builder.row() + .overflow(false, true) + .modifier( + new Modifier() + .padding(12) + ) + .childContext([ + clickFormulateToggle() + ]) +} \ No newline at end of file diff --git a/content.js b/content.js new file mode 100644 index 0000000..4f9f687 --- /dev/null +++ b/content.js @@ -0,0 +1,90 @@ +const listRow = function (content) { + return builder.tableRow() + .componentChildren([ + builder.tableCell().text("1"), + builder.tableCell().text("Info 1"), + builder.tableCell().text("Info 2"), + builder.tableCell().text("Info 3") + ]) +} + +function listColumn(colText, bckColor, id) { + return builder.column() + .setAttribute("id", id) + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .fillMaxWidth(0.497) + .background(bckColor) + .clip(Shapes.RoundedCorner.all(15)) + ) + .componentChildren([ + builder.header(2) + .text(colText) + .alignment(Alignment.CENTER) + .chainModifier() + .padding(new Siding().all(32)) + , + builder.table() + .chainModifier() + .fillMaxWidth() + .linkPadding().all(16) + .componentChild( + builder.tableBody() + .addStyleClass("list-table-body") + .componentChild( + listRow() + ) + ) + ]) +} + +const currentElementDisplay = function (fields) { + return builder.column() + .modifier( + new Modifier() + .fillMaxWidth() + ) + .componentChildren([ + builder.row() + .componentChildren([ + builder.button() + .text("Load Next") + .setEvent(CommonEvents.ONCLICK, "rotateCurrentElement") + .chainModifier() + .fillMaxWidth(0.2) + , + builder.input({ "type": "number" }) + ]) + , + builder.table() + .setAttribute("hidden", "true") + .componentChild(builder.tableBody()) + ]) +} + +const currentSection = function () { + return builder.column() + .setAttribute("id", "current") + .alignment(Alignment.CENTER) + .chainModifier() + .fillMaxWidth() + .linkPadding().vertical(8) + .componentChild( + builder.column() + .chainModifier() + .fillMaxWidth() + .background(MaterialFiveHundredlColors.GOLD) + .clip(Shapes.RoundedCorner.all(15)) + .linkPadding().horizontal(32).vertical(32) + .componentChildren([ + builder.header(2) + .text("Current Element") + .chainModifier() + .linkPadding() + .bottom(8) + , + currentElementDisplay() + ]) + ) +} diff --git a/dataImportArea.js b/dataImportArea.js new file mode 100644 index 0000000..5adf7ac --- /dev/null +++ b/dataImportArea.js @@ -0,0 +1,262 @@ +/** + * Im Grunde eine gekapselte Textarea + * Das Feld in welches roher Datentext eingefügt wird. + * Wir in der Zukunft eventuell in einen sepperaten View verschoben. + * + * @param {Modifier} modifier + * @returns {Component} + */ +function rawTextImportArea(modifier = new Modifier()) { + return builder.div() + .modifier( + new Modifier() + .fillMaxSize() + .padding(6) + ) + .modifier(modifier) + .setAttribute("id", "import-area") + .chainChild() + .textarea() + .name("import-textarea") + .modifier( + new Modifier() + .clip(Shapes.RoundedCorner.all(12)) + .fillMaxSize() + .padding(6) + .dimensions().height(120) + ) +} + +class ProcessingRule { + use; + value; + constructor(display, id, type, placeholder, desc, isFlagged) { + this.display = display; + this.id = id; + this.type = type; + this.placeholder = placeholder; + this.desc = desc; + this.isFlagged = isFlagged; + } + + _getCorrespondingComponent() { + return document.getElementById(this.id) + } + + _getToBeUsedState() { + return !this.isFlagged + || this._getCorrespondingComponent() + .querySelector('input[name="is_to_be_used"]').checked === true; + } + + _getInputTagValue() { + let container = this._getCorrespondingComponent() + .querySelector('.value-container'); + if (this.type === "checkbox") { + return container.checked === true; + } + + if (container.value === "") { + return this.placeholder; + } + return container.value; + } + + updateFromUi() { + this.use = this._getToBeUsedState(); + this.value = this._getInputTagValue(); + } +} + + + +/** + * Die Überschrift und unter der Bedingung rule.isFlagged auch eine checkbox die bestimmt + * ob die ProcessingRule genutzt werden soll. + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function prdUseboxAndLabel(rule) { + return builder.row() + .arrangement(Arrangement.CENTER) + .childContext([ + builder.inputTags.checkbox({ "name": "is_to_be_used" }) + .modifier(new Modifier().margin().right(6)) + .hiddenByCondition(!rule.isFlagged) + , + builder.label() + .setAttribute("for", rule.id) + .text(rule.display) + ]); +} + +/** + * Componente die eine ProcessingRule definiert. + * - Ob sie verwendet werden soll + * - welche Standartwert erwartet/verwendet werden soll + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function processingRuleDefineComponent(rule, index, arr, modifier = new Modifier()) { + return builder.column() + .isHigherComponent() + .setAttribute("id", rule.id) + .title(rule.desc) + .modifier(modifier) + .arrangement(Arrangement.CENTER) + .childContext([ + prdUseboxAndLabel(rule) + , + builder.span() + .modifier(new Modifier().fillMaxSize()) + .arrangement(Arrangement.CENTER) + .chainChild() + .input(rule.type) + .addStyleClass("value-container") + .setAttribute("placeholder", rule.placeholder) + .modifier( + new Modifier() + .padding(4) + .setStyleRule("field-sizing", "content") + ) + .apply(function (el) { + if (!rule.isFlagged) { + el.setAttribute("deactivated", ""); + } + }) + ]) +} + + +/** + * Bereich der ProcessingRules + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function processingRulesArea(modifier = new Modifier()) { + return builder.row() + .modifier( + new Modifier() + .fillMaxWidth() + .padding(8) + ) + .modifier(modifier) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext( + Object.keys(processingRules) + .map(def => processingRules[def]) + .map(processingRuleDefineComponent) + ) +} + +let importedData = []; + +/** + * Funktion keine Komponente + */ +function importEntries() { + let rawtext = document.getElementById("import-area") + .querySelector("textarea").value; + let prules = Object.keys(processingRules) + .reduce((a, c) => { + let r = processingRules[c]; + let pr = new ProcessingRule(r.display, r.id, r.type, r.placeholder, r.desc, r.isFlagged); + pr.updateFromUi(); + a[c] = pr; + return a; + }, processingRules); + + let lines = rawtext.split(prules.line_splitter.value); + let splittedLines = lines + .filter(e => e.trim() !== ""); + + console.log(splittedLines); + + if (prules.entry_splitter.use) { + console.log(prules.entry_splitter.value); + splittedLines = splittedLines + .map(line => line.split(prules.entry_splitter.value)) + } else { + splittedLines = splittedLines + .map(e => [e]); + } + + importedData = splittedLines; + console.log(splittedLines); + + let upcoming = document.querySelector(".plist-upcoming"); + + splittedLines = splittedLines.map((e, i) => listEntryRow(e, i).generate().compext); + + splittedLines + .forEach(element => { + upcoming.insertAdjacentElement( + "beforeend", + element + ) + }); +} + + +/** + * Bereich mit ausführbaren Elementen + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function commandRow(modifier = new Modifier()) { + return builder.row() + .modifier(modifier) + .childContext([ + builder.button() + .text("Import") + .addStyleClass("import-trigger") + .addEventListener( + "click", + importEntries + ) + ]) +} + + +/** + * + * + * @param {ProcessingRule} rule + * @returns {Component} + */ +function importArea(modifier = new Modifier()) { + return builder.column() + .addStyleClass("area") + .addStyleClass("area-import") + .modifier( + new Modifier() + .fillMaxWidth() + .padding(12) + .background(colorModifiers.import.secondary) + ) + .modifier(componentModifiers.majorArea) + .modifier(modifier) + .arrangement(Arrangement.CENTER) + .childContext([ + builder.header(4).text("Import") + , + builder.column() + .arrangement(Arrangement.CENTER) + .modifier( + new Modifier() + .padding().all(32).top(16) + ) + .childContext([ + + rawTextImportArea() + , + processingRulesArea() + , + commandRow() + ]) + ]) +} diff --git a/fieldForCopyValue.js b/fieldForCopyValue.js new file mode 100644 index 0000000..c02718d --- /dev/null +++ b/fieldForCopyValue.js @@ -0,0 +1,78 @@ +/** + * @deprecated Verwendet die ebenfalls deprecated function document.execCommand('copy') + * @param {*} triggerElement + */ +function copyFieldToClipboard2(triggerElement) { + var copyText = document.getElementById("myText"); + copyText.select(); + copyText.setSelectionRange(0, 99999); // For mobile devices + + // Copy the text inside the textarea + document.execCommand("copy"); + + let valuecontainer = triggerElement.parent + .querySelector('.valuecontainer'); + + let fieldvalue = valuecontainer.value; + + navigator.clipboard.writeText(fieldvalue); + + // Optional: Alert the copied text + alert("Copied: " + copyText.value); +} + +function copyFieldToClipboard(triggerElement) { + let valcon = triggerElement.closest('.value-to-clipboard-field') + .querySelector('.valuecontainer'); + navigator.clipboard.writeText(valcon.value) +} + + +/** + * + * @param {string} value + * @param {number} index + * @param {Array} arr + * @param {boolean} hideCopy + * @param {string} [type="text"] Falls ein anderer typ gebraucht wird + * @returns {Component} + */ +function fieldForValueCopy( + value, + index, + arr, + hideCopy = true, + type="text", + setName = "field-" + (index + 3) +) { + return builder.div() + .addStyleClass("value-to-clipboard-field") + .childContext([ + builder.input(type) + .readonly() + .addStyleClass("valuecontainer") + .text(value) + .name(setName) + .modifier( + new Modifier() + .padding(0) + .border(0) + , + ".inputfield-border-none" + ) + , + builder.extensions.iconMIO(ICONS.CONTENT_COPY) + .hiddenByCondition(hideCopy) + .modifier( + new Modifier() + .setStyleRule("cursor", "pointer") + .setStyleRule("visibility", "hidden") + + ) + .setAttribute( + "onclick", + "copyFieldToClipboard(this)" + ) + ]) +} + diff --git a/fun.js b/fun.js new file mode 100644 index 0000000..07e2c6a --- /dev/null +++ b/fun.js @@ -0,0 +1,16 @@ +function rotateCurrentElement() { + let next = document.querySelector('#open table tbody tr'); + let currentTarget = document.querySelector('#current table tbody'); + let current = currentTarget.querySelector("tr"); + let target = document.querySelector('#processed table tbody'); + + if(current){ + current.remove(); + target.insertAdjacentElement("afterbegin", current); + } + + if(next){ + next.remove(); + currentTarget.append(next); + } +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..d635791 --- /dev/null +++ b/index.html @@ -0,0 +1,105 @@ + + + + + + + Processing List + + + + + + + + + + +
+ +
+ + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..efa3160 --- /dev/null +++ b/index.js @@ -0,0 +1,28 @@ +Page.inDev(45) +Page.addExtension(MaterialIcons); + +builder.page( + builder.column() + .arrangement(Arrangement.SPACE_EVENLY) + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .border(new Border().all(15)) + .padding(32) + .dimensions().width(1200) + ) + .childContext([ + builder.header(1) + .alignment(Alignment.CENTER) + .text("Process List") + , + aktionsKonsole() + , + listArea(new Modifier().margin().vertical(5)) + , + importArea( + new Modifier() + .setStyleRule("float", "bottom") + ) + ]) +) diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a4a0c9 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "semi-automatic-ui-process-list", + "version": "1.0.0", + "description": "WebUi to list and process entries, splitted into seperated fields if needed, with data for a task/process. Each entry will be used in a process of a partially manual/automated manner. Tasks of a repetetive kind that are to be executed on the ui (of application/s).", + "license": "ISC", + "keywords": [ + "automation", + "webui" + ], + "author": "cm", + "type": "commonjs", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "jpc-like-websites": "git+https://git.labos.goip.de/chris/jpc-like-websites.git", + "jpclw-extension": "git+https://git.labos.goip.de/chris/jpclw-extension.git", + "jpclwe-materialicons": "git+https://git.labos.goip.de/chris/jpclwe-materialIcons.git" + } +} diff --git a/processingList.js b/processingList.js new file mode 100644 index 0000000..52235d3 --- /dev/null +++ b/processingList.js @@ -0,0 +1,118 @@ +function listEntryRowControls() { + const controls = [ + select, + de_activate, + edit, + ] +} + +function listEntryRowControlsContextMenu() { + const menuOptions = { + copy: { + copy_below, + copy_bottom + }, + move: { + top, + bottom + }, + + } + +} + +/** + * Stellt den Datensatz eines Prozesses dar + * + * @param {String[]} entry + * @param {number} index + * @returns {Component} + */ +function listEntryRow(entry, index) { + return builder.row() + .modifyChildren(new Modifier().padding(2)) + .modifier( + new Modifier() + .fillMaxWidth() + .padding(4) + .background(Colors.white) + .clip(Shapes.RoundedCorner.all(4)) + ) + .alignment(Alignment.CENTER) + .childContext([ + builder.span() + .addStyleClass("entrydata-container") + .setAttribute( + "data-entrydata", + JSON.stringify(entry) + ) + .modifier( + new Modifier() + .fillMaxWidth(0.09) + .margin().right(4) + ) + .arrangement(Arrangement.SPACE_BETWEEN) + .alignment(Alignment.CENTER) + .childContext([ + builder.inputTags.checkbox({ "name": "entry_select" }) + , + fieldForValueCopy(index, -1, [], true, "number","entry-index") + .alignment(Alignment.CENTER) + .modifier( + new Modifier() + .fillMaxWidth(0.7) + .margin().left(2) + ) + + ]) + , + ...entry + .filter(e => e.trim() !== "") + .map(fieldForValueCopy) + ]); +} + +/** + * + * @param {ComponentColor} colorTheme + * @param {Modifier} modifier + * @returns {Component} + */ +function processingList(colorTheme, modifier = new Modifier()) { + return builder.column() + .modifier(componentModifiers.majorArea) + .modifier( + new Modifier() + .background(colorTheme.secondary) + .border(new Border(1)) + .padding(8) + ) + .modifier(modifier) + .chainChild() + .column() + .modifier( + new Modifier() + .background(Colors.white) + .clip(Shapes.RoundedCorner.all(6)) + ) +} + + +function listArea(modifier = new Modifier()) { + return builder.row() + .addStyleClass("area") + .addStyleClass("area-lists") + .modifier(modifier) + .modifier(new Modifier().fillMaxWidth()) + .arrangement(Arrangement.SPACE_EVENLY) + .childContext([ + processingList(colorModifiers.upcoming) + .addStyleClass("plist") + .addStyleClass("plist-upcoming") + , + processingList(colorModifiers.done) + .addStyleClass("plist") + .addStyleClass("plist-done") + ]) + .distibuteSpacingEvenly(true, 0.05) +} diff --git a/processingRules.js b/processingRules.js new file mode 100644 index 0000000..99b99c6 --- /dev/null +++ b/processingRules.js @@ -0,0 +1,37 @@ +const processingRules = { + line_splitter: { + display: "Line splitter", + id: "line_splitter", + type: "text", + placeholder: "\n", + desc: "Das Zeichen um Zeilen umzubrechen.", + isFlagged: false + }, + + entry_splitter: { + display: "Entry Info Splitter", + id: "entry_splitter", + type: "text", + placeholder: ".", + desc: "Das Zeichen das eine Zeile in einzelne Informationen teilt.", + isFlagged: true + }, + + use_headrow: { + display: "Use Head Row", + id: "use_headrow", + type: "checkbox", + placeholder: "false", + desc: "Bestimmt das im Text, die erste Reihe potentielle Spaltenüberschriften beinhaltet.", + isFlagged: true + }, + + generated_fields: { + display: "Generate Fields", + id: "generated_fields", + type: "number", + placeholder: "1", + desc: "Definiert das zusätzliche Informationen aus den anderen generiert werden sollen.", + isFlagged: true + } +} diff --git a/sampleData.txt b/sampleData.txt new file mode 100644 index 0000000..9db65d0 --- /dev/null +++ b/sampleData.txt @@ -0,0 +1,40 @@ +202406-Rimpido-Schulung.mm +3D_Modelle.mm +Bastel-Projekte.mm +Bastel_Handwerksressoursen.mm +Besorgungen.mm +Bodenseeschifferpatent-Präsentation-Gruppe.mm +Bodenseeschifferpatent-Präsentation-Gruppe.pdf +'Bogenbau_ Sehnenbau_ Mittenwicklung – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbücher.pdf' +'Büroumzug Aicheler.mm' +Checkliste.mm +ContentOrientatedText.mm +Data-Analysten_Augen.md +Data-Analysten_Augen.mm +Data-Analysten_Augen.pdf +Data-Analysten_Augen.png +Digitalisierung_von_analogen_Informationen.mm +'GTD - Fokus.mm' +Hochzeit.mm +'How to write Code.mm' +Krankenversicherung-2.mm +Krankenversicherung.mm +Kugelrechner.ods +Kugelrechner_quickVersion.ods +Linksammlung.mm +'Methanol - Synthese.mm' +Netzwerkkabel-Plan.ods +'Offene Recherchen.mm' +Projekt_LehrenMitarbeitenLernen.mm +Projekte.mm +'Rimpido - Unternehmens Roadmap.mm' +Rimpido-Sicherheitskonzept-Umstellung.mm +Systemalternativen.mm +Trauzeugenschaft_Stefan.mm +Typescript_Applikation.mm +Unternehmungen-n.pdf +Unternehmungen.mm +Unternehmungen.pdf +Wasser-Stromgenerator.mm +papbeton_betonplatten_volumenberchnungen.ods +rimpido.mm diff --git a/theming.js b/theming.js new file mode 100644 index 0000000..02405cb --- /dev/null +++ b/theming.js @@ -0,0 +1,43 @@ + +class ComponentColor { + constructor(primary, secondary, text = Colors.black, accent = Colors.seagreen_1) { + this.primary = primary; + this.secondary = secondary; + this.text = text; + this.accent = accent; + } +} + + +const colorModifiers = { + upcoming: new ComponentColor( + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + ), + done: new ComponentColor( + MaterialFiveHundredlColors.GREEN, + MaterialFiveHundredlColors.SEA_GREEN, + MaterialFiveHundredlColors.BLUE, + Colors.white, + ), + deactivated: new ComponentColor( + Colors.gray, + Colors.lightgrey + ), + active: new ComponentColor( + MaterialFiveHundredlColors.ORANGE, + MaterialFiveHundredlColors.SOFT_ORANGE + ), + import: new ComponentColor( + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + MaterialFiveHundredlColors.BLUE, + ) +} + +const componentModifiers={ + majorArea: new Modifier().clip(Shapes.RoundedCorner.all(16)), +} \ No newline at end of file