Thread

The thread control visualizes nested discussion entries.

Import
the javascript logo
import { Thread } from "@schukai/monster/source/components/state/thread.mjs";
Source
the git logo
Package
the npm logo
Since
3.77.0
Add replyClear thread

Introduction

monster-thread visualizes nested discussion entries with hierarchical replies and relative timestamps.

Key Features

  • Nested entries: Supports parent and child messages.
  • Relative time display: Keeps conversations readable over time.
  • Programmatic updates: Accepts new entries and complete thread data from JavaScript.

Thread

This example renders a small discussion thread with nested replies and relative timestamps.

Add replyReset

Javascript

import "@schukai/monster/source/components/state/thread.mjs";
import "@schukai/monster/source/components/form/button.mjs";

const thread = document.getElementById("thread-demo");
const addButton = document.getElementById("thread-demo-add");
const resetButton = document.getElementById("thread-demo-reset");

const seed = [
  {
    id: "1",
    author: "Jane",
    message: "We should ship the updated checkout flow today.",
    timestamp: Date.now() - 1000 * 60 * 35,
    children: [
      {
        id: "2",
        author: "Mark",
        message: "Design review is done. QA is running the final pass.",
        timestamp: Date.now() - 1000 * 60 * 18,
      },
    ],
  },
  {
    id: "3",
    author: "Nina",
    message: "Monitoring is already prepared for the rollout window.",
    timestamp: Date.now() - 1000 * 60 * 8,
  },
];

thread.setOption("entries", seed);

addButton.addEventListener("click", () => {
  thread.addEntry(
    {
      author: "Mila",
      message: "Support macros are updated and linked in the rollout plan.",
      timestamp: Date.now(),
    },
    "1",
  );
});

resetButton.addEventListener("click", () => {
  thread.setOption("entries", seed);
});<script type="module">import "@schukai/monster/source/components/state/thread.mjs";
import "@schukai/monster/source/components/form/button.mjs";

const thread = document.getElementById("thread-demo-run");
const addButton = document.getElementById("thread-demo-add-run");
const resetButton = document.getElementById("thread-demo-reset-run");

const seed = [
  {
    id: "1",
    author: "Jane",
    message: "We should ship the updated checkout flow today.",
    timestamp: Date.now() - 1000 * 60 * 35,
    children: [
      {
        id: "2",
        author: "Mark",
        message: "Design review is done. QA is running the final pass.",
        timestamp: Date.now() - 1000 * 60 * 18,
      },
    ],
  },
  {
    id: "3",
    author: "Nina",
    message: "Monitoring is already prepared for the rollout window.",
    timestamp: Date.now() - 1000 * 60 * 8,
  },
];

thread.setOption("entries", seed);

addButton.addEventListener("click", () => {
  thread.addEntry(
    {
      author: "Mila",
      message: "Support macros are updated and linked in the rollout plan.",
      timestamp: Date.now(),
    },
    "1",
  );
});

resetButton.addEventListener("click", () => {
  thread.setOption("entries", seed);
});</script>

HTML

<monster-thread id="thread-demo"></monster-thread>
<div style="display:flex;flex-wrap:wrap;gap:var(--monster-space-3);margin-top:var(--monster-space-4);">
  <monster-button id="thread-demo-add">Add reply</monster-button>
  <monster-button id="thread-demo-reset">Reset</monster-button>
</div>

Stylesheet

/** no additional stylesheet is defined **/
Open in playground

Review Thread With Nested Replies

This example uses monster-thread as a review conversation with top-level questions and direct replies inside one discussion tree.

Approve copyAdd question

Javascript

import "@schukai/monster/source/components/state/thread.mjs";
import "@schukai/monster/source/components/form/button.mjs";

const thread = document.getElementById("thread-review-demo");
const approveButton = document.getElementById("thread-review-approve");
const questionButton = document.getElementById("thread-review-question");

thread.setOption("entries", [
  {
    id: "review-1",
    author: "Lea",
    message: "Headline copy is ready for the release email.",
    timestamp: Date.now() - 1000 * 60 * 45,
    children: [
      {
        id: "review-2",
        author: "Tom",
        message: "Please keep the CTA shorter for mobile.",
        timestamp: Date.now() - 1000 * 60 * 20,
      },
    ],
  },
]);

approveButton.setOption("actions.click", () => {
  thread.addEntry(
    {
      author: "Mira",
      message: "Approved. I am handing this over to localization.",
      timestamp: Date.now(),
    },
    "review-1",
  );
});

questionButton.setOption("actions.click", () => {
  thread.addEntry({
    author: "Alex",
    message: "Do we need a separate subject line for inactive users?",
    timestamp: Date.now(),
  });
});<script type="module">import "@schukai/monster/source/components/state/thread.mjs";
import "@schukai/monster/source/components/form/button.mjs";

const thread = document.getElementById("thread-review-demo-run");
const approveButton = document.getElementById("thread-review-approve-run");
const questionButton = document.getElementById("thread-review-question-run");

