{#if $$slots.default}
diff --git a/packages/frontend-core/src/components/grid/layout/ButtonColumn.svelte b/packages/frontend-core/src/components/grid/layout/ButtonColumn.svelte
index 159f0dbd45..d059c160ae 100644
--- a/packages/frontend-core/src/components/grid/layout/ButtonColumn.svelte
+++ b/packages/frontend-core/src/components/grid/layout/ButtonColumn.svelte
@@ -70,6 +70,7 @@
rowIdx={row.__idx}
selected={rowSelected}
highlighted={rowHovered || rowFocused}
+ metadata={row.__metadata}
>
{#each buttons as button}
diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte
index 8d73ee3028..f80f05b87c 100644
--- a/packages/frontend-core/src/components/grid/layout/Grid.svelte
+++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte
@@ -59,6 +59,7 @@
export let darkMode
export let isCloud = null
export let allowViewReadonlyColumns = false
+ export let rowConditions = null
// Unique identifier for DOM nodes inside this instance
const gridID = `grid-${Math.random().toString().slice(2)}`
@@ -114,6 +115,8 @@
buttons,
darkMode,
isCloud,
+ allowViewReadonlyColumns,
+ rowConditions,
})
// Derive min height and make available in context
diff --git a/packages/frontend-core/src/components/grid/stores/conditions.js b/packages/frontend-core/src/components/grid/stores/conditions.js
index 9fcec770dc..04eb10b89b 100644
--- a/packages/frontend-core/src/components/grid/stores/conditions.js
+++ b/packages/frontend-core/src/components/grid/stores/conditions.js
@@ -1,19 +1,24 @@
import { writable, get } from "svelte/store"
import { derivedMemo, QueryUtils } from "../../../utils"
+import { OperatorOptions } from "@budibase/shared-core"
+import { FieldType } from "@budibase/types"
+import { processString, processStringSync } from "@budibase/string-templates"
export const createStores = () => {
- const conditionMetadata = writable({})
+ const cellMetadata = writable({})
+ const rowMetadata = writable({})
return {
- conditionMetadata,
+ cellMetadata,
+ rowMetadata,
}
}
export const deriveStores = context => {
const { columns } = context
- // Derive and memoize the conditions present in our columns so that we only
- // recompute condition metdata when absolutely necessary
- const conditions = derivedMemo(columns, $columns => {
+ // Derive and memoize the cell conditions present in our columns so that we
+ // only recompute condition metadata when absolutely necessary
+ const cellConditions = derivedMemo(columns, $columns => {
let newConditions = []
for (let column of $columns) {
for (let condition of column.conditions || []) {
@@ -28,96 +33,147 @@ export const deriveStores = context => {
})
return {
- conditions,
+ cellConditions,
}
}
export const initialise = context => {
- const { conditionMetadata, conditions, rows } = context
+ const { cellMetadata, cellConditions, rowConditions, rowMetadata, rows } =
+ context
- // Evaluates an array of conditions against a certain row and returns the
- // resultant metadata
- const evaluateConditions = (row, conditions) => {
- let metadata = { version: row._rev }
- for (let condition of conditions) {
- try {
- let {
- column,
- type,
- referenceValue,
- operator,
- metadataKey,
- metadataValue,
- } = condition
- let value = row[column]
-
- // Coerce values into correct types for primitives
- if (type === "number") {
- referenceValue = parseFloat(referenceValue)
- value = parseFloat(value)
- } else if (type === "datetime") {
- if (referenceValue) {
- referenceValue = new Date(referenceValue).toISOString()
- }
- if (value) {
- value = new Date(value).toISOString()
- }
- } else if (type === "boolean") {
- referenceValue = `${referenceValue}`.toLowerCase() === "true"
- value = `${value}`.toLowerCase() === "true"
- }
-
- // Build lucene compatible condition expression
- const luceneFilter = {
- operator,
- type,
- field: "value",
- value: referenceValue,
- }
- const query = QueryUtils.buildQuery([luceneFilter])
- const result = QueryUtils.runQuery([{ value }], query)
- if (result.length > 0) {
- if (!metadata[column]) {
- metadata[column] = {}
- }
- metadata[column][metadataKey] = metadataValue
- }
- } catch {
- // Swallow
- }
- }
- return metadata
- }
-
- // Recompute all metadata if conditions change
- conditions.subscribe($conditions => {
+ // Recompute all cell metadata if cell conditions change
+ cellConditions.subscribe($conditions => {
let metadata = {}
- if ($conditions.length) {
+ if ($conditions?.length) {
for (let row of get(rows)) {
- metadata[row._id] = evaluateConditions(row, $conditions)
+ metadata[row._id] = evaluateCellConditions(row, $conditions)
}
}
- conditionMetadata.set(metadata)
+ cellMetadata.set(metadata)
})
- // Recompute specific rows when they change
- rows.subscribe($rows => {
- const $conditions = get(conditions)
- if (!$conditions.length) {
- return
- }
- const metadata = get(conditionMetadata)
- let updates = {}
- for (let row of $rows) {
- if (!row._rev || metadata[row._id]?.version !== row._rev) {
- updates[row._id] = evaluateConditions(row, $conditions)
+ // Recompute all row metadata if row conditions change
+ rowConditions.subscribe($conditions => {
+ let metadata = {}
+ if ($conditions?.length) {
+ for (let row of get(rows)) {
+ metadata[row._id] = evaluateRowConditions(row, $conditions)
}
}
- if (Object.keys(updates).length) {
- conditionMetadata.update(state => ({
+ rowMetadata.set(metadata)
+ })
+
+ // Recompute metadata for specific rows when they change
+ rows.subscribe($rows => {
+ const $cellConditions = get(cellConditions)
+ const $rowConditions = get(rowConditions)
+ const processCells = $cellConditions?.length > 0
+ const processRows = $rowConditions?.length > 0
+ if (!processCells && !processRows) {
+ return
+ }
+ const $cellMetadata = get(cellMetadata)
+ const $rowMetadata = get(rowMetadata)
+ let cellUpdates = {}
+ let rowUpdates = {}
+ for (let row of $rows) {
+ // Process cell metadata
+ if (processCells) {
+ if (!row._rev || $cellMetadata[row._id]?.version !== row._rev) {
+ cellUpdates[row._id] = evaluateCellConditions(row, $cellConditions)
+ }
+ }
+ // Process row metadata
+ if (processRows) {
+ if (!row._rev || $rowMetadata[row._id]?.version !== row._rev) {
+ rowUpdates[row._id] = evaluateRowConditions(row, $rowConditions)
+ }
+ }
+ }
+ if (Object.keys(cellUpdates).length) {
+ cellMetadata.update(state => ({
...state,
- ...updates,
+ ...cellUpdates,
+ }))
+ }
+ if (Object.keys(rowUpdates).length) {
+ rowMetadata.update(state => ({
+ ...state,
+ ...rowUpdates,
}))
}
})
}
+
+// Evaluates an array of cell conditions against a certain row and returns the
+// resultant metadata
+const evaluateCellConditions = (row, conditions) => {
+ let metadata = { version: row._rev }
+ for (let condition of conditions) {
+ try {
+ let {
+ column,
+ type,
+ referenceValue,
+ operator,
+ metadataKey,
+ metadataValue,
+ } = condition
+ let value = row[column]
+
+ // Coerce values into correct types for primitives
+ if (type === "number") {
+ referenceValue = parseFloat(referenceValue)
+ value = parseFloat(value)
+ } else if (type === "datetime") {
+ if (referenceValue) {
+ referenceValue = new Date(referenceValue).toISOString()
+ }
+ if (value) {
+ value = new Date(value).toISOString()
+ }
+ } else if (type === "boolean") {
+ referenceValue = `${referenceValue}`.toLowerCase() === "true"
+ value = `${value}`.toLowerCase() === "true"
+ }
+
+ // Build lucene compatible condition expression
+ const luceneFilter = {
+ operator,
+ type,
+ field: "value",
+ value: referenceValue,
+ }
+ const query = QueryUtils.buildQuery([luceneFilter])
+ const result = QueryUtils.runQuery([{ value }], query)
+ if (result.length > 0) {
+ if (!metadata[column]) {
+ metadata[column] = {}
+ }
+ metadata[column][metadataKey] = metadataValue
+ }
+ } catch {
+ // Swallow
+ }
+ }
+ return metadata
+}
+
+// Evaluates an array of row conditions against a certain row and returns the
+// resultant metadata
+const evaluateRowConditions = (row, conditions) => {
+ let metadata = { version: row._rev }
+ for (let condition of conditions) {
+ try {
+ const { metadataKey, metadataValue, value } = condition
+ console.log("JS")
+ const result = processStringSync(value, { row })
+ if (result === true) {
+ metadata[metadataKey] = metadataValue
+ }
+ } catch {
+ // Swallow
+ }
+ }
+ return metadata
+}
diff --git a/packages/frontend-core/src/components/grid/stores/config.js b/packages/frontend-core/src/components/grid/stores/config.js
index 6da6ebf11e..d2d07c630e 100644
--- a/packages/frontend-core/src/components/grid/stores/config.js
+++ b/packages/frontend-core/src/components/grid/stores/config.js
@@ -15,6 +15,7 @@ export const createStores = context => {
const columnWhitelist = getProp("columnWhitelist")
const notifySuccess = getProp("notifySuccess")
const notifyError = getProp("notifyError")
+ const rowConditions = getProp("rowConditions")
return {
datasource,
@@ -26,6 +27,7 @@ export const createStores = context => {
columnWhitelist,
notifySuccess,
notifyError,
+ rowConditions,
}
}
diff --git a/packages/frontend-core/src/components/grid/stores/viewport.js b/packages/frontend-core/src/components/grid/stores/viewport.js
index e2eb5a4821..fc5960f14c 100644
--- a/packages/frontend-core/src/components/grid/stores/viewport.js
+++ b/packages/frontend-core/src/components/grid/stores/viewport.js
@@ -11,7 +11,8 @@ export const deriveStores = context => {
width,
height,
rowChangeCache,
- conditionMetadata,
+ cellMetadata,
+ rowMetadata,
} = context
// Derive visible rows
@@ -35,21 +36,24 @@ export const deriveStores = context => {
scrolledRowCount,
visualRowCapacity,
rowChangeCache,
- conditionMetadata,
+ cellMetadata,
+ rowMetadata,
],
([
$rows,
$scrolledRowCount,
$visualRowCapacity,
$rowChangeCache,
- $conditionMetadata,
+ $cellMetadata,
+ $rowMetadata,
]) => {
return $rows
.slice($scrolledRowCount, $scrolledRowCount + $visualRowCapacity)
.map(row => ({
...row,
...$rowChangeCache[row._id],
- __metadata: $conditionMetadata[row._id],
+ __metadata: $rowMetadata[row._id],
+ __cellMetadata: $cellMetadata[row._id],
}))
}
)