Skip to content

Support SmartBlock Buttons in Roam {{[[table]]}} #118

@mdroidian

Description

@mdroidian

reported by Mark L via slack.

tl;dr What's left

  • write getUidFromTablePosition()
  • check what happens if {{[[table]]}} block ALSO has a {{SmartBlock}} button

Notes

I started looking into this, but didn't get a chance to finish. Here were my findings:

If a SmartBlock button is part of roam {{[[table]]}} the observer doesn't catch it because it's parent blockUid is actually the table block, so the match check fails as there is no SmartBlock button in it's textContent.

image

It doesn't look like there the original uid that holds the SmartBlock button (VeSLtlz0N) is accessible in the DOM

image

Possible solution:

smartblocks/src/index.ts

Lines 543 to 561 in 170690d

const registerElAsSmartBlockTrigger = ({
textContent,
text,
el,
parentUid,
hideIcon,
}: {
textContent: string;
text: string;
el: HTMLElement;
parentUid: string;
hideIcon?: false;
}) => {
// We include textcontent here bc there could be multiple smartblocks in a block
const regex = new RegExp(
`{{(${textContent.replace(/\+/g, "\\+")}):(?:42)?SmartBlock:(.*?)}}`
);
const match = regex.exec(text);
if (match) {

If !match we could check text (which is parentUid text) for {{[[table]]}}

If true

  • calculate row/column position based on DOM (el is the button element in the roam )
  • get the tree of parentUid
  • get original uid based on row/column
  • something like:

        // check if parent is roam {{[[table]]}}
        const getMatchFromRoamTable = () => {
          const parentText = getTextByBlockUid(parentUid);
          const roamTablePattern = /\{\{\[\[table\]\]\}\}/;
          const isInRoamTable = roamTablePattern.exec(parentText);
          if (!isInRoamTable) return null;
          const tableEl = el.closest("table");
          if (!tableEl) return null;
    
          // Get the row and column of the button
          const rows = Array.from(tableEl.querySelectorAll("tr")).filter(
            (row) => row.innerHTML.trim() !== "" // first row is blank?
          ); 
          let position = { row: 0, column: 0 };
          rows.map((row, rowIndex) => {
            const cells = Array.from(row.querySelectorAll("td"));
            cells.map((cell, colIndex) => {
              if (cell.contains(el)) {
                position = { row: rowIndex + 1, column: colIndex + 1 };
              }
            });
          });
          if (!position.row || !position.column) return null;
    
          // Get UID from tree based position
          const tree = getBasicTreeByParentUid(parentUid);
          const uid = getUidFromTablePosition() // need to create this
          const tableButtonText = getTextByBlockUid(uid);
          return regex.exec(tableButtonText);
        };
        if (!match) match = getMatchFromRoamTable();

    but the getUidFromTablePosition() gets a little tricky as there are multiple ways to construct a table

    image

    It looks like

    • node with no children = 1 row
    • node with 1 child = 1 row
    • node with 1+n children = 1+n rows

    I didn't get a chance to finish writing that, but unless I missed something, it seems quite possible.

    Oh, also, should consider if the {{[[table]]}} block ALSO has a {{SmartBlock}} button ... check to make sure it gets the data-roamjs-smartblock-button first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions