diff --git a/package.json b/package.json index 70a4ef6..5c16ccb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bb-component-SuperContainer", - "version": "1.0.12", + "version": "1.1.0", "description": "A Multi Layout Data Aware Container Component for Budibase", "author": "Michael Poirazi", "license": "MIT", diff --git a/schema.json b/schema.json index e0bde21..4a4da8c 100644 --- a/schema.json +++ b/schema.json @@ -288,7 +288,7 @@ "type": "boolean", "label": "Wrap", "key": "wrap", - "showInBar": true, + "showInBar": false, "barIcon": "ModernGridView", "barTitle": "Wrap" } @@ -342,17 +342,29 @@ "defaultValue": false, "dependsOn": { "setting": "theme", "value": "budibase" } }, + { + "type": "boolean", + "key": "tabsEmphasized", + "label": "Emphasized", + "defaultValue": false, + "dependsOn": { "setting": "theme", "value": "budibase" } + }, + { + "type": "boolean", + "key": "tabsIconsOnly", + "label": "Icons Only", + "defaultValue": false + }, { "type": "select", "key": "tabsSize", "label": "Size", "options": [ - { "label": "Small", "value": "2.5rem" }, - { "label": "Medium", "value": "3rem" }, - { "label": "Large", "value": "3.5rem" }, - { "label": "Extra Large", "value": "4rem" } + { "label": "Small", "value": "S" }, + { "label": "Medium", "value": "M" }, + { "label": "Large", "value": "L" } ], - "defaultValue": "3rem" + "defaultValue": "M" }, { "type": "select", @@ -363,7 +375,7 @@ { "label": "Center", "value": "center" }, { "label": "Right", "value": "flex-end" } ], - "defaultValue": "center" + "defaultValue": "flex-start" } ] }, @@ -400,7 +412,7 @@ "defaultValue": "New Tab" }, { - "type": "icon", + "type": "text", "key": "icon", "label": "Icon" }, diff --git a/src/Component.svelte b/src/Component.svelte index 243c4e3..5fa0a89 100644 --- a/src/Component.svelte +++ b/src/Component.svelte @@ -4,17 +4,20 @@ import RepeaterPreview from "./RepeaterPreview.svelte"; import TabControl from "./TabControl.svelte"; import Grabber from "./Grabber.svelte"; - import fsm from "svelte-fsm"; - import { v4 as uuidv4 } from "uuid"; + import { writable } from "svelte/store"; const { styleable, builderStore, Provider, componentStore } = getContext("sdk"); const component = getContext("component"); + const parentState = getContext("superContainer"); + const parentGridStore = getContext("superContainerParams"); + + $: console.log($parentGridStore) - export let dataprovider; + export let dataprovider export let sourceArray - export let bound; + export let bound = false export let flex; export let flexFactor = 1; @@ -29,7 +32,9 @@ export let tabsSize; export let tabsAlignment; export let tabsQuiet; + export let tabsEmphasized export let activeTab = 0 + export let tabsIconsOnly = false export let theme; export let gridColumns = 3; @@ -53,27 +58,13 @@ let selectedTab = undefined; let componentID = $component.id; - let id = uuidv4(); + let id = Math.random() * 10; let childCssVariables = {}; let cssVariables = {}; let gridPreviewSlots - let slots - - $: if (bound && sourceArray) - slots = safeParse(sourceArray) - else - slots = null - function safeParse(str) { - let parsed; - - try { - parsed = JSON.parse(str); - } catch (error) { - console.error(error); - } - return parsed; - } + // The array of slots to be rendered in array repeater mode + let slots // The State machine that handles the child role of the super container if nested const childState = fsm( "containerItem" , { @@ -131,8 +122,8 @@ }, refresh() { childCssVariables = { - "grid-column" : "span " + colSpan, - "grid-row" : "span " + rowSpan + "grid-column" : "span " + Math.min( colSpan, $parentGridStore?.gridColumns ), + "grid-row" : "span " + Math.min( rowSpan, $parentGridStore?.gridRows), }; }, }, @@ -162,6 +153,7 @@ const state = fsm(mode, { "*": { registerContainer(componentID, id, state, title, icon, color, reqcolSpan, reqrowSpan) { + console.log(containers, id) containers = [ ...containers, { @@ -182,8 +174,8 @@ containers[index].title = title; containers[index].icon = icon; containers[index].color = color; - containers[index].colSpan = reqcolSpan ?? 1; - containers[index].rowSpan = reqrowSpan ?? 1; + containers[index].colSpan = reqcolSpan; + containers[index].rowSpan = reqrowSpan; } containers = containers; }, @@ -263,13 +255,25 @@ this.refresh( $builderStore.inBuilder ); }, refresh( inBuilder ) { - if ( inBuilder ) { - let repeaterUsedSlots = bound && dataprovider && containers?.length ? dataprovider.rows.length * containers[0]?.colSpan * containers[0]?.rowSpan : 0; - let totalSlots = gridColumns * gridRows - let nonSuperComponents = $component.children == 0 ? 0 : $component.children - containers?.length - let childUsedSlots = containers?.reduce( ( p , c , idx ) => { return p + ( c.colSpan * c.rowSpan ) }, 0 ) - let neededPreviewSlots = totalSlots - repeaterUsedSlots - childUsedSlots - nonSuperComponents + let repeaterUsedSlots = 0 + let nonSuperComponents = 0 + let totalSlots = gridColumns * gridRows + let childUsedSlots = containers?.reduce( ( p , c , idx ) => { return p + ( c.colSpan * c.rowSpan ) }, 0 ) + if ( inBuilder ) { + if ( bound == "dataprovider" && dataprovider ) { + nonSuperComponents = $component.children - ( containers.length / dataprovider?.rows.length ); + repeaterUsedSlots = $component.children ? dataprovider?.rows.length * nonSuperComponents : dataprovider?.rows.length + } else if ( bound == "array" && slots?.length ) { + nonSuperComponents = $component.children - ( containers.length / slots.length ); + repeaterUsedSlots = $component.children ? slots.length * nonSuperComponents : slots.length + } else { + nonSuperComponents = 1 + } + + + let neededPreviewSlots = totalSlots - repeaterUsedSlots - childUsedSlots + console.log( totalSlots,repeaterUsedSlots,childUsedSlots,nonSuperComponents,dataprovider?.rows.length ,slots?.length ) gridPreviewSlots = neededPreviewSlots > 0 ? new Array( neededPreviewSlots ) : [] } @@ -323,7 +327,7 @@ if (containers.length > 0) this.selectTab(containers[0].id) } cssVariables = { - "flex-direction": error ? "column" : direction == "row" ? "column" : "row", + "flex-direction": direction == "row" || error ? "column" : "row", }; }, selectTab(tabId) { @@ -338,18 +342,24 @@ }, }, }); + + let gridStore = new writable({}) + $: $gridStore = { gridColumns, gridRows } + $: console.log($gridStore) $: randomColor = $builderStore.inBuilder && bound ? "32CD3230" - : Math.floor(Math.random() * 16777215).toString(16) + "30"; + : Math.floor(Math.random() * 16777215).toString(16) + "10"; $: nested = component ? $component.ancestors[$component.ancestors.length - 2] == "plugin/bb-component-SuperContainer" : false; + $: if ( bound == "array" && sourceArray ) slots = safeParse(sourceArray) + $: childState.synch($parentState); - $: parentState?.updateContainer(id, title, icon, color, colSpan, rowSpan); + $: parentState?.updateContainer(id, title, icon, color, Math.min(colSpan, $parentGridStore?.gridColumns ), Math.min( rowSpan, $parentGridStore?.gridRows)); $: { if ( @@ -358,14 +368,9 @@ $componentStore.selectedComponentPath?.includes($component.id) ) { parentState.selectChild($component.id); - // childState.synch($parentState) if ( childMode != $parentState+"Item" ) builderStore.actions.updateProp("childMode", $parentState+"Item") - } - } - // Revert to default childMode if placed outside Super Container - $: { - if ( + } else if ( $builderStore.inBuilder && !parentState && childMode != "containerItem" && $componentStore.selectedComponentPath?.includes($component.id) @@ -392,11 +397,27 @@ }; $: state.synchProperties($$props); - $: error = mode == "tabs" && containers?.length < 1 ? "- At least one child Super Container needed to render Tabs" - : mode == "splitview" && containers?.length < 2 ? "- At least two child Super Containers needed to render a Split View" - : bound == "array" && !Array.isArray(slots) ? "- Invalid Source - Unable to parse array" + + // Catch Erroneous states last after all reactive statememtns + $: error = mode == "tabs" && containers?.length < 1 ? "At least one child Super Container needed to render Tabs" + : mode == "splitview" && containers?.length < 2 ? "At least two child Super Containers needed to render a Split View" + : bound == "dataprovider" && !dataprovider ? "Please place inside a Data Provider" + : bound == "array" && !slots ? "Error Parsing Source Array" + : $parentState == "grid" && colSpan > $parentGridStore.gridColumns ? "Out of Grid Bounds - Parent has only " + $parentGridStore.gridColumns + " Columns" + : $parentState == "grid" && rowSpan > $parentGridStore.gridRows ? "Out of Grid Bounds - Parent has only " + $parentGridStore.gridRows + " Rows" : undefined + function safeParse(str) { + let parsed; + + try { + parsed = JSON.parse(str); + } catch (error) { + console.debug(error) + } + return parsed; + } + onMount(() => { if ( mode == "tabs" && containers.length > 0 ) { @@ -417,6 +438,7 @@ }); setContext("superContainer", state); + setContext("superContainerParams", gridStore ) (resizing ? state.resize(e) : null)} /> -{#if $childState != "hidden" } +{#if $childState != "hidden" && $childState != "tabItem" }
{#if error && $builderStore.inBuilder} -

🛑 {error}

+

🔔 {error}

{/if} {#if mode == "tabs" && containers?.length > 0} @@ -456,6 +478,8 @@ {tabsQuiet} {tabsAlignment} {tabsSize} + {tabsIconsOnly} + {tabsEmphasized} /> {/if} @@ -470,12 +494,12 @@ {#if mode == "grid" && $builderStore.inBuilder} {#each gridPreviewSlots as guide, idx }
- {idx + gridPreviewSlots?.length } + {idx + ( gridColumns * gridRows ) - gridPreviewSlots?.length }
{/each} {/if} - {:else if bound == "array" && slots && !error} + {:else if bound == "array" && slots?.length} {#each slots as row, idx } @@ -485,7 +509,7 @@ {#if mode == "grid" && $builderStore.inBuilder} {#each gridPreviewSlots as guide, idx }
- {idx + gridPreviewSlots?.length } + {idx + ( gridColumns * gridRows ) - gridPreviewSlots?.length }
{/each} {/if} @@ -493,7 +517,7 @@ {:else if mode == "grid" && $builderStore.inBuilder && $component.empty } {#each gridPreviewSlots as _, idx }
- {idx + gridPreviewSlots?.length } + {idx + ( gridColumns * gridRows ) - gridPreviewSlots?.length } {#if idx == 0} {/if} @@ -532,6 +556,7 @@ } .super-grid { display: grid; + position: relative; grid-template-columns: repeat(var(--grid-columns), 1fr); grid-template-rows: repeat(var(--grid-rows), 1fr); column-gap: var(--grid-column-gap); @@ -582,9 +607,13 @@ } .error { - font-size: 15px; + font-size: 16px; font-weight: 500; - color: var(--primaryColor); + padding: 1rem; + width: 100%; + border: 1px solid var(--spectrum-global-color-red-500); + border-radius: 4px; + background-color: var(--backgroundColoe); } .spectrum-OpacityCheckerboard { diff --git a/src/RepeaterPreview.svelte b/src/RepeaterPreview.svelte index ff0c113..52d35fe 100644 --- a/src/RepeaterPreview.svelte +++ b/src/RepeaterPreview.svelte @@ -1,39 +1,30 @@ {#if inBuilder} - -
- {#if mode == "container"} - - - - - {:else if mode == "tabs"} - - - +
+ + + - {:else if mode == "splitview"} - - - - - - - - {/if} -
{/if} \ No newline at end of file + diff --git a/src/TabControl.svelte b/src/TabControl.svelte index 78eb7bf..d80b738 100644 --- a/src/TabControl.svelte +++ b/src/TabControl.svelte @@ -9,6 +9,8 @@ export let tabsQuiet export let tabsSize export let tabsAlignment + export let tabsIconsOnly + export let tabsEmphasized let tabs = [] @@ -27,42 +29,57 @@ -
- {#each containers as container, idx } -
state.selectTab(container.id)} - > - { container.title || "Tab " + idx } -
- {/each} -
+{#if containers?.length} +
+ {#each containers as container, idx } +
state.selectTab(container.id)} + > + {#if container.icon} +
+ {@html container?.icon} +
+ {/if} + {#if !tabsIconsOnly || !container.icon} + + { container.title || "Tab " + idx } + + {/if} +
+ {/each} +
+{/if} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b8e85d6..1860731 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3141,7 +3141,7 @@ msgpackr@^1.5.2: optionalDependencies: msgpackr-extract "^3.0.2" -nanoid@^3.3.6: +nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== @@ -3899,11 +3899,11 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.2.10: - version "8.4.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" - integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + version "8.4.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.32.tgz#1dac6ac51ab19adb21b8b34fd2d93a86440ef6c9" + integrity sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw== dependencies: - nanoid "^3.3.6" + nanoid "^3.3.7" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -4073,9 +4073,9 @@ process-warning@^1.0.0: integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== process-warning@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.3.1.tgz#0caf992272c439f45dd416e1407ee25a3d4c778a" - integrity sha512-JjBvFEn7MwFbzUDa2SRtKJSsyO0LlER4V/FmwLMhBlXNbGgGxdyFCxIdMDLerWUycsVUyaoM9QFLvppFy4IWaQ== + version "2.3.2" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.3.2.tgz#70d8a3251aab0eafe3a595d8ae2c5d2277f096a5" + integrity sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA== process@^0.11.10: version "0.11.10"