@kubex/zinc 1.1.79 → 1.1.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/custom-elements.json +1398 -109
  2. package/dist/vscode.html-custom-data.json +202 -3
  3. package/dist/web-types.json +478 -4
  4. package/dist/zn.d.ts +467 -8
  5. package/dist/zn.min.js +388 -316
  6. package/docs/pages/components/inline-edit.md +22 -0
  7. package/docs/pages/components/input.md +21 -0
  8. package/docs/pages/components/remarkd-editor.md +43 -0
  9. package/docs/pages/components/slash-item.md +126 -0
  10. package/docs/pages/components/slash-menu.md +161 -0
  11. package/docs/pages/components/textarea.md +148 -0
  12. package/docs/pages/components/translations.md +34 -0
  13. package/package.json +1 -1
  14. package/src/components/inline-edit/inline-edit.component.ts +36 -0
  15. package/src/components/inline-edit/inline-edit.test.ts +83 -1
  16. package/src/components/input/input.component.ts +143 -0
  17. package/src/components/input/input.test.ts +113 -1
  18. package/src/components/remarkd-editor/remarkd-editor.component.ts +157 -95
  19. package/src/components/remarkd-editor/remarkd-editor.scss +24 -36
  20. package/src/components/remarkd-editor/remarkd-editor.test.ts +110 -6
  21. package/src/components/slash-item/index.ts +12 -0
  22. package/src/components/slash-item/slash-item.component.ts +76 -0
  23. package/src/components/slash-item/slash-item.scss +5 -0
  24. package/src/components/slash-menu/index.ts +14 -0
  25. package/src/components/slash-menu/slash-menu-controller.ts +361 -0
  26. package/src/components/slash-menu/slash-menu-items.ts +122 -0
  27. package/src/components/slash-menu/slash-menu.component.ts +327 -0
  28. package/src/components/slash-menu/slash-menu.scss +120 -0
  29. package/src/components/slash-menu/slash-menu.test.ts +154 -0
  30. package/src/components/textarea/textarea.component.ts +143 -0
  31. package/src/components/textarea/textarea.test.ts +310 -2
  32. package/src/components/translations/translations.component.ts +31 -0
  33. package/src/components/translations/translations.test.ts +89 -1
  34. package/src/events/events.ts +2 -0
  35. package/src/events/zn-slash-insert.ts +9 -0
  36. package/src/events/zn-slash-select.ts +9 -0
  37. package/src/utilities/caret-position.ts +118 -0
  38. package/src/zinc.ts +3 -0
package/dist/zn.d.ts CHANGED
@@ -1857,9 +1857,292 @@ declare module "components/data-table-filter/index" {
1857
1857
  }
1858
1858
  }
1859
1859
  }
