@jimka/typescript-ui 0.1.0 → 0.1.1

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 (123) hide show
  1. package/README.md +5 -18
  2. package/dist/lib/AbstractBooleanInput-C6CidMMX.js +2 -0
  3. package/dist/lib/AbstractBooleanInput-C6CidMMX.js.map +1 -0
  4. package/dist/lib/AbstractInput-CfjW_HM2.js +2 -0
  5. package/dist/lib/AbstractInput-CfjW_HM2.js.map +1 -0
  6. package/dist/lib/AnimatedDropdown-BFz-DO7R.js +2 -0
  7. package/dist/lib/AnimatedDropdown-BFz-DO7R.js.map +1 -0
  8. package/dist/lib/Border-5Ki2DCWz.js +2 -0
  9. package/dist/lib/Border-5Ki2DCWz.js.map +1 -0
  10. package/dist/lib/BoxLayout-8ryMD4j5.js +2 -0
  11. package/dist/lib/BoxLayout-8ryMD4j5.js.map +1 -0
  12. package/dist/lib/Button-Cdq2ySPL.js +2 -0
  13. package/dist/lib/Button-Cdq2ySPL.js.map +1 -0
  14. package/dist/lib/Card-AKIUAWYS.js +2 -0
  15. package/dist/lib/Card-AKIUAWYS.js.map +1 -0
  16. package/dist/lib/ComboBox-e_YVsWBr.js +2 -0
  17. package/dist/lib/ComboBox-e_YVsWBr.js.map +1 -0
  18. package/dist/lib/Component-Bs6TvaGZ.js +2 -0
  19. package/dist/lib/Component-Bs6TvaGZ.js.map +1 -0
  20. package/dist/lib/Container-CzyZeMyZ.js +2 -0
  21. package/dist/lib/Container-CzyZeMyZ.js.map +1 -0
  22. package/dist/lib/DOM-DeF_dWe2.js +2 -0
  23. package/dist/lib/DOM-DeF_dWe2.js.map +1 -0
  24. package/dist/lib/DragManager-c6M_mE3e.js +2 -0
  25. package/dist/lib/DragManager-c6M_mE3e.js.map +1 -0
  26. package/dist/lib/FieldDecorator-Bl0h9UEF.js +2 -0
  27. package/dist/lib/FieldDecorator-Bl0h9UEF.js.map +1 -0
  28. package/dist/lib/Fit-CUS1JXy3.js +2 -0
  29. package/dist/lib/Fit-CUS1JXy3.js.map +1 -0
  30. package/dist/lib/Glyph-6Ki3A1_a.js +2 -0
  31. package/dist/lib/Glyph-6Ki3A1_a.js.map +1 -0
  32. package/dist/lib/Grid-D8luwRPI.js +2 -0
  33. package/dist/lib/Grid-D8luwRPI.js.map +1 -0
  34. package/dist/lib/GridConstraints-CzC0w4i-.js +2 -0
  35. package/dist/lib/GridConstraints-CzC0w4i-.js.map +1 -0
  36. package/dist/lib/HBox-BWMf9h6r.js +2 -0
  37. package/dist/lib/HBox-BWMf9h6r.js.map +1 -0
  38. package/dist/lib/Header-CQjomjo0.js +2 -0
  39. package/dist/lib/Header-CQjomjo0.js.map +1 -0
  40. package/dist/lib/IconText-BXJK0MOq.js +2 -0
  41. package/dist/lib/IconText-BXJK0MOq.js.map +1 -0
  42. package/dist/lib/LayoutSerialization-C_zcwaN3.js +2 -0
  43. package/dist/lib/LayoutSerialization-C_zcwaN3.js.map +1 -0
  44. package/dist/lib/LayoutSizes-XdhpAOmc.js +2 -0
  45. package/dist/lib/LayoutSizes-XdhpAOmc.js.map +1 -0
  46. package/dist/lib/List-BgxnLWnl.js +2 -0
  47. package/dist/lib/List-BgxnLWnl.js.map +1 -0
  48. package/dist/lib/Menu-BdnwRY0M.js +2 -0
  49. package/dist/lib/Menu-BdnwRY0M.js.map +1 -0
  50. package/dist/lib/MenuButton-D15cSbMf.js +2 -0
  51. package/dist/lib/MenuButton-D15cSbMf.js.map +1 -0
  52. package/dist/lib/Panel-DApa7vA6.js +2 -0
  53. package/dist/lib/Panel-DApa7vA6.js.map +1 -0
  54. package/dist/lib/Position-DJKB0N3l.js +2 -0
  55. package/dist/lib/Position-DJKB0N3l.js.map +1 -0
  56. package/dist/lib/ProgressSpinner-CNYcsBhw.js +2 -0
  57. package/dist/lib/ProgressSpinner-CNYcsBhw.js.map +1 -0
  58. package/dist/lib/RadioButton-CepkneSA.js +2 -0
  59. package/dist/lib/RadioButton-CepkneSA.js.map +1 -0
  60. package/dist/lib/RovingTabIndex-D-zOuptw.js +2 -0
  61. package/dist/lib/RovingTabIndex-D-zOuptw.js.map +1 -0
  62. package/dist/lib/Scrollbar-DQdCdj94.js +2 -0
  63. package/dist/lib/Scrollbar-DQdCdj94.js.map +1 -0
  64. package/dist/lib/Slider-DvDdVvRm.js +2 -0
  65. package/dist/lib/Slider-DvDdVvRm.js.map +1 -0
  66. package/dist/lib/Spacer-BqiPiCqz.js +2 -0
  67. package/dist/lib/Spacer-BqiPiCqz.js.map +1 -0
  68. package/dist/lib/TabButton-DqYDfN7P.js +2 -0
  69. package/dist/lib/TabButton-DqYDfN7P.js.map +1 -0
  70. package/dist/lib/Text-DJoUHukb.js +2 -0
  71. package/dist/lib/Text-DJoUHukb.js.map +1 -0
  72. package/dist/lib/Tooltip-5BR7yYEX.js +3 -0
  73. package/dist/lib/Tooltip-5BR7yYEX.js.map +1 -0
  74. package/dist/lib/VBox-CdV9VGxR.js +2 -0
  75. package/dist/lib/VBox-CdV9VGxR.js.map +1 -0
  76. package/dist/lib/VirtualRowView-C18j90n5.js +2 -0
  77. package/dist/lib/VirtualRowView-C18j90n5.js.map +1 -0
  78. package/dist/lib/VirtualScroller-BKMvTisA.js +2 -0
  79. package/dist/lib/VirtualScroller-BKMvTisA.js.map +1 -0
  80. package/dist/lib/component/button.es.js +1 -1
  81. package/dist/lib/component/button.es.js.map +1 -1
  82. package/dist/lib/component/chart.es.js +1 -1
  83. package/dist/lib/component/chart.es.js.map +1 -1
  84. package/dist/lib/component/container.es.js +1 -1
  85. package/dist/lib/component/container.es.js.map +1 -1
  86. package/dist/lib/component/diagram.es.js +1 -1
  87. package/dist/lib/component/diagram.es.js.map +1 -1
  88. package/dist/lib/component/display.es.js +1 -1
  89. package/dist/lib/component/display.es.js.map +1 -1
  90. package/dist/lib/component/editor.es.js +3 -1
  91. package/dist/lib/component/editor.es.js.map +1 -1
  92. package/dist/lib/component/input.es.js +1 -1
  93. package/dist/lib/component/input.es.js.map +1 -1
  94. package/dist/lib/component/list.es.js +1 -1
  95. package/dist/lib/component/list.es.js.map +1 -1
  96. package/dist/lib/component/menubar.es.js +1 -1
  97. package/dist/lib/component/menubar.es.js.map +1 -1
  98. package/dist/lib/component/table.es.js +2 -2
  99. package/dist/lib/component/table.es.js.map +1 -1
  100. package/dist/lib/component/tree.es.js +1 -1
  101. package/dist/lib/component/tree.es.js.map +1 -1
  102. package/dist/lib/core.es.js +1 -1
  103. package/dist/lib/core.es.js.map +1 -1
  104. package/dist/lib/layout.es.js +1 -1
  105. package/dist/lib/overlay.es.js +1 -1
  106. package/dist/lib/overlay.es.js.map +1 -1
  107. package/dist/lib/primitive.es.js +1 -1
  108. package/dist/lib/router.es.js +2 -0
  109. package/dist/lib/router.es.js.map +1 -0
  110. package/dist/lib/types/component/container/SplitGutter.d.ts +1 -1
  111. package/dist/lib/types/component/container/WindowBorder.d.ts +1 -1
  112. package/dist/lib/types/component/display/Markdown.d.ts +10 -0
  113. package/dist/lib/types/component/display/index.d.ts +1 -1
  114. package/dist/lib/types/component/editor/MarkdownEditor.d.ts +6 -0
  115. package/dist/lib/types/component/editor/markdownTableTransformer.d.ts +2 -0
  116. package/dist/lib/types/core/DOM.d.ts +6 -0
  117. package/dist/lib/types/overlay/AbstractWindow.d.ts +1 -1
  118. package/dist/lib/types/router/RoutePattern.d.ts +18 -0
  119. package/dist/lib/types/router/Router.d.ts +36 -0
  120. package/dist/lib/types/router/index.d.ts +2 -0
  121. package/dist/lib/validation.es.js +1 -1
  122. package/llms.txt +8 -4
  123. package/package.json +6 -1
