javascriptfrontendsolid-js

How can i make solid-mason work with items of undefinite heights


I want to make the solid mason library work with elements with non-defined initial height (it doesn't).

The component causing an issue is shown below, the size is undefinite as we don't know the amount of text coming. This causes a miscalculation by the library and we end up with elements overlapping.

export const NoteItem = (props: { noteInfo: NoteRef }) => {
  const [loading, setLoading] = createSignal(true);
  const [noteContent, setNoteContent] = createSignal('');

  const content = async () => {
    const note = await get_note_content(props.noteInfo.notepath);
    setNoteContent(note);
    setLoading(false);
  };

  onMount(() => {
    content();
  });

  return (
    <Show when={!loading()}>
      <Dialog>
        <DialogTrigger class="w-full">
          <div class="max-h-[500px] min-h-[50px] w-full overflow-hidden rounded-xl border border-transparent bg-foreground/10 p-6 shadow-md hover:border-secondary">
            <NoteContent content={noteContent()} />
          </div>
          {props.noteInfo.metadata.name === '' ? null : (
            <p class="mt-[10px] h-5 overflow-hidden text-ellipsis whitespace-nowrap text-center text-sm font-medium text-muted/80">
              {props.noteInfo.metadata.name}
            </p>
          )}
        </DialogTrigger>
        <ViewBox source={props.noteInfo} content={noteContent()} type="note" />
      </Dialog>
    </Show>
  );
};

const NoteContent = (props: { content: string }) => {
  const [container, setContainer] = createSignal<HTMLDivElement>();

  // createEffect(() => {
  //   window.dispatchEvent(new Event('resize'));
  // });

  createTiptapEditor(() => ({
    element: container()!,
    editable: false,
    extensions: [
      StarterKit,
      Markdown.configure({
        transformPastedText: true,
        transformCopiedText: true,
      }),
    ],
    editorProps: {
      attributes: {
        class:
          'focus:outline-none prose dark:prose-invert prose-sm sm:prose-base lg:prose-lg xl:prose-xl max-w-full h-full ',
      },
    },
    content: props.content,
  }));

  return <div ref={setContainer} class="h-full overflow-y-scroll text-start" />;
};

When i resize the window, the manson component of the library recalculate the whole grid and elements go to the right positions, so i tried to sent a resize js event from my note component but it didn't fix the issue.

createEffect(() => {
  window.dispatchEvent(new Event('resize'));
});

Solution

  • You have several issues with your implementation. The outcome is the result of those issues.

    Solid-Mason already listens for and handles the resize event, so you don't have to fire it manually. You should not fire resize event but use it to interact with the underlying elements if necessary. For that you can use a ref or onMount hook. But, for this use case it is not even necessary because your intention is fetching the data first then render it.

    You split the component state between three signals, which is hard to manage and it makes your component re-render multiple times. Since you are using an async value, it is best to create a resource and use its state and data: https://www.solidjs.com/docs/latest/api#createresource

    You are fecthing content after component renders. You should fetch the content first than render it. So this logic is not only wrong but also triggers multiple unnecessary re-renders even after data becomes available:

    const content = async () => {
      const note = await get_note_content(props.noteInfo.notepath);
      setNoteContent(note);
      setLoading(false);
    };
    
    onMount(() => {
      content();
    });
    

    Using a resource fixes this issues as well. Just fetch the data in the main component and render the content once the resource resolves to a value.

    Having different amount of text should not be a problem since solid-mason does not require you to provide same height elements. You can clean up your CSS a bit to provide fixed width elements.

    If you create a live demo using one of the online playgrounds like stackblitz or codesandbox, people can offer more concrete solutions.