1860
+ declare module "components/slash-menu/slash-menu-items" {
1861
+ export interface SlashMenuItem {
1862
+ /** The text shown in the menu. */
1863
+ label: string;
1864
+ /** The text inserted into the field. Omit for items handled entirely by the `zn-slash-select` event. */
1865
+ value?: string;
1866
+ /** Icon shown against the item, e.g. `tag@lu`. */
1867
+ icon?: string;
1868
+ /** Supporting text shown under the label. */
1869
+ description?: string;
1870
+ /** Extra terms the item can be found by. */
1871
+ keywords?: string | string[];
1872
+ /** Heading the item is listed under. Items without a group are listed first, in source order. */
1873
+ group?: string;
1874
+ /** Overrides the position of the item within its match band. Lower sorts first. */
1875
+ order?: number;
1876
+ /** Identifier passed through on `zn-slash-select`, for items that do something other than insert text. */
1877
+ action?: string;
1878
+ /** Where the caret lands after insertion, as an offset into `value`. Defaults to the end. */
1879
+ caretOffset?: number;
1880
+ /** Listed, but not selectable. */
1881
+ disabled?: boolean;
1882
+ }
1883
+ /**
1884
+ * Registers a named, reusable set of insertions, so a list defined once (e.g. the merge fields
1885
+ * allowed in legal copy) can be referenced from markup with `slash-preset="<name>"`.
1886
+ */
1887
+ export function registerSlashMenuPreset(name: string, items: SlashMenuItem[]): void;
1888
+ /** Removes a preset registered with `registerSlashMenuPreset`. */
1889
+ export function unregisterSlashMenuPreset(name: string): void;
1890
+ /** The names of every registered preset. */
1891
+ export function slashMenuPresetNames(): string[];
1892
+ /** Resolves one or more preset names (comma separated, or an array) to their items. */
1893
+ export function getSlashMenuPreset(names: string | string[]): SlashMenuItem[];
1894
+ /**
1895
+ * Parses the `slash-items` attribute. Accepts a JSON array of items, or the shorthand
1896
+ * `Label={{TOKEN}}, Other={{OTHER}}` (the label may be omitted to use the token as its own label).
1897
+ */
1898
+ export function parseSlashItems(value: string | null | undefined): SlashMenuItem[];
1899
+ /** Filters and ranks items against a query. An empty query keeps every item in its declared order. */
1900
+ export function filterSlashItems(items: SlashMenuItem[], query: string): SlashMenuItem[];
1901
+ }
1902
+ declare module "utilities/caret-position" {
1903
+ export interface CaretCoordinates {
1904
+ top: number;
1905
+ left: number;
1906
+ height: number;
1907
+ }
1908
+ export type TextField = HTMLTextAreaElement | HTMLInputElement;
1909
+ /**
1910
+ * Measures where the caret sits inside a text field, relative to the field's own top/left corner.
1911
+ * There is no browser API for this, so the field is mirrored into an off-screen div and the offset
1912
+ * of a marker span at `index` is read back.
1913
+ */
1914
+ export function getCaretCoordinates(field: TextField, index: number): CaretCoordinates;
1915
+ /**
1916
+ * Projects measured caret coordinates onto the viewport, clamped to the field's box so a caret
1917
+ * scrolled out of view doesn't drag anchored content off with it. Split from the measurement so a
1918
+ * cached measurement can be re-projected as the field scrolls or moves.
1919
+ */
1920
+ export function caretRectFrom(field: TextField, { top, left, height }: CaretCoordinates): DOMRect;
1921
+ /** The caret's position as a viewport rect. */
1922
+ export function getCaretRect(field: TextField, index: number): DOMRect;
1923
+ }
1924
+ declare module "components/slash-menu/slash-menu.component" {
1925
+ import ZincElement from "internal/zinc-element";
1926
+ import ZnIcon from "components/icon/index";
1927
+ import type { CSSResultGroup, PropertyValues } from 'lit';
1928
+ import type { Placement, VirtualElement } from '@floating-ui/dom';
1929
+ import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
1930
+ export const SLASH_ITEM_SELECT = "zn-slash-item-select";
1931
+ /**
1932
+ * @summary A keyboard-driven list of insertions, anchored to the caret of the field that opened it.
1933
+ * @documentation https://zinc.style/components/slash-menu
1934
+ * @status experimental
1935
+ * @since 1.1
1936
+ *
1937
+ * @dependency zn-icon
1938
+ *
1939
+ * @event zn-slash-item-select - Emitted when an item is chosen. Does not cross shadow boundaries; the
1940
+ * component driving the menu (e.g. `zn-textarea`) re-emits it as `zn-slash-select`.
1941
+ *
1942
+ * @csspart panel - The floating panel that holds the list.
1943
+ * @csspart heading - The panel's heading.
1944
+ * @csspart item - An item in the list.
1945
+ * @csspart group-heading - A group heading between items.
1946
+ * @csspart footer - The truncation footer, shown when not every match fits.
1947
+ *
1948
+ * @cssproperty --slash-menu-width - The width of the panel.
1949
+ * @cssproperty --slash-menu-max-height - The maximum height of the panel before it scrolls.
1950
+ */
1951
+ export default class ZnSlashMenu extends ZincElement {
1952
+ static styles: CSSResultGroup;
1953
+ static dependencies: {
1954
+ 'zn-icon': typeof ZnIcon;
1955
+ };
1956
+ private panel;
1957
+ private stopAutoUpdate?;
1958
+ /** Whether the menu is showing. */
1959
+ open: boolean;
1960
+ /** The items to list. Already filtered — the menu displays what it is given. */
1961
+ items: SlashMenuItem[];
1962
+ /** The query the items were matched against, shown in the heading. */
1963
+ query: string;
1964
+ /** The heading shown when there is no query. */
1965
+ heading: string;
1966
+ /** Shown in place of the list when there are no items. */
1967
+ emptyText: string;
1968
+ /** The most items to render at once. Remaining matches are reported in the footer. */
1969
+ maxItems: number;
1970
+ /** The element or caret rect the panel is positioned against. */
1971
+ anchor: Element | VirtualElement | null;
1972
+ /** The preferred placement of the panel. */
1973
+ placement: Placement;
1974
+ /** The gap between the caret and the panel. */
1975
+ distance: number;
1976
+ private activeIndex;
1977
+ private get visibleItems();
1978
+ /** The item that Enter would insert. */
1979
+ get activeItem(): SlashMenuItem | undefined;
1980
+ show(): void;
1981
+ hide(): void;
1982
+ /** Sets the active item by index, wrapping at both ends and skipping disabled items. */
1983
+ setActiveIndex(index: number): void;
1984
+ /** Moves the active item by `delta` places. */
1985
+ moveActive(delta: number): void;
1986
+ /** Chooses the active item, as pressing Enter would. */
1987
+ selectActive(): void;
1988
+ /** Recalculates the panel's position against its anchor. */
1989
+ reposition(): void;
1990
+ connectedCallback(): void;
1991
+ disconnectedCallback(): void;
1992
+ private startPositioner;
1993
+ private stopPositioner;
1994
+ private position;
1995
+ private selectItem;
1996
+ private scrollActiveIntoView;
1997
+ private readonly handleItemMouseDown;
1998
+ protected willUpdate(changed: PropertyValues): void;
1999
+ protected updated(changed: PropertyValues): void;
2000
+ private renderItem;
2001
+ private renderItems;
2002
+ render(): import("lit-html").TemplateResult<1>;
2003
+ }
2004
+ }
2005
+ declare module "components/slash-menu/slash-menu-controller" {
2006
+ import type { TextField } from "utilities/caret-position";
2007
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2008
+ import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
2009
+ import type ZnSlashMenu from "components/slash-menu/slash-menu.component";
2010
+ export interface SlashMenuControllerOptions {
2011
+ /**
2012
+ * Resolves the menu to render results into. Called the first time the menu is needed, so the host
2013
+ * can render it lazily; may return a promise (e.g. after awaiting `updateComplete`).
2014
+ */
2015
+ menu: () => ZnSlashMenu | null | Promise<ZnSlashMenu | null>;
2016
+ /** The available items, unfiltered. Receives the current query so lists can be resolved remotely. */
2017
+ items: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
2018
+ /** The characters that open the menu. Defaults to `/`. */
2019
+ trigger?: () => string;
2020
+ /** Called before an item is inserted. Return `false` to handle the item yourself. */
2021
+ onSelect?: (item: SlashMenuItem, query: string) => boolean;
2022
+ /** Called after an item's value has been written into the field. */
2023
+ onInsert?: (item: SlashMenuItem, value: string) => void;
2024
+ }
2025
+ /**
2026
+ * Drives a slash menu for a plain `<textarea>` or `<input>`: watches the caret for the trigger
2027
+ * sequence, resolves and filters items, and inserts the chosen value.
2028
+ *
2029
+ * The host owns the field and the menu element; this controller owns the interaction.
2030
+ */
2031
+ export class SlashMenuController implements ReactiveController {
2032
+ private readonly host;
2033
+ private readonly options;
2034
+ private readonly caretAnchor;
2035
+ private field;
2036
+ private menu;
2037
+ private triggerIndex;
2038
+ private query;
2039
+ /** Trigger position the user dismissed with Escape; the menu stays shut until they move off it. */
2040
+ private dismissedIndex;
2041
+ private resolveToken;
2042
+ private inserting;
2043
+ private isOpen;
2044
+ private listening;
2045
+ /** Caret measurement is the expensive part of positioning, so the last one is reused. */
2046
+ private measured?;
2047
+ constructor(host: ReactiveControllerHost & HTMLElement, options: SlashMenuControllerOptions);
2048
+ /** Whether the menu is currently showing. */
2049
+ get open(): boolean;
2050
+ hostConnected(): void;
2051
+ hostDisconnected(): void;
2052
+ /** Starts watching a field. Safe to call repeatedly with the same field. */
2053
+ attach(field: TextField): void;
2054
+ /** Stops watching the current field and closes the menu. */
2055
+ detach(): void;
2056
+ private addListeners;
2057
+ private removeListeners;
2058
+ /** Closes the menu without marking the trigger as dismissed. */
2059
+ close(): void;
2060
+ /** Opens the menu at the caret, as a toolbar button or keyboard shortcut would. */
2061
+ requestOpen(): void;
2062
+ private caretRect;
2063
+ private readonly handleInput;
2064
+ private readonly handleCaretMove;
2065
+ private readonly handleKeyUp;
2066
+ private readonly handleBlur;
2067
+ private readonly handleScroll;
2068
+ private readonly handleKeyDown;
2069
+ private readonly handleItemSelect;
2070
+ private detect;
2071
+ private resolve;
2072
+ private select;
2073
+ private replace;
2074
+ }
2075
+ }
2076
+ declare module "components/slash-item/slash-item.component" {
2077
+ import ZincElement from "internal/zinc-element";
2078
+ import type { CSSResultGroup } from 'lit';
2079
+ import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
2080
+ /**
2081
+ * @summary Declares a single insertion for a slash menu. Renders nothing itself — it describes an
2082
+ * entry for the component it is slotted into, e.g. `<zn-textarea>`'s `slash-items` slot.
2083
+ * @documentation https://zinc.style/components/slash-item
2084
+ * @status experimental
2085
+ * @since 1.1
2086
+ *
2087
+ * @slot - The text to insert, for values that are long or span multiple lines. Ignored when the
2088
+ * `value` attribute is set.
2089
+ */
2090
+ export default class ZnSlashItem extends ZincElement {
2091
+ static styles: CSSResultGroup;
2092
+ /** The text shown in the menu. */
2093
+ label: string;
2094
+ /** The text inserted into the field. Falls back to this element's text content. */
2095
+ value: string;
2096
+ /** Icon shown against the item, e.g. `tag@lu`. */
2097
+ icon: string;
2098
+ /** Supporting text shown under the label. */
2099
+ description: string;
2100
+ /** Extra terms the item can be found by, comma separated. */
2101
+ keywords: string;
2102
+ /** Heading the item is listed under. */
2103
+ group: string;
2104
+ /** Overrides the item's position in the menu. Lower sorts first. */
2105
+ order: number;
2106
+ /** Where the caret lands after insertion, as an offset into the inserted value. */
2107
+ caretOffset: number;
2108
+ /** Identifier passed through on `zn-slash-select`, for items that do something other than insert. */
2109
+ action: string;
2110
+ /** Listed, but not selectable. */
2111
+ disabled: boolean;
2112
+ /** The item as the slash menu consumes it. */
2113
+ toSlashMenuItem(): SlashMenuItem;
2114
+ private get insertValue();
2115
+ render(): import("lit-html").TemplateResult<1>;
2116
+ }
2117
+ }
2118
+ declare module "components/slash-item/index" {
2119
+ import ZnSlashItem from "components/slash-item/slash-item.component";
2120
+ export * from "components/slash-item/slash-item.component";
2121
+ export default ZnSlashItem;
2122
+ global {
2123
+ interface HTMLElementTagNameMap {
2124
+ 'zn-slash-item': ZnSlashItem;
2125
+ }
2126
+ }
2127
+ }
2128
+ declare module "components/slash-menu/index" {
2129
+ import ZnSlashMenu from "components/slash-menu/slash-menu.component";
2130
+ export * from "components/slash-menu/slash-menu.component";
2131
+ export * from "components/slash-menu/slash-menu-controller";
2132
+ export * from "components/slash-menu/slash-menu-items";
2133
+ export default ZnSlashMenu;
2134
+ global {
2135
+ interface HTMLElementTagNameMap {
2136
+ 'zn-slash-menu': ZnSlashMenu;
2137
+ }
2138
+ }
2139
+ }
1860
2140
  declare module "components/input/input.component" {
2141
+ import { type SlashMenuItem } from "components/slash-menu/slash-menu-items";
1861
2142
  import ZincElement from "internal/zinc-element";
1862
2143
  import ZnIcon from "components/icon/index";
2144
+ import ZnSlashItem from "components/slash-item/index";
2145
+ import ZnSlashMenu from "components/slash-menu/index";
1863
2146
  import ZnTooltip from "components/tooltip/index";
1864
2147
  import type { ZincFormControl } from "internal/zinc-element";
1865
2148
  /**
@@ -1887,8 +2170,20 @@ declare module "components/input/input.component" {
1887
2170
  * @slot show-password-icon - An icon to use in lieu of the default show password icon.
1888
2171
  * @slot hide-password-icon - An icon to use in lieu of the default hide password icon.
1889
2172
  * @slot help-text - Text that describes how to use the input. Alternatively, you can use the `help-text` attribute.
2173
+ * @slot slash-items - `<zn-slash-item>` elements describing the insertions offered by the slash menu. Items are
2174
+ * picked up wherever they sit inside the input, so this slot name is optional.
2175
+ * @slot slash-menu - A `<zn-slash-menu>` to use instead of the built-in one, so its heading, sizing and styling can
2176
+ * be set in markup. Any `<zn-slash-item>` children of it become the menu's items.
2177
+ *
2178
+ * @dependency zn-slash-item
2179
+ * @dependency zn-slash-menu
2180
+ *
2181
+ * @event zn-slash-select - Emitted when a slash menu item is chosen. Cancelable — call `preventDefault()` to
2182
+ * suppress the insertion and handle the item yourself.
2183
+ * @event zn-slash-insert - Emitted after a slash menu item's value has been inserted.
1890
2184
  *
1891
2185
  * @csspart form-control - The form control that wraps the label, input, and help text.
2186
+ * @csspart slash-menu - The slash menu shown at the caret.
1892
2187
  * @csspart form-control-label - The label's wrapper.
1893
2188
  * @csspart form-control-input - The input's wrapper.
1894
2189
  * @csspart form-control-help-text - The help text's wrapper.
@@ -1909,14 +2204,20 @@ declare module "components/input/input.component" {
1909
2204
  static styles: import("lit").CSSResult;
1910
2205
  static dependencies: {
1911
2206
  'zn-icon': typeof ZnIcon;
2207
+ 'zn-slash-item': typeof ZnSlashItem;
2208
+ 'zn-slash-menu': typeof ZnSlashMenu;
1912
2209
  'zn-tooltip': typeof ZnTooltip;
1913
2210
  };
1914
2211
  private readonly formControlController;
1915
2212
  private readonly hasSlotController;
1916
2213
  private readonly localize;
2214
+ private readonly slashController;
1917
2215
  input: HTMLInputElement;
1918
2216
  colorPicker: HTMLInputElement;
2217
+ slashMenuElement: ZnSlashMenu | null;
1919
2218
  private hasFocus;
2219
+ /** Renders the slash menu only once it has been needed, keeping unused inputs cheap. */
2220
+ private hasSlashMenu;
1920
2221
  private isUserTyping;
1921
2222
  title: string;
1922
2223
  private __numberInput;
@@ -2019,6 +2320,23 @@ declare module "components/input/input.component" {
2019
2320
  * keyboard on supportive devices.
2020
2321
  */
2021
2322
  inputmode: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
2323
+ /**
2324
+ * Quick insertions offered by the slash menu. Accepts a JSON array of items, or the shorthand
2325
+ * `Brand name={{BRAND_NAME}}, Support email={{SUPPORT_EMAIL}}`. Can also be set as an array of
2326
+ * `SlashMenuItem` objects in JavaScript.
2327
+ */
2328
+ slashItems: SlashMenuItem[];
2329
+ /** Names of item sets registered with `registerSlashMenuPreset`, comma separated. */
2330
+ slashPreset: string;
2331
+ /** The characters that open the slash menu. */
2332
+ slashTrigger: string;
2333
+ /** The heading shown above the slash menu's items. */
2334
+ slashHeading: string;
2335
+ /**
2336
+ * Resolves additional items each time the menu opens, for lists that come from elsewhere (e.g. an
2337
+ * API). Receives the current query and may return a promise. JavaScript only.
2338
+ */
2339
+ slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
2022
2340
  /**
2023
2341
  * When enabled, pressing enter will always submit the surrounding form, even when the form uses
2024
2342
  * enter-navigation to move between fields.
@@ -2058,6 +2376,14 @@ declare module "components/input/input.component" {
2058
2376
  private handleColorPickerChange;
2059
2377
  private focusInput;
2060
2378
  protected firstUpdated(): void;
2379
+ /** The insertions the slash menu offers, gathered from every source, in the order they were declared. */
2380
+ resolveSlashItems(search?: string): Promise<SlashMenuItem[]>;
2381
+ /** Opens the slash menu at the caret, inserting the trigger if it isn't already there. */
2382
+ showSlashMenu(): Promise<void>;
2383
+ /** Closes the slash menu. */
2384
+ hideSlashMenu(): void;
2385
+ private slottedSlashItems;
2386
+ private mountSlashMenu;
2061
2387
  handleDisabledChange(): void;
2062
2388
  handleStepChange(): void;
2063
2389
  handleValueChange(): Promise<void>;
@@ -4815,6 +5141,7 @@ declare module "components/icon-picker/index" {
4815
5141
  }
4816
5142
  declare module "components/inline-edit/inline-edit.component" {
4817
5143
  import { type CSSResultGroup, type HTMLTemplateResult, type PropertyValues } from 'lit';
5144
+ import { type SlashMenuItem } from "components/slash-menu/slash-menu-items";
4818
5145
  import ZincElement from "internal/zinc-element";
4819
5146
  import ZnSelect from "components/select/index";
4820
5147
  import type { ZincFormControl } from "internal/zinc-element";
@@ -4870,6 +5197,19 @@ declare module "components/inline-edit/inline-edit.component" {
4870
5197
  step: number | 'any';
4871
5198
  inputType: 'select' | 'text' | 'data-select' | 'number' | 'textarea';
4872
5199
  textareaRows: 1;
5200
+ /**
5201
+ * Quick insertions offered by the slash menu of a `text` or `textarea` input. Accepts a JSON array of items, or
5202
+ * the shorthand `Brand name={{BRAND_NAME}}, Support email={{SUPPORT_EMAIL}}`.
5203
+ */
5204
+ slashItems: SlashMenuItem[];
5205
+ /** Names of item sets registered with `registerSlashMenuPreset`, comma separated. */
5206
+ slashPreset: string;
5207
+ /** The characters that open the slash menu. */
5208
+ slashTrigger: string;
5209
+ /** The heading shown above the slash menu's items. */
5210
+ slashHeading: string;
5211
+ /** Resolves additional slash menu items each time the menu opens. JavaScript only. */
5212
+ slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
4873
5213
  options: {
4874
5214
  [key: string]: string;
4875
5215
  };
@@ -6208,7 +6548,10 @@ declare module "components/editor/modules/ai/tooltip/ai-tooltip.component" {
6208
6548
  }
6209
6549
  declare module "components/textarea/textarea.component" {
6210
6550
  import { type CSSResultGroup } from 'lit';
6551
+ import { type SlashMenuItem } from "components/slash-menu/slash-menu-items";
6211
6552
  import ZincElement, { type ZincFormControl } from "internal/zinc-element";
6553
+ import ZnSlashItem from "components/slash-item/index";
6554
+ import ZnSlashMenu from "components/slash-menu/index";
6212
6555
  /**
6213
6556
  * @summary Textareas collect data from the user and allow multiple lines of text.
6214
6557
  * @documentation https://zinc.style/components/textarea
@@ -6219,12 +6562,22 @@ declare module "components/textarea/textarea.component" {
6219
6562
  * @slot label-tooltip - Used to add text that is displayed in a tooltip next to the label. Alternatively, you can use the `label-tooltip` attribute.
6220
6563
  * @slot context-note - Used to add contextual text that is displayed above the textarea, on the right. Alternatively, you can use the `context-note` attribute.
6221
6564
  * @slot help-text - Text that describes how to use the input. Alternatively, you can use the `help-text` attribute.
6565
+ * @slot slash-items - `<zn-slash-item>` elements describing the insertions offered by the slash menu. Items are
6566
+ * picked up wherever they sit inside the textarea, so this slot name is optional.
6567
+ * @slot slash-menu - A `<zn-slash-menu>` to use instead of the built-in one, so its heading, sizing and styling can
6568
+ * be set in markup. Any `<zn-slash-item>` children of it become the menu's items.
6569
+ *
6570
+ * @dependency zn-slash-item
6571
+ * @dependency zn-slash-menu
6222
6572
  *
6223
6573
  * @event zn-blur - Emitted when the control loses focus.
6224
6574
  * @event zn-change - Emitted when an alteration to the control's value is committed by the user.
6225
6575
  * @event zn-focus - Emitted when the control gains focus.
6226
6576
  * @event zn-input - Emitted when the control receives input.
6227
6577
  * @event zn-invalid - Emitted when the form control has been checked for validity and its constraints aren't satisfied.
6578
+ * @event zn-slash-select - Emitted when a slash menu item is chosen. Cancelable — call `preventDefault()` to
6579
+ * suppress the insertion and handle the item yourself.
6580
+ * @event zn-slash-insert - Emitted after a slash menu item's value has been inserted.
6228
6581
  *
6229
6582
  * @csspart form-control - The form control that wraps the label, input, and help text.
6230
6583
  * @csspart form-control-label - The label's wrapper.
@@ -6232,17 +6585,26 @@ declare module "components/textarea/textarea.component" {
6232
6585
  * @csspart form-control-help-text - The help text's wrapper.
6233
6586
  * @csspart base - The component's base wrapper.
6234
6587
  * @csspart textarea - The internal `<textarea>` control.
6588
+ * @csspart slash-menu - The slash menu shown at the caret.
6235
6589
  */
6236
6590
  export default class ZnTextarea extends ZincElement implements ZincFormControl {
6237
6591
  static styles: CSSResultGroup;
6592
+ static dependencies: {
6593
+ 'zn-slash-item': typeof ZnSlashItem;
6594
+ 'zn-slash-menu': typeof ZnSlashMenu;
6595
+ };
6238
6596
  private readonly formControlController;
6239
6597
  private readonly hasSlotController;
6240
6598
  private readonly resizeObserver;
6241
6599
  /** Ensures we only attempt to derive the initial value from light DOM content once */
6242
6600
  private _didInitFromContent;
6601
+ private readonly slashController;
6243
6602
  formControl: HTMLElement;
6244
6603
  input: HTMLTextAreaElement;
6604
+ slashMenuElement: ZnSlashMenu | null;
6245
6605
  hasFocus: boolean;
6606
+ /** Renders the slash menu only once it has been needed, keeping unused textareas cheap. */
6607
+ private hasSlashMenu;
6246
6608
  title: string;
6247
6609
  /** The name of the textarea, submitted as a name/value pair with form data. */
6248
6610
  name: string;
@@ -6301,6 +6663,23 @@ declare module "components/textarea/textarea.component" {
6301
6663
  * keyboard on supportive devices.
6302
6664
  */
6303
6665
  inputmode: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url';
6666
+ /**
6667
+ * Quick insertions offered by the slash menu. Accepts a JSON array of items, or the shorthand
6668
+ * `Brand name={{BRAND_NAME}}, Support email={{SUPPORT_EMAIL}}`. Can also be set as an array of
6669
+ * `SlashMenuItem` objects in JavaScript.
6670
+ */
6671
+ slashItems: SlashMenuItem[];
6672
+ /** Names of item sets registered with `registerSlashMenuPreset`, comma separated. */
6673
+ slashPreset: string;
6674
+ /** The characters that open the slash menu. */
6675
+ slashTrigger: string;
6676
+ /** The heading shown above the slash menu's items. */
6677
+ slashHeading: string;
6678
+ /**
6679
+ * Resolves additional items each time the menu opens, for lists that come from elsewhere (e.g. an
6680
+ * API). Receives the current query and may return a promise. JavaScript only.
6681
+ */
6682
+ slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
6304
6683
  /** The default value of the form control. Primarily used for resetting the form control. */
6305
6684
  defaultValue: string;
6306
6685
  transparent: boolean;
@@ -6310,6 +6689,14 @@ declare module "components/textarea/textarea.component" {
6310
6689
  get validationMessage(): string;
6311
6690
  connectedCallback(): void;
6312
6691
  firstUpdated(): void;
6692
+ /** The insertions the slash menu offers, gathered from every source, in the order they were declared. */
6693
+ resolveSlashItems(search?: string): Promise<SlashMenuItem[]>;
6694
+ /** Opens the slash menu at the caret, inserting the trigger if it isn't already there. */
6695
+ showSlashMenu(): Promise<void>;
6696
+ /** Closes the slash menu. */
6697
+ hideSlashMenu(): void;
6698
+ private slottedSlashItems;
6699
+ private mountSlashMenu;
6313
6700
  private handleBlur;
6314
6701
  private handleChange;
6315
6702
  private handleFocus;
@@ -8280,6 +8667,7 @@ declare module "components/audio-select/index" {
8280
8667
  }
8281
8668
  }
8282
8669
  declare module "components/translations/translations.component" {
8670
+ import { type SlashMenuItem } from "components/slash-menu/slash-menu-items";
8283
8671
  import ZincElement from "internal/zinc-element";
8284
8672
  import ZnButton from "components/button/index";
8285
8673
  import ZnButtonGroup from "components/button-group/index";
@@ -8309,6 +8697,19 @@ declare module "components/translations/translations.component" {
8309
8697
  flush: boolean;
8310
8698
  inputType: 'select' | 'text' | 'number' | 'textarea';
8311
8699
  textareaRows: number | undefined;
8700
+ /**
8701
+ * Quick insertions offered by the slash menu on `text` and `textarea` inputs. Accepts a JSON array of items, or
8702
+ * the shorthand `Brand name={{BRAND_NAME}}, Support email={{SUPPORT_EMAIL}}`. Every language shares the list.
8703
+ */
8704
+ slashItems: SlashMenuItem[];
8705
+ /** Names of item sets registered with `registerSlashMenuPreset`, comma separated. */
8706
+ slashPreset: string;
8707
+ /** The characters that open the slash menu. */
8708
+ slashTrigger: string;
8709
+ /** The heading shown above the slash menu's items. */
8710
+ slashHeading: string;
8711
+ /** Resolves additional slash menu items each time the menu opens. JavaScript only. */
8712
+ slashItemsProvider?: (query: string) => SlashMenuItem[] | Promise<SlashMenuItem[]>;
8312
8713
  /** When true, hides the individual language navbar and defers language control to a parent zn-translation-group. */
8313
8714
  grouped: boolean;
8314
8715
  languages: Record<string, string>;
@@ -8830,6 +9231,7 @@ declare module "components/markdown-editor/index" {
8830
9231
  declare module "components/remarkd-editor/remarkd-editor.component" {
8831
9232
  import { type CSSResultGroup, type PropertyValues } from 'lit';
8832
9233
  import ZincElement from "internal/zinc-element";
9234
+ import ZnSlashMenu from "components/slash-menu/index";
8833
9235
  import type { ZincFormControl } from "internal/zinc-element";
8834
9236
  /**
8835
9237
  * @summary A Notion-style block editor for remarkd content. Blocks render inline; click one to edit its source.
@@ -8841,35 +9243,44 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
8841
9243
  * @dependency zn-button-group
8842
9244
  * @dependency zn-icon
8843
9245
  * @dependency zn-file
9246
+ * @dependency zn-slash-menu
8844
9247
  *
8845
9248
  * @event zn-input - Emitted on each keystroke while editing a block.
8846
9249
  * @event zn-change - Emitted when a block edit is committed and the value changes.
8847
9250
  *
8848
9251
  * @csspart base - The component's base wrapper.
8849
9252
  * @csspart toolbar - The always-visible block-insert toolbar.
9253
+ * @csspart raw-toggle - The button that switches between the block view and the raw source view.
8850
9254
  * @csspart block - A rendered block wrapper.
8851
9255
  * @csspart rendered - The rendered remarkd output of a block.
8852
9256
  * @csspart input - The textarea shown while editing a block.
8853
- * @csspart slash-menu - The context menu opened by typing "/" in a block.
9257
+ * @csspart raw - The full-document textarea shown in raw source mode.
9258
+ * @csspart slash-menu - The `zn-slash-menu` opened by typing "/" in an empty block.
8854
9259
  * @csspart image-controls - The caption / alignment / size panel shown when an image block is clicked.
8855
9260
  */
8856
9261
  export default class ZnRemarkdEditor extends ZincElement implements ZincFormControl {
8857
9262
  static styles: CSSResultGroup;
9263
+ static dependencies: {
9264
+ 'zn-slash-menu': typeof ZnSlashMenu;
9265
+ };
8858
9266
  private readonly formControlController;
9267
+ private readonly slashController;
8859
9268
  private editingDraft;
9269
+ private rawEntryValue;
8860
9270
  private suppressValueSync;
8861
9271
  private suppressBlurCommit;
8862
9272
  private validationInput;
9273
+ private slashMenuElement;
8863
9274
  private blocks;
8864
9275
  private editingIndex;
8865
- private slashMenuOpen;
8866
- private slashQuery;
8867
- private slashActiveIndex;
9276
+ /** Renders the slash menu only once it has been needed. */
9277
+ private hasSlashMenu;
8868
9278
  private imagePickerIndex;
8869
9279
  private imageEdit;
8870
9280
  private dropIndicator;
8871
9281
  private dragIndex;
8872
9282
  private editShell;
9283
+ private rawMode;
8873
9284
  private pendingDragHandle;
8874
9285
  private dragStartX;
8875
9286
  private dragStartY;
@@ -8888,6 +9299,8 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
8888
9299
  * to `uploadUrl` and the returned `uploadPath` is embedded as the image URL.
8889
9300
  */
8890
9301
  attachmentUrl: string;
9302
+ /** Adds a toolbar toggle that swaps the block view for the full remarkd source. */
9303
+ allowRaw: boolean;
8891
9304
  /** Makes the editor required for form submission. */
8892
9305
  required: boolean;
8893
9306
  /** Makes the editor read-only. */
@@ -8902,7 +9315,7 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
8902
9315
  setCustomValidity(message: string): void;
8903
9316
  /** Starts editing the first block, or a new block if the document is empty. */
8904
9317
  focus(): void;
8905
- /** Commits any in-progress block edit. */
9318
+ /** Commits any in-progress block or raw edit. */
8906
9319
  blur(): void;
8907
9320
  protected firstUpdated(_changedProperties: PropertyValues): void;
8908
9321
  disconnectedCallback(): void;
@@ -8945,10 +9358,13 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
8945
9358
  private handleEditBlur;
8946
9359
  /** Commits the in-progress edit; returns the index after the committed parts. */
8947
9360
  private commitEdit;
8948
- private get filteredSlashItems();
8949
9361
  private handleDraftInput;
8950
9362
  private handleEditKeydown;
8951
- private applySlashItem;
9363
+ /** Whether the block being edited is nothing but the slash command. */
9364
+ private isSlashBlock;
9365
+ private mountSlashMenu;
9366
+ /** Returns false for items the controller should not insert text for. */
9367
+ private handleSlashSelect;
8952
9368
  private handleEditPaste;
8953
9369
  private handleDragOver;
8954
9370
  private handleDrop;
@@ -8966,11 +9382,25 @@ declare module "components/remarkd-editor/remarkd-editor.component" {
8966
9382
  private insertImage;
8967
9383
  private uploadImage;
8968
9384
  private autosize;
9385
+ private toggleRawMode;
9386
+ private focusRaw;
9387
+ /**
9388
+ * Raw mode keeps the whole document in one textarea, so the textarea — not
9389
+ * the block list — is authoritative while it is open: re-splitting on every
9390
+ * keystroke would normalise blank lines out from under the cursor.
9391
+ */
9392
+ private handleRawInput;
9393
+ /**
9394
+ * Re-splits the raw source into blocks. Not `updateBlocks` — that only
9395
+ * reports a change when the re-join differs from the value, and raw edits
9396
+ * have already written straight to the value.
9397
+ */
9398
+ private commitRaw;
8969
9399
  private handleToolbarInsert;
8970
- private renderSlashMenu;
8971
9400
  private renderImageControls;
8972
9401
  private renderBlock;
8973
9402
  render(): import("lit-html").TemplateResult<1>;
9403
+ private renderRaw;
8974
9404
  /** The block views, with the inline image picker spliced in when active. */
8975
9405
  private renderBody;
8976
9406
  private renderImagePicker;
@@ -11103,6 +11533,30 @@ declare module "events/zn-theme-submit" {
11103
11533
  }
11104
11534
  }
11105
11535
  }
11536
+ declare module "events/zn-slash-select" {
11537
+ import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
11538
+ export type ZnSlashSelectEvent = CustomEvent<{
11539
+ item: SlashMenuItem;
11540
+ query: string;
11541
+ }>;
11542
+ global {
11543
+ interface GlobalEventHandlersEventMap {
11544
+ 'zn-slash-select': ZnSlashSelectEvent;
11545
+ }
11546
+ }
11547
+ }
11548
+ declare module "events/zn-slash-insert" {
11549
+ import type { SlashMenuItem } from "components/slash-menu/slash-menu-items";
11550
+ export type ZnSlashInsertEvent = CustomEvent<{
11551
+ item: SlashMenuItem;
11552
+ value: string;
11553
+ }>;
11554
+ global {
11555
+ interface GlobalEventHandlersEventMap {
11556
+ 'zn-slash-insert': ZnSlashInsertEvent;
11557
+ }
11558
+ }
11559
+ }
11106
11560
  declare module "events/events" {
11107
11561
  export type { ZnAfterHideEvent } from "events/zn-after-hide";
11108
11562
  export type { ZnAfterShowEvent } from "events/zn-after-show";
@@ -11125,6 +11579,8 @@ declare module "events/events" {
11125
11579
  export type { ZnPageSelectionChangeEvent } from "events/zn-page-selection-change";
11126
11580
  export type { ZnThemeChangeEvent } from "events/zn-theme-change";
11127
11581
  export type { ZnThemeSubmitEvent } from "events/zn-theme-submit";
11582
+ export type { ZnSlashSelectEvent } from "events/zn-slash-select";
11583
+ export type { ZnSlashInsertEvent } from "events/zn-slash-insert";
11128
11584
  }
11129
11585
  declare module "zinc" {
11130
11586
  export { default as Button } from "components/button/index";
@@ -11240,11 +11696,14 @@ declare module "zinc" {
11240
11696
  export { default as PageSectionCard } from "components/page-builder/modules/page-section-card/index";
11241
11697
  export { default as PreviewFrame } from "components/preview-frame/index";
11242
11698
  export { default as ThemeEditor } from "components/theme-editor/index";
11699
+ export { default as SlashMenu } from "components/slash-menu/index";
11700
+ export { default as SlashItem } from "components/slash-item/index";
11243
11701
  export { default as ZincElement } from "internal/zinc-element";
11244
11702
  export * from "utilities/on";
11245
11703
  export * from "utilities/query";
11246
11704
  export * from "utilities/lit-to-html";
11247
11705
  export * from "utilities/form";
11706
+ export * from "utilities/caret-position";
11248
11707
  export * from "events/events";
11249
11708
  }
11250
11709
  declare module "components/editor/modules/events/zn-command-select" {