@@ -1,2 +1,2 @@
1
- import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./Position-B-WfZYAJ.js";import{t as c}from"./Placement-7kqdaK4Z.js";var Point=class extends t{_x;_y;constructor(e,t){super(),this._x=e??0,this._y=t??0}getX(){return this._x??0}getY(){return this._y??0}render(){return this.getX()+` `+this.getY()}};export{r as Insets,c as Placement,Point,s as Position,e as UNBOUNDED,a as borderSideWidth,n as borderToStyle,i as isUnbounded,o as saturate};
1
+ import{a as e,c as t,i as n,n as r,o as i,r as a,s as o,t as s}from"./Position-DJKB0N3l.js";import{t as c}from"./Placement-7kqdaK4Z.js";var Point=class extends t{_x;_y;constructor(e,t){super(),this._x=e??0,this._y=t??0}getX(){return this._x??0}getY(){return this._y??0}render(){return this.getX()+` `+this.getY()}};export{r as Insets,c as Placement,Point,s as Position,e as UNBOUNDED,a as borderSideWidth,n as borderToStyle,i as isUnbounded,o as saturate};
2
2
  //# sourceMappingURL=primitive.es.js.map
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./DOM-DeF_dWe2.js";import{t}from"./ListenerBag-WPdKuuds.js";function normalizePath(e){let t=e.startsWith(`#`)?e.slice(1):e,n=t.indexOf(`?`);return n>=0&&(t=t.slice(0,n)),`/`+splitPath(t).join(`/`)}function splitPath(e){return e.split(`/`).filter(e=>e.length>0)}function compilePattern(e){let t=normalizePath(e),n=splitPath(t),r=n.map((t,r)=>{if(t===`*`){if(r!==n.length-1)throw Error(`RoutePattern: "*" is only valid as the final segment of "${e}"`);return{kind:`catchAll`,value:``}}return t.startsWith(`:`)?{kind:`param`,value:t.slice(1)}:{kind:`static`,value:t}});return{pattern:t,key:r.map(e=>e.kind===`static`?e.value:e.kind===`param`?`:`:`*`).join(`/`),segments:r}}function matchPattern(e,t){let n=e.segments,r=n.length>0&&n.at(-1).kind===`catchAll`,i=r?n.length-1:n.length;if(r?t.length<i:t.length!==i)return null;let a={};for(let e=0;e<i;e+=1){let r=n[e],i=t[e];if(r.kind===`static`){if(r.value!==i)return null}else r.kind===`param`&&(a[r.value]=decodeSegment(i))}return a}function decodeSegment(e){try{return decodeURIComponent(e)}catch{return e}}var n={static:2,param:1,catchAll:0};function rankAt(e,t){return t>=e.length?3:n[e[t].kind]}function selectPattern(e,t){let n=splitPath(t),r=null,i=null;for(let t of e){let e=matchPattern(t,n);e!==null&&(r===null||isMoreSpecific(t.segments,r.segments))&&(r=t,i=e)}return r===null?null:{compiled:r,params:i}}function isMoreSpecific(e,t){let n=Math.max(e.length,t.length);for(let r=0;r<n;r+=1){let n=rankAt(e,r),i=rankAt(t,r);if(n!==i)return n>i}return!1}var Router=class{_routes=new Map;_listeners=new t;_started=!1;_onHashChange=()=>this.handleHashChange();constructor(e){this.applyOptions(e??{})}register(e,t){let n=compilePattern(e),r=this._routes.get(n.key);return r!==void 0&&r.pattern!==n.pattern&&console.warn(`Router: "${r.pattern}" and "${n.pattern}" have the same specificity; "${n.pattern}" replaces it.`),this._routes.set(n.key,{...n,handler:t}),this}start(){return this._started?(console.warn(`Router: start() called on an already-started router; ignoring.`),this):(this._started=!0,this.applyCurrentRoute(),e.sink.addListener(e.source.getWindow(),`hashchange`,this._onHashChange),this)}stop(){return this._started?(e.sink.removeListener(e.source.getWindow(),`hashchange`,this._onHashChange),this._started=!1,this):this}navigate(t,n){let r=`#/`+splitPath(normalizePath(t)).map(e=>encodeURIComponent(e)).join(`/`);return n?.replace===!0?e.sink.replaceLocationHash(r):e.sink.setLocationHash(r),this}getPath(){return normalizePath(e.source.getLocationHash())}on(e,t){return this._listeners.add(e,t),this}off(e,t){return this._listeners.remove(e,t),this}emit(e,t){this._listeners.fire(e,t)}applyCurrentRoute(){let e=this.getPath(),t=selectPattern(Array.from(this._routes.values()),e);if(t===null){this.emit(`nomatch`,e);return}let n={pattern:t.compiled.pattern,params:t.params,path:e};t.compiled.handler(t.params,e),this.emit(`navigate`,n)}applyOptions(e){if(e.routes!==void 0)for(let t of Object.keys(e.routes))this.register(t,e.routes[t]);if(e.listeners!==void 0)for(let t of Object.keys(e.listeners)){let n=e.listeners[t];n!==void 0&&this._listeners.add(t,n)}}handleHashChange(){this.applyCurrentRoute()}};export{Router};
2
+ //# sourceMappingURL=router.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.es.js","names":[],"sources":["../../src/typescript/lib/router/RoutePattern.ts","../../src/typescript/lib/router/Router.ts"],"sourcesContent":["// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\n// Pure route-pattern mechanics behind Router: normalizing a hash/path,\n// compiling a registered pattern into segments + an ambiguity key, matching a\n// path against a compiled pattern, and picking the most specific match. No\n// DOM seam involved — internal to router/, not exported from the barrel.\n\n/** The kind of a single pattern segment. */\nexport type SegmentKind = \"static\" | \"param\" | \"catchAll\";\n\n/** One segment of a compiled pattern. */\nexport interface RouteSegment {\n kind: SegmentKind;\n /** Literal text for \"static\", the parameter name for \"param\", \"\" for \"catchAll\". */\n value: string;\n}\n\n/** A pattern parsed into segments, plus its ambiguity key. */\nexport interface CompiledPattern {\n /** The pattern string as registered, after normalization (e.g. \"/data/:id\"). */\n pattern: string;\n /** Ambiguity key: static segments by literal, \"param\" as \":\", \"catchAll\" as \"*\". */\n key: string;\n segments: RouteSegment[];\n}\n\n/**\n * Normalizes a hash or path to a leading-slash, no-trailing-slash path: strips\n * a leading `\"#\"`, discards everything from the first `\"?\"` onward, and\n * collapses empty segments (so a doubled `\"//\"` and a trailing `\"/\"` both\n * disappear). `\"\"`, `\"#\"`, and `\"#/\"` all normalize to `\"/\"`.\n *\n * @param input - The raw hash (with or without its leading `\"#\"`) or path.\n * @returns The normalized path.\n */\nexport function normalizePath(input: string): string {\n let path = input.startsWith(\"#\") ? input.slice(1) : input;\n const queryIndex = path.indexOf(\"?\");\n\n if (queryIndex >= 0) {\n path = path.slice(0, queryIndex);\n }\n\n return \"/\" + splitPath(path).join(\"/\");\n}\n\n/**\n * Splits a normalized path into its non-empty segments.\n *\n * @param path - The path to split.\n * @returns The segments, in order.\n */\nexport function splitPath(path: string): string[] {\n return path.split(\"/\").filter((segment) => segment.length > 0);\n}\n\n/**\n * Parses a registered pattern into {@link RouteSegment}s and derives its\n * ambiguity key.\n *\n * @param pattern - The pattern string, e.g. `\"/data/rows/:sel\"`.\n * @returns The compiled pattern.\n * @throws If `\"*\"` appears anywhere but the final segment.\n */\nexport function compilePattern(pattern: string): CompiledPattern {\n const normalized = normalizePath(pattern);\n const rawSegments = splitPath(normalized);\n\n const segments: RouteSegment[] = rawSegments.map((raw, i) => {\n if (raw === \"*\") {\n if (i !== rawSegments.length - 1) {\n throw new Error(`RoutePattern: \"*\" is only valid as the final segment of \"${pattern}\"`);\n }\n\n return { kind: \"catchAll\", value: \"\" };\n }\n\n if (raw.startsWith(\":\")) {\n return { kind: \"param\", value: raw.slice(1) };\n }\n\n return { kind: \"static\", value: raw };\n });\n\n const key = segments\n .map((segment) => segment.kind === \"static\" ? segment.value : segment.kind === \"param\" ? \":\" : \"*\")\n .join(\"/\");\n\n return { pattern: normalized, key, segments };\n}\n\n/**\n * Returns the extracted params when `segments` match `compiled`, or `null`\n * when they don't. A trailing `catchAll` segment matches zero or more\n * remaining segments; every other kind requires an exact segment count.\n * Captured `:param` values run through `decodeURIComponent`, falling back to\n * the raw segment text on a malformed escape.\n *\n * @param compiled - The compiled pattern to match against.\n * @param segments - The candidate path's segments (from {@link splitPath}).\n * @returns The extracted params, or `null` when there is no match.\n */\nexport function matchPattern(compiled: CompiledPattern, segments: string[]): Record<string, string> | null {\n const patternSegments = compiled.segments;\n const isCatchAll = patternSegments.length > 0 && patternSegments.at(-1)!.kind === \"catchAll\";\n const staticLength = isCatchAll ? patternSegments.length - 1 : patternSegments.length;\n\n if (isCatchAll ? segments.length < staticLength : segments.length !== staticLength) {\n return null;\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < staticLength; i += 1) {\n const patternSegment = patternSegments[i];\n const value = segments[i];\n\n if (patternSegment.kind === \"static\") {\n if (patternSegment.value !== value) {\n return null;\n }\n } else if (patternSegment.kind === \"param\") {\n params[patternSegment.value] = decodeSegment(value);\n }\n }\n\n return params;\n}\n\n/**\n * Decodes a captured path segment, falling back to the raw text when it\n * carries a malformed percent-escape.\n *\n * @param value - The raw segment text.\n * @returns The decoded value, or `value` unchanged on a decode failure.\n */\nfunction decodeSegment(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/** Rank of each real segment kind: higher wins a specificity tie-break. */\nconst KIND_RANK: Record<SegmentKind, number> = { static: 2, param: 1, catchAll: 0 };\n\n/**\n * Rank of position `i` in `segments`. Reaching the end of the pattern (`i` at\n * or past its length) outranks every real segment kind — see\n * {@link selectPattern}.\n *\n * @param segments - The pattern's segments.\n * @param i - The position to rank.\n * @returns The rank at that position.\n */\nfunction rankAt(segments: readonly RouteSegment[], i: number): number {\n return i >= segments.length ? 3 : KIND_RANK[segments[i].kind];\n}\n\n/**\n * Returns the highest-ranked pattern in `compiled` that matches `path`, or\n * `null` when none match. Patterns are compared segment by segment from the\n * left via {@link rankAt}; the first position where two matching patterns\n * differ decides the winner. Two patterns can never tie on every position —\n * that would mean identical ambiguity keys, which the route table (see\n * `Router`) forbids. Registration order never affects the result.\n *\n * @param compiled - The candidate compiled patterns.\n * @param path - The normalized path to match.\n * @returns The winning pattern and its extracted params, or `null`.\n */\nexport function selectPattern<T extends CompiledPattern>(\n compiled: readonly T[],\n path: string,\n): { compiled: T; params: Record<string, string> } | null {\n const segments = splitPath(path);\n\n let best: T | null = null;\n let bestParams: Record<string, string> | null = null;\n\n for (const candidate of compiled) {\n const params = matchPattern(candidate, segments);\n\n if (params === null) {\n continue;\n }\n\n if (best === null || isMoreSpecific(candidate.segments, best.segments)) {\n best = candidate;\n bestParams = params;\n }\n }\n\n return best === null ? null : { compiled: best, params: bestParams! };\n}\n\n/**\n * Whether `a` outranks `b` by {@link rankAt} at the first position where they\n * differ.\n *\n * @param a - The candidate pattern's segments.\n * @param b - The current best pattern's segments.\n * @returns `true` when `a` is more specific than `b`.\n */\nfunction isMoreSpecific(a: readonly RouteSegment[], b: readonly RouteSegment[]): boolean {\n const length = Math.max(a.length, b.length);\n\n for (let i = 0; i < length; i += 1) {\n const rankA = rankAt(a, i);\n const rankB = rankAt(b, i);\n\n if (rankA !== rankB) {\n return rankA > rankB;\n }\n }\n\n return false;\n}\n","// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0\n\nimport { DOM } from '~/core/DOM.js';\nimport { ListenerBag } from '~/core/ListenerBag.js';\nimport { compilePattern, normalizePath, splitPath, selectPattern, type CompiledPattern } from '~/router/RoutePattern.js';\n\n/** Params extracted from a matched pattern's `:name` segments. */\nexport type RouteParams = Record<string, string>;\n\n/** A registered route's callback: drives whatever it fronts, never builds it. */\nexport type RouteHandler = (params: RouteParams, path: string) => void;\n\n/** The events a {@link Router} emits. */\nexport type RouterEvent = \"navigate\" | \"nomatch\";\n\n/** The pattern and params a navigation resolved to. */\nexport interface RouteMatch {\n /** The pattern that won, as registered. */\n pattern: string;\n params: RouteParams;\n /** The normalized path that was matched. */\n path: string;\n}\n\n/** Construction options for {@link Router}. */\nexport interface RouterOptions {\n /** Patterns to `register` at construction, keyed by pattern string. */\n routes?: Record<string, RouteHandler>;\n listeners?: Partial<{\n navigate: (match: RouteMatch) => void;\n nomatch: (path: string) => void;\n }>;\n}\n\n/** A registered route: its compiled pattern plus the handler it drives. */\ntype CompiledRoute = CompiledPattern & { handler: RouteHandler };\n\n/**\n * Maps the URL hash to a single top-level app section. Patterns are\n * registered with {@link register} (e.g. `\"/data/rows/:sel\"`); on a\n * navigation, the router selects the most specific matching pattern, extracts\n * its `:param` values, and calls that pattern's handler — the handler drives\n * components that already exist, it never builds them.\n *\n * {@link start} reads the current hash, applies the matching route\n * synchronously, then installs the `hashchange` listener; call it once the\n * app has built its component tree and before the first layout pass runs, so\n * the routed section is already selected when that pass runs. {@link stop}\n * removes the listener.\n *\n * It is a plain class (no DOM element, so not `callable()`-wrapped) — it\n * follows the shape of a data store: an options bag, a private listener bag,\n * and typed `on` / `off` / `emit` forwarders.\n *\n * @category Core\n */\nexport class Router {\n\n private _routes: Map<string, CompiledRoute> = new Map();\n private _listeners: ListenerBag<RouterEvent> = new ListenerBag<RouterEvent>();\n private _started: boolean = false;\n\n // Stable reference so add/remove pair up; delegates to a named method.\n private readonly _onHashChange: () => void = () => this.handleHashChange();\n\n /**\n * @param options - Routes and listeners to register at construction.\n */\n constructor(options?: RouterOptions) {\n this.applyOptions(options ?? {});\n }\n\n /**\n * Registers a pattern's handler. Re-registering the exact same pattern\n * string replaces its handler silently. Registering a *different*\n * pattern string that collides with an already-registered one on\n * specificity (e.g. `\"/users/:id\"` and `\"/users/:name\"`) replaces it too,\n * but logs a warning naming both patterns — that collision usually\n * signals an authoring mistake.\n *\n * @param pattern - The route pattern, e.g. `\"/data/rows/:sel\"`.\n * @param handler - Called with the extracted params and the normalized\n * path when this pattern wins a navigation.\n * @returns This router, for chaining.\n */\n register(pattern: string, handler: RouteHandler): this {\n const compiled = compilePattern(pattern);\n const existing = this._routes.get(compiled.key);\n\n if (existing !== undefined && existing.pattern !== compiled.pattern) {\n console.warn(`Router: \"${existing.pattern}\" and \"${compiled.pattern}\" have the same specificity; \"${compiled.pattern}\" replaces it.`);\n }\n\n this._routes.set(compiled.key, { ...compiled, handler });\n\n return this;\n }\n\n /**\n * Reads the current hash, applies the matching route synchronously, then\n * installs the `hashchange` listener. Calling `start()` again on an\n * already-started router logs a warning and is otherwise a no-op — it\n * does not install a second listener.\n *\n * @returns This router, for chaining.\n */\n start(): this {\n if (this._started) {\n console.warn(`Router: start() called on an already-started router; ignoring.`);\n\n return this;\n }\n\n this._started = true;\n this.applyCurrentRoute();\n DOM.sink.addListener(DOM.source.getWindow(), \"hashchange\", this._onHashChange);\n\n return this;\n }\n\n /**\n * Removes the `hashchange` listener. Safe to call when never started, or\n * more than once.\n *\n * @returns This router, for chaining.\n */\n stop(): this {\n if (!this._started) {\n return this;\n }\n\n DOM.sink.removeListener(DOM.source.getWindow(), \"hashchange\", this._onHashChange);\n this._started = false;\n\n return this;\n }\n\n /**\n * Writes `path` into the hash — pushing a history entry, or replacing the\n * current one when `options.replace` is `true`. Each segment is\n * percent-encoded. Writing the path already in the hash is a same-value\n * write: it fires no `hashchange` and re-runs no handler.\n *\n * @param path - The path to navigate to, e.g. `\"/settings\"`.\n * @param options - `replace` to replace the current history entry instead\n * of pushing a new one.\n * @returns This router, for chaining.\n */\n navigate(path: string, options?: { replace?: boolean }): this {\n const segments = splitPath(normalizePath(path));\n const hash = \"#/\" + segments.map((segment) => encodeURIComponent(segment)).join(\"/\");\n\n if (options?.replace === true) {\n DOM.sink.replaceLocationHash(hash);\n } else {\n DOM.sink.setLocationHash(hash);\n }\n\n return this;\n }\n\n /**\n * The normalized path currently in the hash.\n *\n * @returns The current path.\n */\n getPath(): string {\n return normalizePath(DOM.source.getLocationHash());\n }\n\n on(event: \"navigate\", listener: (match: RouteMatch) => void): this;\n on(event: \"nomatch\", listener: (path: string) => void): this;\n\n /**\n * Subscribes a listener to a router event. Listeners fire in\n * registration order.\n *\n * @param event - The event to listen for.\n * @param listener - The callback to invoke.\n * @returns This router, for chaining.\n */\n on(event: RouterEvent, listener: Function): this {\n this._listeners.add(event, listener);\n\n return this;\n }\n\n /**\n * Removes a previously registered listener. No-op if it was never\n * registered for that event.\n *\n * @param event - The event the listener was registered for.\n * @param listener - The exact callback reference to remove.\n * @returns This router, for chaining.\n */\n off(event: RouterEvent, listener: Function): this {\n this._listeners.remove(event, listener);\n\n return this;\n }\n\n /**\n * Notifies all listeners registered for an event, in registration order.\n *\n * @param event - The event to emit.\n * @param payload - The value passed to each listener.\n */\n protected emit(event: RouterEvent, payload: unknown): void {\n this._listeners.fire(event, payload);\n }\n\n /**\n * Reads the current hash and calls the most specific matching pattern's\n * handler, emitting `\"navigate\"` — or emits `\"nomatch\"` when nothing\n * matches. Shared by {@link start} and the `hashchange` listener.\n */\n private applyCurrentRoute(): void {\n const path = this.getPath();\n const result = selectPattern(Array.from(this._routes.values()), path);\n\n if (result === null) {\n this.emit(\"nomatch\", path);\n\n return;\n }\n\n const match: RouteMatch = { pattern: result.compiled.pattern, params: result.params, path };\n\n result.compiled.handler(result.params, path);\n this.emit(\"navigate\", match);\n }\n\n /**\n * Applies an {@link RouterOptions} bag: registers `routes`, then wires\n * `listeners`. Called from the constructor body, after every field\n * initializer has run.\n *\n * @param options - The options bag to apply.\n */\n protected applyOptions(options: RouterOptions): void {\n if (options.routes !== undefined) {\n for (const pattern of Object.keys(options.routes)) {\n this.register(pattern, options.routes[pattern]);\n }\n }\n\n if (options.listeners !== undefined) {\n for (const event of Object.keys(options.listeners) as RouterEvent[]) {\n const listener = options.listeners[event];\n\n if (listener !== undefined) {\n this._listeners.add(event, listener);\n }\n }\n }\n }\n\n private handleHashChange(): void {\n this.applyCurrentRoute();\n }\n}\n"],"mappings":"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"}
@@ -52,7 +52,7 @@ declare class SplitGutter extends Component<SplitGutterOptions> {
52
52
  destroy(): void;
53
53
  private _dispatchDrag;
54
54
  onDragStart(evnt: MouseEvent): void;
55
- onDragStop(): void;
55
+ onDragStop(e?: Event): void;
56
56
  onDrag(evnt: MouseEvent): void;
57
57
  private dragCursor;
58
58
  private applyCursor;
@@ -34,7 +34,7 @@ declare class WindowBorder extends Component<WindowBorderOptions> {
34
34
  isSnapTarget(): boolean;
35
35
  setSnapTarget(value: boolean): this;
36
36
  onDragStart(): void;
37
- onDragStop(): void;
37
+ onDragStop(e?: Event): void;
38
38
  private dragCursor;
39
39
  render(): import("../../core/DOM.js").Handle;
40
40
  }
@@ -1,8 +1,14 @@
1
1
  import { Component, ComponentOptions } from "../../core/Component.js";
2
2
  import type { Handle } from "../../core/DOM.js";
3
3
  import { Size } from "../../primitive/Size.js";
4
+ export interface MarkdownLinkResolution {
5
+ href: string;
6
+ external: boolean;
7
+ }
8
+ export type MarkdownLinkResolver = (href: string) => MarkdownLinkResolution;
4
9
  export interface MarkdownOptions extends ComponentOptions {
5
10
  markdown?: string;
11
+ linkResolver?: MarkdownLinkResolver;
6
12
  }
7
13
  declare class Markdown extends Component<MarkdownOptions> {
8
14
  private _contentHandles;
@@ -11,6 +17,8 @@ declare class Markdown extends Component<MarkdownOptions> {
11
17
  constructor(markdown?: string, options?: MarkdownOptions);
12
18
  protected applyOptions(options: MarkdownOptions): this;
13
19
  getMarkdown(): string;
20
+ setLinkResolver(resolver: MarkdownLinkResolver): this;
21
+ getLinkResolver(): MarkdownLinkResolver;
14
22
  setMarkdown(markdown: string): this;
15
23
  getMinSize(): Size | null;
16
24
  getPreferredSize(): Size | null;
@@ -26,6 +34,8 @@ declare class Markdown extends Component<MarkdownOptions> {
26
34
  private appendParagraph;
27
35
  private appendList;
28
36
  private appendListItem;
37
+ private appendTable;
38
+ private appendTableRow;
29
39
  private appendBlockquote;
30
40
  private appendCode;
31
41
  private appendInlineTokens;
@@ -12,7 +12,7 @@ export type { GlyphDef, NamedGlyphDef } from '../../component/display/Glyphs.js'
12
12
  export { IconText } from '../../component/display/IconText.js';
13
13
  export type { IconTextOptions } from '../../component/display/IconText.js';
14
14
  export { Markdown } from '../../component/display/Markdown.js';
15
- export type { MarkdownOptions } from '../../component/display/Markdown.js';
15
+ export type { MarkdownOptions, MarkdownLinkResolution, MarkdownLinkResolver } from '../../component/display/Markdown.js';
16
16
  export { IconLabel } from '../../component/display/IconLabel.js';
17
17
  export type { IconLabelOptions } from '../../component/display/IconLabel.js';
18
18
  export { ProgressBar } from '../../component/display/ProgressBar.js';
@@ -16,6 +16,7 @@ export interface MarkdownEditorOptions extends ComponentOptions {
16
16
  declare class MarkdownEditor extends Component<MarkdownEditorOptions> {
17
17
  private _editor;
18
18
  private _unregister;
19
+ private _unregisterTableView;
19
20
  private readonly _listeners;
20
21
  private readonly _card;
21
22
  private readonly _wysiwyg;
@@ -36,6 +37,11 @@ declare class MarkdownEditor extends Component<MarkdownEditorOptions> {
36
37
  toggleOrderedList(): this;
37
38
  toggleLink(url: string | null): this;
38
39
  setBlockType(type: MarkdownBlockType): this;
40
+ insertTable(rows: number, columns: number): this;
41
+ insertTableRow(after?: boolean): this;
42
+ deleteTableRow(): this;
43
+ insertTableColumn(after?: boolean): this;
44
+ deleteTableColumn(): this;
39
45
  on(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this;
40
46
  off(event: MarkdownEditorEvent, listener: (payload: MarkdownEditorChange) => void): this;
41
47
  protected emit(event: MarkdownEditorEvent, payload: MarkdownEditorChange): void;
@@ -0,0 +1,2 @@
1
+ import type { MultilineElementTransformer, Transformer } from "@lexical/markdown";
2
+ export declare function createTableTransformer(getTransformers: () => Transformer[]): MultilineElementTransformer;
@@ -78,6 +78,8 @@ export interface DOMSink {
78
78
  blur(handle: Handle): void;
79
79
  setValue(handle: Handle, value: string): void;
80
80
  setSelectionRange(handle: Handle, start: number, end: number): void;
81
+ setLocationHash(hash: string): void;
82
+ replaceLocationHash(hash: string): void;
81
83
  addListener<T extends Event = Event>(target: Handle, type: string, handler: (event: T) => void, options?: boolean | AddEventListenerOptions): void;
82
84
  removeListener<T extends Event = Event>(target: Handle, type: string, handler: (event: T) => void, options?: boolean | EventListenerOptions): void;
83
85
  dispatchEvent(target: Handle, event: Event): void;
@@ -130,6 +132,7 @@ export interface DOMSource {
130
132
  matchMedia(query: string): MediaQueryResult;
131
133
  isWindow(target: Handle | null): boolean;
132
134
  getWindow(): Handle;
135
+ getLocationHash(): string;
133
136
  contains(ancestor: Handle, node: Handle | null): boolean;
134
137
  querySelector(root: Handle, selector: string): Handle | null;
135
138
  querySelectorAll(root: Handle, selector: string): Handle[];
@@ -194,6 +197,8 @@ export declare class ProductionDOMSink implements DOMSink {
194
197
  blur(handle: Handle): void;
195
198
  setValue(handle: Handle, value: string): void;
196
199
  setSelectionRange(handle: Handle, start: number, end: number): void;
200
+ setLocationHash(hash: string): void;
201
+ replaceLocationHash(hash: string): void;
197
202
  addListener<T extends Event = Event>(target: Handle, type: string, handler: (event: T) => void, options?: boolean | AddEventListenerOptions): void;
198
203
  removeListener<T extends Event = Event>(target: Handle, type: string, handler: (event: T) => void, options?: boolean | EventListenerOptions): void;
199
204
  dispatchEvent(target: Handle, event: Event): void;
@@ -246,6 +251,7 @@ export declare class ProductionDOMSource implements DOMSource {
246
251
  matchMedia(query: string): MediaQueryResult;
247
252
  isWindow(target: Handle | null): boolean;
248
253
  getWindow(): Handle;
254
+ getLocationHash(): string;
249
255
  contains(ancestor: Handle, node: Handle | null): boolean;
250
256
  querySelector(root: Handle, selector: string): Handle | null;
251
257
  querySelectorAll(root: Handle, selector: string): Handle[];
@@ -158,7 +158,7 @@ export declare abstract class AbstractWindow extends Container<WindowOptions> im
158
158
  private clampDragDelta;
159
159
  clampPositionToViewport(): this;
160
160
  onDrag(e: MouseEvent): void;
161
- onMouseUp(): void;
161
+ onMouseUp(e?: Event): void;
162
162
  doLayout(): this;
163
163
  render(): Handle;
164
164
  private currentRect;
@@ -0,0 +1,18 @@
1
+ export type SegmentKind = "static" | "param" | "catchAll";
2
+ export interface RouteSegment {
3
+ kind: SegmentKind;
4
+ value: string;
5
+ }
6
+ export interface CompiledPattern {
7
+ pattern: string;
8
+ key: string;
9
+ segments: RouteSegment[];
10
+ }
11
+ export declare function normalizePath(input: string): string;
12
+ export declare function splitPath(path: string): string[];
13
+ export declare function compilePattern(pattern: string): CompiledPattern;
14
+ export declare function matchPattern(compiled: CompiledPattern, segments: string[]): Record<string, string> | null;
15
+ export declare function selectPattern<T extends CompiledPattern>(compiled: readonly T[], path: string): {
16
+ compiled: T;
17
+ params: Record<string, string>;
18
+ } | null;
@@ -0,0 +1,36 @@
1
+ export type RouteParams = Record<string, string>;
2
+ export type RouteHandler = (params: RouteParams, path: string) => void;
3
+ export type RouterEvent = "navigate" | "nomatch";
4
+ export interface RouteMatch {
5
+ pattern: string;
6
+ params: RouteParams;
7
+ path: string;
8
+ }
9
+ export interface RouterOptions {
10
+ routes?: Record<string, RouteHandler>;
11
+ listeners?: Partial<{
12
+ navigate: (match: RouteMatch) => void;
13
+ nomatch: (path: string) => void;
14
+ }>;
15
+ }
16
+ export declare class Router {
17
+ private _routes;
18
+ private _listeners;
19
+ private _started;
20
+ private readonly _onHashChange;
21
+ constructor(options?: RouterOptions);
22
+ register(pattern: string, handler: RouteHandler): this;
23
+ start(): this;
24
+ stop(): this;
25
+ navigate(path: string, options?: {
26
+ replace?: boolean;
27
+ }): this;
28
+ getPath(): string;
29
+ on(event: "navigate", listener: (match: RouteMatch) => void): this;
30
+ on(event: "nomatch", listener: (path: string) => void): this;
31
+ off(event: RouterEvent, listener: Function): this;
32
+ protected emit(event: RouterEvent, payload: unknown): void;
33
+ private applyCurrentRoute;
34
+ protected applyOptions(options: RouterOptions): void;
35
+ private handleHashChange;
36
+ }
@@ -0,0 +1,2 @@
1
+ export { Router } from '../router/Router.js';
2
+ export type { RouteParams, RouteHandler, RouterEvent, RouteMatch, RouterOptions } from '../router/Router.js';
@@ -1 +1 @@
1
- import{t as e}from"./FieldDecorator-E7WzutjO.js";export{e as FieldDecorator};
1
+ import{t as e}from"./FieldDecorator-Bl0h9UEF.js";export{e as FieldDecorator};
package/llms.txt CHANGED
@@ -99,14 +99,18 @@ Organized by task. Columns: task → symbol · import subpath · summary · docs
99
99
  - Node in a tree store → **TreeNode** · `@jimka/typescript-ui/data` · A node in a TreeStore's hierarchy: a thin structural wrapper around a ModelRecord, carrying parent/child links, depth, and leaf state. The… · docs/data/store.md
100
100
  - Relate models (has-many / belongs-to) → **Association** · `@jimka/typescript-ui/data` · Declarative association descriptor; behaviour lives in AbstractModel / ModelRecord / AbstractStore. · docs/data/associations.md
101
101
 
102
+ ### App shell
103
+ - Map the URL hash to a top-level app section → **Router** · `@jimka/typescript-ui/router` · Maps the URL hash to a single top-level app section. Patterns are registered with register (e.g. `"/data/rows/:sel"`); on a navigation, the… · docs/concepts/routing.md
104
+
102
105
  ## Conventions (hard rules)
103
106
  1. Never use CSS flex/grid or `position: relative` — arrange children with a LayoutManager (HBox/VBox/Border/Grid/Split/…). → docs/concepts/layout-system.md
104
107
  2. Size with setMinSize/setPreferredSize/setMaxSize honouring min ≤ preferred ≤ max; to scroll, wrap content in a Panel with autoScroll — never `overflow` CSS. → docs/concepts/sizing.md
105
108
  3. Construct with the callable + options-bag idiom (`Button({ text })`), not post-construction setters; reserve `setX` for runtime changes. → docs/recipes/component-options.md
106
- 4. Route DOM events through the `Event` class (`Event.addListener`); emit and observe semantic events with `on`/`off`/`emit`. Never call `addEventListener`. → docs/concepts/events.md
107
- 5. Theme through design tokens, never hardcoded colours. → docs/concepts/theming.md
108
- 6. Move data through Model/Store/Binding, not manual DOM writes — bind components to records instead of pushing values by hand. → docs/concepts/data-binding.md
109
- 7. One DOM element per component class; touch the element only at render time, not from a setter dispatched during construction. → docs/concepts/component-lifecycle.md
109
+ 4. Mount the top-level layout with `Body.init({ layoutManager, components })` one call; use `Body.getInstance()` only to reach the body afterwards. → docs/components/Body.md
110
+ 5. Route DOM events through the `Event` class (`Event.addListener`); emit and observe semantic events with `on`/`off`/`emit`. Never call `addEventListener`. → docs/concepts/events.md
111
+ 6. Theme through design tokens, never hardcoded colours. → docs/concepts/theming.md
112
+ 7. Move data through Model/Store/Binding, not manual DOM writes bind components to records instead of pushing values by hand. → docs/concepts/data-binding.md
113
+ 8. One DOM element per component class; touch the element only at render time, not from a setter dispatched during construction. → docs/concepts/component-lifecycle.md
110
114
 
111
115
  ## Drill down
112
116
  - Component & layout detail (options, examples) → docs/components/, docs/layouts/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jimka/typescript-ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A web-based layout manager and UI component framework written in TypeScript.",
5
5
  "keywords": ["ui", "ui-components", "typescript", "layout", "layout-manager", "widgets", "framework", "dock", "data-table", "retained-mode"],
6
6
  "author": "Jimmy Karlsson",
@@ -41,6 +41,10 @@
41
41
  "import": "./dist/lib/validation.es.js",
42
42
  "types": "./dist/lib/types/validation/index.d.ts"
43
43
  },
44
+ "./router": {
45
+ "import": "./dist/lib/router.es.js",
46
+ "types": "./dist/lib/types/router/index.d.ts"
47
+ },
44
48
  "./component/input": {
45
49
  "import": "./dist/lib/component/input.es.js",
46
50
  "types": "./dist/lib/types/component/input/index.d.ts"
@@ -188,6 +192,7 @@
188
192
  "@lexical/markdown": "^0.46.0",
189
193
  "@lexical/rich-text": "^0.46.0",
190
194
  "@lexical/selection": "^0.46.0",
195
+ "@lexical/table": "^0.46.0",
191
196
  "@lexical/utils": "^0.46.0",
192
197
  "@lezer/highlight": "^1.2.3",
193
198
  "codemirror": "^6.0.2",