thread.setOption("entries", [
  {
    id: "review-1",
    author: "Lea",
    message: "Headline copy is ready for the release email.",
    timestamp: Date.now() - 1000 * 60 * 45,
    children: [
      {
        id: "review-2",
        author: "Tom",
        message: "Please keep the CTA shorter for mobile.",
        timestamp: Date.now() - 1000 * 60 * 20,
      },
    ],
  },
]);

approveButton.setOption("actions.click", () => {
  thread.addEntry(
    {
      author: "Mira",
      message: "Approved. I am handing this over to localization.",
      timestamp: Date.now(),
    },
    "review-1",
  );
});

questionButton.setOption("actions.click", () => {
  thread.addEntry({
    author: "Alex",
    message: "Do we need a separate subject line for inactive users?",
    timestamp: Date.now(),
  });
});</script>

HTML

<monster-thread id="thread-review-demo"></monster-thread>
<div style="display:flex;flex-wrap:wrap;gap:var(--monster-space-3);margin-top:var(--monster-space-4);">
  <monster-button id="thread-review-approve">Approve copy</monster-button>
  <monster-button id="thread-review-question">Add question</monster-button>
</div>

Stylesheet

/** no additional stylesheet is defined **/
Open in playground

Component Design

The thread control keeps an internal entry tree, renders an empty state when necessary, and updates visible timestamps on an interval.

Styling Hooks

  • labels.nothingToReport: Changes the empty-state copy.
  • entries: Supplies the nested discussion structure.
  • ::part(...): Styles the rendered thread entries and messages.

HTML Structure

<monster-thread></monster-thread>

JavaScript Initialization

const element = document.createElement('monster-thread');
document.body.appendChild(element);

Exported

Thread

Derived from

CustomElement

Options

The Options listed in this section are defined directly within the class. This class is derived from several parent classes, including the CustomElement class. Therefore, it inherits Options from these parent classes. If you cannot find a specific Options in this list, we recommend consulting the documentation of the CustomElement.

Option
Type
Default
Description
templates
object
Template definitions
templates.main
string
undefined
Main template
labels
object
Labels
labels.nothingToReport
string
There is nothing to report yet.
Label for empty state
updateFrequency
number
10000
Update frequency in milliseconds for the timestamp

  • since
  • deprecated

Properties and Attributes

The Properties and Attributes listed in this section are defined directly within the class. This class is derived from several parent classes, including the CustomElement class and ultimately from HTMLElement. Therefore, it inherits Properties and Attributes from these parent classes. If you cannot find a specific Properties and Attributes in this list, we recommend consulting the documentation of the CustomElement.

  • data-monster-options: Sets the configuration options for the collapse component when used as an HTML attribute.
  • data-monster-option-[name]: Sets the value of the configuration option [name] for the collapse component when used as an HTML attribute.

Methods

The methods listed in this section are defined directly within the class. This class is derived from several parent classes, including the CustomElement class and ultimately from HTMLElement. Therefore, it inherits methods from these parent classes. If you cannot find a specific method in this list, we recommend consulting the documentation of the CustomElement.

Behavioral methods

addEntry(entry,)
Parameters
  • entry {entry|object}: entry
  • undefined {string|null}: parentId
Returns
  • {Thread}
Add an entry to the thread.
addMessage(message,date,)
Parameters
  • message {string}: message
  • date {date}: date
  • undefined {string|null}: parentId
Returns
  • {Thread}
Throws
  • {TypeError} message is not a string
Add a message entry to the thread.
clear()
Returns
  • {Thread}
Clear the thread.
toggleEntry(entryId)
Parameters
  • entryId {string}: entryId
Returns
  • {Thread}
Toggle the collapsed state of an entry.

Structural methods

getClosedEntries()
Returns
  • {string[]}
Get ids of entries that are currently collapsed.
getCollapsedState()
Returns
  • {object<string,}: boolean>}
Get collapsed state for all entries.
getOpenEntries()
Returns
  • {string[]}
Get ids of entries that are currently open.
getOption(path,)
Parameters
  • path {string}: path
  • undefined {*}: defaultValue
Returns
  • {*}
setCollapsedState(boolean>})
Parameters
  • boolean>} {object<string,}: stateMap
Returns
  • {Thread}
Set collapsed state for entries by id.
setOption(path,value)
Parameters
  • path {string}: path
  • value {*}: value
Returns
  • {Thread}
setOptions(options)
Parameters
  • options {object|string}: options
Returns
  • {Thread}

Static methods

[instanceSymbol]()
Returns
  • {symbol}
This method is called by the instanceof operator.
getCSSStyleSheet()
Returns
  • {CSSStyleSheet[]}
getTag()
Returns
  • {string}

Lifecycle methods

Lifecycle methods are called by the environment and are usually not intended to be called directly.

[assembleMethodSymbol]()
Returns
  • {void}

Events

This component does not fire any public events. It may fire events that are inherited from its parent classes.

The current width of the area is too small to display the content correctly.