diff --git a/docs/query-builder.md b/docs/query-builder.md index 9a9c91e..3f66640 100644 --- a/docs/query-builder.md +++ b/docs/query-builder.md @@ -181,6 +181,7 @@ The `label`, which gets specified after **AS**, denotes the name of the column t | `node:{node}:{data}` | Specify one of the metadata `{data}` options or an `{attribute}` to return for an intermediary node. | [Link](examples.md#data) | | `node:{node}:/regular_expression/` | Returns a match according to a regular expression between `/`'s. | [Link](examples.md#regular-expression) | | `node` | Use the label to edit the column header of the first column | [Link](examples.md#first-column-header) | +| `text({node})` | Returns plain text for a node by stripping page refs, tags, and button markup. | N/A | | `add({label1}, {label2})` | Add the values of two columns. Supports adding values to dates. | [Link](examples.md#add-or-subtract) | | `subtract({label1}, {label2})` | Subtract the values betweenn two columns. Supports adding values to dates. | [Link](examples.md#add-or-subtract) | diff --git a/src/utils/predefinedSelections.ts b/src/utils/predefinedSelections.ts index 719e9ff..4ed4c94 100644 --- a/src/utils/predefinedSelections.ts +++ b/src/utils/predefinedSelections.ts @@ -16,6 +16,7 @@ import { IconNames } from "@blueprintjs/icons"; import parseQuery from "./parseQuery"; import toCellValue from "./toCellValue"; import createBlock from "roamjs-components/writes/createBlock"; +import toTextOnlyValue from "./toTextOnlyValue"; const ALIAS_TEST = /^node$/i; const REGEX_TEST = /\/([^}]*)\//; @@ -25,6 +26,7 @@ const CREATE_BY_TEST = /^\s*(author|create(d)?\s*by)\s*$/i; const EDIT_BY_TEST = /^\s*(last\s*)?edit(ed)?\s*by\s*$/i; const SUBTRACT_TEST = /^subtract\(([^,)]+),([^,)]+)\)$/i; const ADD_TEST = /^add\(([^,)]+),([^,)]+)\)$/i; +const TEXT_ONLY_TEST = /^text\(\s*([^)]+?)\s*\)$/i; const NODE_TEST = /^node:(\s*[^:]+\s*)(:.*)?$/i; const ACTION_TEST = /^action:\s*([^:]+)\s*(?::(.*))?$/i; const DATE_FORMAT_TEST = /^date-format\(([^,)]+),([^,)]+)\)$/i; @@ -216,6 +218,9 @@ const EDIT_DATE_SUGGESTIONS: SelectionSuggestion[] = [ ]; const CREATE_BY_SUGGESTIONS: SelectionSuggestion[] = [{ text: "created by" }]; const EDIT_BY_SUGGESTIONS: SelectionSuggestion[] = [{ text: "edited by" }]; +const TEXT_ONLY_SUGGESTIONS: SelectionSuggestion[] = [ + { text: "text({{node}})" }, +]; // TOO SLOW // const ATTR_SUGGESTIONS: SelectionSuggestion[] = ( // window.roamAlphaAPI.data.fast.q( @@ -402,6 +407,26 @@ const predefinedSelections: PredefinedSelection[] = [ }, suggestions: [{ text: "node" }], }, + { + test: TEXT_ONLY_TEST, + pull: ({ match, where }) => { + const node = (match?.[1] || "").trim().replace(/^\?/, ""); + return node && isVariableExposed(where, node) + ? `(pull ?${node} [:block/string :node/title :block/uid])` + : ""; + }, + mapper: (r) => { + const uid = r?.[":block/uid"] || ""; + return { + "": toTextOnlyValue({ + value: r?.[":node/title"] || r?.[":block/string"] || "", + uid, + }), + "-uid": uid, + }; + }, + suggestions: TEXT_ONLY_SUGGESTIONS, + }, { test: SUBTRACT_TEST, pull: ({ returnNode }) => `(pull ?${returnNode} [:db/id])`, diff --git a/src/utils/toTextOnlyValue.ts b/src/utils/toTextOnlyValue.ts new file mode 100644 index 0000000..42884df --- /dev/null +++ b/src/utils/toTextOnlyValue.ts @@ -0,0 +1,28 @@ +import toCellValue from "./toCellValue"; + +const BUTTON_MARKUP_REGEX = /\{\{([^{}]*)\}\}/g; +const BUTTON_PREFIX_REGEX = /^\s*(?:\[\[)?button(?:\]\])?\s*:/i; + +const stripButtonMarkup = (value: string): string => { + let output = value; + let previous = ""; + while (output !== previous) { + previous = output; + output = output.replace(BUTTON_MARKUP_REGEX, (_, inner: string) => + inner.replace(BUTTON_PREFIX_REGEX, "").trim() + ); + } + return output; +}; + +const toTextOnlyValue = ({ + value, + uid, + defaultValue = "", +}: { + value: number | Date | string; + uid: string; + defaultValue?: string; +}) => stripButtonMarkup(toCellValue({ value, uid, defaultValue })); + +export default toTextOnlyValue;