@holoviz/panel 1.4.2 → 1.5.0-a.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 (40) hide show
  1. package/dist/bundled/notificationarea/panel/1.5.0-a.1/dist/bundled/font-awesome/css/all.min.css +2 -0
  2. package/dist/bundled/panel/1.5.0-a.1/dist/bundled/bootstrap5/css/bootstrap.min.css +2 -0
  3. package/dist/bundled/panel/1.5.0-a.1/dist/bundled/bootstrap5/js/bootstrap.bundle.min.js +2 -0
  4. package/dist/bundled/panel/1.5.0-a.1/dist/bundled/font-awesome/css/all.min.css +2 -0
  5. package/dist/bundled/panel/1.5.0-a.1/dist/bundled/jquery/jquery.slim.min.js +2 -0
  6. package/dist/bundled/plotlyplot/panel/1.5.0-a.1/dist/bundled/jquery/jquery.slim.min.js +2 -0
  7. package/dist/bundled/theme/fast.css +0 -1
  8. package/dist/css/models/card.css +11 -0
  9. package/dist/lib/models/comm_manager.d.ts +1 -1
  10. package/dist/lib/models/comm_manager.js +16 -18
  11. package/dist/lib/models/comm_manager.js.map +1 -1
  12. package/dist/lib/models/html.d.ts +7 -7
  13. package/dist/lib/models/html.js +28 -25
  14. package/dist/lib/models/html.js.map +1 -1
  15. package/dist/lib/models/index.d.ts +1 -0
  16. package/dist/lib/models/index.js +1 -0
  17. package/dist/lib/models/index.js.map +1 -1
  18. package/dist/lib/models/layout.d.ts +5 -4
  19. package/dist/lib/models/layout.js +41 -36
  20. package/dist/lib/models/layout.js.map +1 -1
  21. package/dist/lib/models/pdf.js +2 -2
  22. package/dist/lib/models/pdf.js.map +1 -1
  23. package/dist/lib/models/reactive_html.d.ts +18 -17
  24. package/dist/lib/models/reactive_html.js +159 -138
  25. package/dist/lib/models/reactive_html.js.map +1 -1
  26. package/dist/lib/models/text_input.d.ts +25 -0
  27. package/dist/lib/models/text_input.js +38 -0
  28. package/dist/lib/models/text_input.js.map +1 -0
  29. package/dist/lib/styles/models/card.css.js +1 -1
  30. package/dist/panel.js +335 -261
  31. package/dist/panel.js.map +1 -1
  32. package/dist/panel.json +1 -1
  33. package/dist/panel.min.js +33 -32
  34. package/package.json +1 -1
  35. package/dist/bundled/notificationarea/panel/1.4.2/dist/bundled/font-awesome/css/all.min.css +0 -2
  36. package/dist/bundled/panel/1.4.2/dist/bundled/bootstrap5/css/bootstrap.min.css +0 -2
  37. package/dist/bundled/panel/1.4.2/dist/bundled/bootstrap5/js/bootstrap.bundle.min.js +0 -2
  38. package/dist/bundled/panel/1.4.2/dist/bundled/font-awesome/css/all.min.css +0 -2
  39. package/dist/bundled/panel/1.4.2/dist/bundled/jquery/jquery.slim.min.js +0 -2
  40. package/dist/bundled/plotlyplot/panel/1.4.2/dist/bundled/jquery/jquery.slim.min.js +0 -2
@@ -1,6 +1,6 @@
1
1
  import { Markup } from "@bokehjs/models/widgets/markup";
2
2
  import { PanelMarkupView } from "./layout";
3
- import { htmlDecode } from "./html";
3
+ import { html_decode } from "./html";
4
4
  export class PDFView extends PanelMarkupView {
5
5
  static __name__ = "PDFView";
6
6
  connect_signals() {
@@ -19,7 +19,7 @@ export class PDFView extends PanelMarkupView {
19
19
  this.container.innerHTML = `<embed src="${url}#page=${this.model.start_page}" type="application/pdf" width="100%" height="100%"></embed>`;
20
20
  }
21
21
  else {
22
- const html = htmlDecode(this.model.text);
22
+ const html = html_decode(this.model.text);
23
23
  this.container.innerHTML = html || "";
24
24
  }
25
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../models/pdf.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAA;AACrD,OAAO,EAAC,eAAe,EAAC,MAAM,UAAU,CAAA;AACxC,OAAO,EAAC,UAAU,EAAC,MAAM,QAAQ,CAAA;AAEjC,MAAM,OAAO,OAAQ,SAAQ,eAAe;;IAGjC,eAAe;QACtB,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,MAAM,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAC,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAA;QACtE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;IACnF,CAAC;IAEQ,MAAM;QACb,KAAK,CAAC,MAAM,EAAE,CAAA;QACd,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAA;YAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;YACrC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,eAAe,GAAG,SAAS,IAAI,CAAC,KAAK,CAAC,UAAU,8DAA8D,CAAA;QAC3I,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACxC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;QACvC,CAAC;IACH,CAAC;IAES,sBAAsB;QAC9B,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC7C,MAAM,UAAU,GAAG,GAAG,CAAA;QACtB,MAAM,WAAW,GAAG,EAAE,CAAA;QACtB,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;YAC3E,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAA;YAChE,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;YACvC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,EAAC,IAAI,EAAE,iBAAiB,EAAC,CAAC,CAAA;IACzD,CAAC;;AAcH,MAAM,OAAO,GAAI,SAAQ,MAAM;;IAG7B,YAAY,KAA0B;QACpC,KAAK,CAAC,KAAK,CAAC,CAAA;IACd,CAAC;IAED,MAAM,CAAU,UAAU,GAAG,qBAAqB,CAAA;IAClD;QACE,IAAI,CAAC,SAAS,CAAC,YAAY,GAAG,OAAO,CAAA;QACrC,IAAI,CAAC,MAAM,CAAY,CAAC,EAAC,GAAG,EAAE,IAAI,EAAC,EAAE,EAAE,CAAC,CAAC;YACvC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;YACpB,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;SACrB,CAAC,CAAC,CAAA;IACL,CAAC"}
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../models/pdf.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAC,MAAM,gCAAgC,CAAA;AACrD,OAAO,EAAC,eAAe,EAAC,MAAM,UAAU,CAAA;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,QAAQ,CAAA;AAElC,MAAM,OAAO,OAAQ,SAAQ,eAAe;;IAGjC,eAAe;QACtB,KAAK,CAAC,eAAe,EAAE,CAAA;QACvB,MAAM,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAC,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAA;QACtE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;IACnF,CAAC;IAEQ,MAAM;QACb,KAAK,CAAC,MAAM,EAAE,CAAA;QACd,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,EAAE,CAAA;YAC1C,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;YACrC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,eAAe,GAAG,SAAS,IAAI,CAAC,KAAK,CAAC,UAAU,8DAA8D,CAAA;QAC3I,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;YACzC,IAAI,CAAC,SAAS,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAA;QACvC,CAAC;IACH,CAAC;IAES,sBAAsB;QAC9B,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAC7C,MAAM,UAAU,GAAG,GAAG,CAAA;QACtB,MAAM,WAAW,GAAG,EAAE,CAAA;QACtB,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;YAC3E,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAA;YAChE,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;YACvC,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAChC,CAAC;QACD,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,EAAC,IAAI,EAAE,iBAAiB,EAAC,CAAC,CAAA;IACzD,CAAC;;AAcH,MAAM,OAAO,GAAI,SAAQ,MAAM;;IAG7B,YAAY,KAA0B;QACpC,KAAK,CAAC,KAAK,CAAC,CAAA;IACd,CAAC;IAED,MAAM,CAAU,UAAU,GAAG,qBAAqB,CAAA;IAClD;QACE,IAAI,CAAC,SAAS,CAAC,YAAY,GAAG,OAAO,CAAA;QACrC,IAAI,CAAC,MAAM,CAAY,CAAC,EAAC,GAAG,EAAE,IAAI,EAAC,EAAE,EAAE,CAAC,CAAC;YACvC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC;YACpB,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;SACrB,CAAC,CAAC,CAAA;IACL,CAAC"}
@@ -1,30 +1,31 @@
1
+ import type { Dict } from "@bokehjs/core/types";
1
2
  import type * as p from "@bokehjs/core/properties";
2
- import type { LayoutDOM } from "@bokehjs/models/layouts/layout_dom";
3
+ import { Model } from "@bokehjs/model";
4
+ import { UIElement } from "@bokehjs/models/ui/ui_element";
3
5
  import { HTMLBox, HTMLBoxView } from "./layout";
4
6
  export declare class ReactiveHTMLView extends HTMLBoxView {
5
7
  model: ReactiveHTML;
6
8
  html: string;
7
9
  container: HTMLDivElement;
8
- _parent: any;
9
- _changing: boolean;
10
- _event_listeners: any;
11
- _mutation_observers: MutationObserver[];
12
- _script_fns: any;
13
- _state: any;
10
+ protected _changing: boolean;
11
+ protected readonly _event_listeners: Map<string, Map<string, (event: Event) => void>>;
12
+ protected _mutation_observers: MutationObserver[];
13
+ protected _script_fns: Map<string, Function>;
14
+ protected _state: any;
14
15
  initialize(): void;
15
- _recursive_connect(model: any, update_children: boolean, path: string): void;
16
+ _recursive_connect(model: Model, update_children: boolean, path: string): void;
16
17
  connect_signals(): void;
17
18
  connect_scripts(): void;
18
19
  run_script(property: string, silent?: boolean): void;
19
- get_records(property: string, index?: boolean): any[];
20
+ get_records(property_name: string, index?: boolean): unknown[];
20
21
  disconnect_signals(): void;
21
22
  remove(): void;
22
- get child_models(): LayoutDOM[];
23
+ get child_models(): UIElement[];
23
24
  _after_layout(): void;
24
25
  render(): void;
25
26
  private _send_event;
26
27
  private _render_child;
27
- _render_node(node: any, children: any[]): void;
28
+ _render_node(node: string, children: (string | UIElement)[]): void;
28
29
  private _render_children;
29
30
  private _render_html;
30
31
  private _render_script;
@@ -38,16 +39,16 @@ export declare class ReactiveHTMLView extends HTMLBoxView {
38
39
  export declare namespace ReactiveHTML {
39
40
  type Attrs = p.AttrsOf<Props>;
40
41
  type Props = HTMLBox.Props & {
41
- attrs: p.Property<any>;
42
- callbacks: p.Property<any>;
43
- children: p.Property<any>;
44
- data: p.Property<any>;
42
+ attrs: p.Property<Dict<[string, string[], string][]>>;
43
+ callbacks: p.Property<Dict<[string, string][]>>;
44
+ children: p.Property<Dict<(UIElement | string)[] | string>>;
45
+ data: p.Property<Model>;
45
46
  event_params: p.Property<string[]>;
46
- events: p.Property<any>;
47
+ events: p.Property<Dict<Dict<boolean>>>;
47
48
  html: p.Property<string>;
48
49
  looped: p.Property<string[]>;
49
50
  nodes: p.Property<string[]>;
50
- scripts: p.Property<any>;
51
+ scripts: p.Property<Dict<string[]>>;
51
52
  };
52
53
  }
53
54
  export interface ReactiveHTML extends ReactiveHTML.Attrs {
@@ -2,71 +2,78 @@ import { render } from "preact";
2
2
  import { useCallback } from "preact/hooks";
3
3
  import { html } from "htm/preact";
4
4
  import { div } from "@bokehjs/core/dom";
5
+ import { assert, unreachable } from "@bokehjs/core/util/assert";
6
+ import { enumerate } from "@bokehjs/core/util/iterator";
5
7
  import { isArray, isString } from "@bokehjs/core/util/types";
8
+ import { dict, keys, entries } from "@bokehjs/core/util/object";
9
+ import { Model } from "@bokehjs/model";
10
+ import { UIElement } from "@bokehjs/models/ui/ui_element";
6
11
  import { dict_to_records } from "./data";
7
12
  import { serializeEvent } from "./event-to-object";
8
- import { DOMEvent, htmlDecode } from "./html";
13
+ import { DOMEvent, html_decode } from "./html";
9
14
  import { HTMLBox, HTMLBoxView } from "./layout";
10
15
  function serialize_attrs(attrs) {
11
16
  const serialized = {};
12
- for (const attr in attrs) {
13
- let value = attrs[attr];
14
- if (!isString(value)) {
15
- }
16
- else if (value !== "" && (value === "NaN" || !isNaN(Number(value)))) {
17
- value = Number(value);
18
- }
19
- else if (value === "false" || value === "true") {
20
- value = value === "true" ? true : false;
21
- }
22
- serialized[attr] = value;
17
+ for (const [attr, value] of entries(attrs)) {
18
+ const serialized_value = (() => {
19
+ if (isString(value)) {
20
+ if (value !== "" && (value === "NaN" || !isNaN(Number(value)))) {
21
+ return Number(value);
22
+ }
23
+ else if (value === "false" || value === "true") {
24
+ return value === "true" ? true : false;
25
+ }
26
+ }
27
+ return value;
28
+ })();
29
+ serialized[attr] = serialized_value;
23
30
  }
24
31
  return serialized;
25
32
  }
26
- function escapeRegex(string) {
33
+ function escape_regex(string) {
27
34
  return string.replace(/[-\/\\^$*+?.()|[\]]/g, "\\$&");
28
35
  }
29
- function extractToken(template, str, tokens) {
30
- const tokenMapping = {};
36
+ function extract_token(template, str, tokens) {
37
+ const token_mapping = new Map();
31
38
  for (const match of tokens) {
32
- tokenMapping[`{${match}}`] = "(.*)";
39
+ token_mapping.set(`{${match}}`, "(.*)");
33
40
  }
34
- const tokenList = [];
35
- let regexpTemplate = `^${escapeRegex(template)}$`;
41
+ const token_list = [];
42
+ let regexp_template = `^${escape_regex(template)}$`;
36
43
  // Find the order of the tokens
37
- let i, tokenIndex, tokenEntry;
38
- for (const m in tokenMapping) {
39
- tokenIndex = template.indexOf(m);
44
+ let i, token_index, token_entry;
45
+ for (const [m, replacement] of token_mapping) {
46
+ token_index = template.indexOf(m);
40
47
  // Token found
41
- if (tokenIndex > -1) {
42
- regexpTemplate = regexpTemplate.replace(m, tokenMapping[m]);
43
- tokenEntry = {
44
- index: tokenIndex,
48
+ if (token_index > -1) {
49
+ regexp_template = regexp_template.replace(m, replacement);
50
+ token_entry = {
51
+ index: token_index,
45
52
  token: m,
46
53
  };
47
- for (i = 0; i < tokenList.length && tokenList[i].index < tokenIndex; i++) {
48
- ;
49
- }
54
+ for (i = 0; i < token_list.length && token_list[i].index < token_index; i++) { }
50
55
  // Insert it at index i
51
- if (i < tokenList.length) {
52
- tokenList.splice(i, 0, tokenEntry);
56
+ if (i < token_list.length) {
57
+ token_list.splice(i, 0, token_entry);
53
58
  }
54
59
  else {
55
- tokenList.push(tokenEntry);
60
+ token_list.push(token_entry);
56
61
  }
57
62
  }
58
63
  }
59
- regexpTemplate = regexpTemplate.replace(/\{[^{}]+\}/g, ".*");
60
- const match = new RegExp(regexpTemplate).exec(str);
61
- let result = null;
62
- if (match) {
63
- result = {};
64
+ regexp_template = regexp_template.replace(/\{[^{}]+\}/g, ".*");
65
+ const match = new RegExp(regexp_template).exec(str);
66
+ if (match != null) {
67
+ const result = {};
64
68
  // Find your token entry
65
- for (i = 0; i < tokenList.length; i++) {
66
- result[tokenList[i].token.slice(1, -1)] = match[i + 1];
69
+ for (i = 0; i < token_list.length; i++) {
70
+ result[token_list[i].token.slice(1, -1)] = match[i + 1];
67
71
  }
72
+ return result;
73
+ }
74
+ else {
75
+ return null;
68
76
  }
69
- return result;
70
77
  }
71
78
  function element_lookup(root, el_id) {
72
79
  let el = root.getElementById(el_id);
@@ -79,37 +86,30 @@ export class ReactiveHTMLView extends HTMLBoxView {
79
86
  static __name__ = "ReactiveHTMLView";
80
87
  html;
81
88
  container;
82
- _parent = null;
83
89
  _changing = false;
84
- _event_listeners = {};
90
+ _event_listeners = new Map();
85
91
  _mutation_observers = [];
86
- _script_fns = {};
92
+ _script_fns = new Map();
87
93
  _state = {};
88
94
  initialize() {
89
95
  super.initialize();
90
- this.html = htmlDecode(this.model.html) || this.model.html;
96
+ this.html = html_decode(this.model.html) ?? this.model.html;
91
97
  }
92
98
  _recursive_connect(model, update_children, path) {
93
- for (const prop in model.properties) {
94
- let subpath;
95
- if (path.length) {
96
- subpath = `${path}.${prop}`;
97
- }
98
- else {
99
- subpath = prop;
100
- }
101
- const obj = model[prop];
99
+ for (const prop of model) {
100
+ const subpath = path.length != 0 ? `${path}.${prop.attr}` : prop.attr;
101
+ const obj = prop.get_value();
102
102
  if (obj == null) {
103
103
  continue;
104
104
  }
105
- if (obj.properties != null) {
105
+ if (obj instanceof Model) {
106
106
  this._recursive_connect(obj, true, subpath);
107
107
  }
108
- this.on_change(model.properties[prop], () => {
108
+ this.on_change(prop, () => {
109
109
  if (update_children) {
110
- for (const node in this.model.children) {
111
- if (this.model.children[node] == prop) {
112
- let children = model[prop];
110
+ for (const [node, attr] of entries(this.model.children)) {
111
+ if (attr == prop.attr) {
112
+ let children = prop.get_value();
113
113
  if (!isArray(children)) {
114
114
  children = [children];
115
115
  }
@@ -128,7 +128,7 @@ export class ReactiveHTMLView extends HTMLBoxView {
128
128
  super.connect_signals();
129
129
  const { children, events } = this.model.properties;
130
130
  this.on_change(children, async () => {
131
- this.html = htmlDecode(this.model.html) || this.model.html;
131
+ this.html = html_decode(this.model.html) ?? this.model.html;
132
132
  await this.build_child_views();
133
133
  this.invalidate_render();
134
134
  });
@@ -140,32 +140,33 @@ export class ReactiveHTMLView extends HTMLBoxView {
140
140
  this.connect_scripts();
141
141
  }
142
142
  connect_scripts() {
143
- const id = this.model.data.id;
144
- for (const prop in this.model.scripts) {
145
- const scripts = this.model.scripts[prop];
143
+ const { id } = this.model.data;
144
+ for (const [prop, scripts] of entries(this.model.scripts)) {
146
145
  let data_model = this.model.data;
147
146
  let attr;
148
- if (prop.indexOf(".") >= 0) {
147
+ if (prop.includes(".")) {
149
148
  const path = prop.split(".");
150
149
  attr = path[path.length - 1];
151
150
  for (const p of path.slice(0, -1)) {
152
- data_model = data_model[p];
151
+ const value = data_model.property(p).get_value();
152
+ assert(value instanceof Model);
153
+ data_model = value;
153
154
  }
154
155
  }
155
156
  else {
156
157
  attr = prop;
157
158
  }
158
159
  for (const script of scripts) {
159
- const decoded_script = htmlDecode(script) || script;
160
+ const decoded_script = html_decode(script) ?? script;
160
161
  const script_fn = this._render_script(decoded_script, id);
161
- this._script_fns[prop] = script_fn;
162
- const property = data_model.properties[attr];
163
- if (property == null) {
162
+ this._script_fns.set(prop, script_fn);
163
+ if (!(attr in data_model.properties)) {
164
164
  continue;
165
165
  }
166
+ const property = data_model.property(attr);
166
167
  const is_event_param = this.model.event_params.includes(prop);
167
168
  this.on_change(property, () => {
168
- if (!this._changing && !(is_event_param && !data_model[prop])) {
169
+ if (!this._changing && !(is_event_param && !data_model.property(prop).get_value())) {
169
170
  this.run_script(prop);
170
171
  if (is_event_param) {
171
172
  data_model.setv({ [prop]: false });
@@ -176,7 +177,7 @@ export class ReactiveHTMLView extends HTMLBoxView {
176
177
  }
177
178
  }
178
179
  run_script(property, silent = false) {
179
- const script_fn = this._script_fns[property];
180
+ const script_fn = this._script_fns.get(property);
180
181
  if (script_fn === undefined) {
181
182
  if (!silent) {
182
183
  console.log(`Script '${property}' could not be found.`);
@@ -184,15 +185,17 @@ export class ReactiveHTMLView extends HTMLBoxView {
184
185
  return;
185
186
  }
186
187
  const this_obj = {
187
- get_records: (property, index) => this.get_records(property, index),
188
+ get_records(property, index) {
189
+ return this.get_records(property, index);
190
+ },
188
191
  };
189
- for (const name in this._script_fns) {
192
+ for (const name of this._script_fns.keys()) {
190
193
  this_obj[name] = () => this.run_script(name);
191
194
  }
192
195
  return script_fn(this.model, this.model.data, this._state, this, (s) => this.run_script(s), this_obj);
193
196
  }
194
- get_records(property, index = true) {
195
- return dict_to_records(this.model.data[property], index);
197
+ get_records(property_name, index = true) {
198
+ return dict_to_records(this.model.data.property(property_name), index);
196
199
  }
197
200
  disconnect_signals() {
198
201
  super.disconnect_signals();
@@ -205,8 +208,8 @@ export class ReactiveHTMLView extends HTMLBoxView {
205
208
  }
206
209
  get child_models() {
207
210
  const models = [];
208
- for (const parent in this.model.children) {
209
- for (const model of this.model.children[parent]) {
211
+ for (const [_parent, children] of entries(this.model.children)) {
212
+ for (const model of children) {
210
213
  if (!isString(model)) {
211
214
  models.push(model);
212
215
  }
@@ -241,33 +244,40 @@ export class ReactiveHTMLView extends HTMLBoxView {
241
244
  }
242
245
  this.model.trigger_event(new DOMEvent(elname, serialized));
243
246
  }
244
- _render_child(model, el) {
245
- const view = this._child_views.get(model);
246
- if (view == null) {
247
- el.innerHTML = htmlDecode(model) || model;
247
+ _render_child(child, el) {
248
+ if (isString(child)) {
249
+ el.innerHTML = html_decode(child) ?? child;
248
250
  }
249
251
  else {
250
- el.appendChild(view.el);
251
- view.render();
252
- view.after_render();
252
+ const view = this._child_views.get(child);
253
+ if (view == null) {
254
+ el.innerHTML = "";
255
+ }
256
+ else {
257
+ el.appendChild(view.el);
258
+ view.render();
259
+ view.after_render();
260
+ }
253
261
  }
254
262
  }
255
263
  _render_node(node, children) {
256
- const id = this.model.data.id;
257
- if (this.model.looped.indexOf(node) > -1) {
258
- for (let i = 0; i < children.length; i++) {
259
- const el = element_lookup(this.shadow_el, `${node}-${i}-${id}`);
264
+ const { id } = this.model.data;
265
+ if (this.model.looped.includes(node)) {
266
+ for (const [child, i] of enumerate(children)) {
267
+ const el_id = `${node}-${i}-${id}`;
268
+ const el = element_lookup(this.shadow_el, el_id);
260
269
  if (el == null) {
261
- console.warn(`DOM node '${node}-${i}-${id}' could not be found. Cannot render children.`);
270
+ console.warn(`DOM node '${el_id}' could not be found. Cannot render children.`);
262
271
  continue;
263
272
  }
264
- this._render_child(children[i], el);
273
+ this._render_child(child, el);
265
274
  }
266
275
  }
267
276
  else {
268
- const el = element_lookup(this.shadow_el, `${node}-${id}`);
277
+ const el_id = `${node}-${id}`;
278
+ const el = element_lookup(this.shadow_el, el_id);
269
279
  if (el == null) {
270
- console.warn(`DOM node '${node}-${id}' could not be found. Cannot render children.`);
280
+ console.warn(`DOM node '${el_id}' could not be found. Cannot render children.`);
271
281
  return;
272
282
  }
273
283
  for (const child of children) {
@@ -276,24 +286,33 @@ export class ReactiveHTMLView extends HTMLBoxView {
276
286
  }
277
287
  }
278
288
  _render_children() {
279
- for (const node in this.model.children) {
280
- let children = this.model.children[node];
281
- if (isString(children)) {
282
- children = this.model.data[children];
283
- if (!isArray(children)) {
284
- children = [children];
289
+ for (const [node, children] of entries(this.model.children)) {
290
+ const computed_children = (() => {
291
+ if (isString(children)) {
292
+ const value = this.model.data.property(children).get_value();
293
+ if (isString(value)) {
294
+ return [value];
295
+ }
296
+ else if (isArray(value)) {
297
+ return value;
298
+ }
299
+ else {
300
+ unreachable();
301
+ }
285
302
  }
286
- }
287
- this._render_node(node, children);
303
+ else {
304
+ return children;
305
+ }
306
+ })();
307
+ this._render_node(node, computed_children);
288
308
  }
289
309
  }
290
310
  _render_html(literal, state = {}) {
291
311
  let htm = literal.replace(/[`]/g, "\\$&");
292
- let callbacks = "";
312
+ let collected_callbacks = "";
293
313
  const methods = [];
294
- for (const elname in this.model.callbacks) {
295
- for (const callback of this.model.callbacks[elname]) {
296
- const [cb, method] = callback;
314
+ for (const [el_name, callbacks] of entries(this.model.callbacks)) {
315
+ for (const [cb, method] of callbacks) {
297
316
  let definition;
298
317
  htm = htm.replaceAll(`\${${method}}`, `$--{${method}}`);
299
318
  if (method.startsWith("script(")) {
@@ -314,7 +333,7 @@ export class ReactiveHTMLView extends HTMLBoxView {
314
333
  else {
315
334
  definition = `
316
335
  const ${method} = (event) => {
317
- let elname = "${elname}"
336
+ let elname = "${el_name}"
318
337
  if (RegExp("\{\{.*loop\.index.*\}\}").test(elname)) {
319
338
  const pattern = RegExp(elname.replace(/\{\{(.+?)\}\}/g, String.fromCharCode(92) + "d+"))
320
339
  for (const p of event.path) {
@@ -332,27 +351,28 @@ export class ReactiveHTMLView extends HTMLBoxView {
332
351
  continue;
333
352
  }
334
353
  methods.push(method);
335
- callbacks = callbacks + definition;
354
+ collected_callbacks += definition;
336
355
  }
337
356
  }
338
- htm = (htm
357
+ htm = htm
339
358
  .replaceAll("${model.", "$-{model.")
340
359
  .replaceAll("${", "${data.")
341
360
  .replaceAll("$-{model.", "${model.")
342
- .replaceAll("$--{", "${"));
343
- return new Function("view, model, data, state, html, useCallback", `${callbacks}return html\`${htm}\`;`)(this, this.model, this.model.data, state, html, useCallback);
361
+ .replaceAll("$--{", "${");
362
+ return new Function("view, model, data, state, html, useCallback", `${collected_callbacks}return html\`${htm}\`;`)(this, this.model, this.model.data, state, html, useCallback);
344
363
  }
345
364
  _render_script(literal, id) {
346
365
  const scripts = [];
347
366
  for (const elname of this.model.nodes) {
348
367
  const elvar = elname.replace("-", "_");
349
- if (literal.indexOf(elvar) === -1) {
368
+ if (!literal.includes(elvar)) {
350
369
  continue;
351
370
  }
352
371
  const script = `
353
372
  let ${elvar} = view.shadow_el.getElementById('${elname}-${id}')
354
- if (${elvar} == null)
373
+ if (${elvar} == null) {
355
374
  ${elvar} = document.getElementById('${elname}-${id}')
375
+ }
356
376
  if (${elvar} == null) {
357
377
  console.warn("DOM node '${elname}' could not be found. Cannot execute callback.")
358
378
  return
@@ -377,8 +397,8 @@ export class ReactiveHTMLView extends HTMLBoxView {
377
397
  this._mutation_observers = [];
378
398
  }
379
399
  _setup_mutation_observers() {
380
- const id = this.model.data.id;
381
- for (const name in this.model.attrs) {
400
+ const { id } = this.model.data;
401
+ for (const name of keys(this.model.attrs)) {
382
402
  const el = element_lookup(this.shadow_el, `${name}-${id}`);
383
403
  if (el == null) {
384
404
  console.warn(`DOM node '${name}-${id}' could not be found. Cannot set up MutationObserver.`);
@@ -392,45 +412,45 @@ export class ReactiveHTMLView extends HTMLBoxView {
392
412
  }
393
413
  }
394
414
  _remove_event_listeners() {
395
- const id = this.model.data.id;
396
- for (const node in this._event_listeners) {
415
+ const { id } = this.model.data;
416
+ for (const [node, callbacks] of this._event_listeners) {
397
417
  const el = element_lookup(this.shadow_el, `${node}-${id}`);
398
418
  if (el == null) {
399
419
  continue;
400
420
  }
401
- for (const event_name in this._event_listeners[node]) {
402
- const event_callback = this._event_listeners[node][event_name];
421
+ for (const [event_name, event_callback] of callbacks) {
403
422
  el.removeEventListener(event_name, event_callback);
404
423
  }
405
424
  }
406
- this._event_listeners = {};
425
+ this._event_listeners.clear();
407
426
  }
408
427
  _setup_event_listeners() {
409
- const id = this.model.data.id;
410
- for (const node in this.model.events) {
428
+ const { id } = this.model.data;
429
+ const attrs = dict(this.model.attrs);
430
+ for (const [node, node_events] of entries(this.model.events)) {
411
431
  const el = element_lookup(this.shadow_el, `${node}-${id}`);
412
432
  if (el == null) {
413
433
  console.warn(`DOM node '${node}-${id}' could not be found. Cannot subscribe to DOM events.`);
414
434
  continue;
415
435
  }
416
- const node_events = this.model.events[node];
417
- for (const event_name in node_events) {
436
+ for (const [event_name, event_doit] of entries(node_events)) {
418
437
  const event_callback = (event) => {
419
438
  this._send_event(node, event_name, event);
420
- if (node in this.model.attrs && node_events[event_name]) {
439
+ if (attrs.has(node) && event_doit) {
421
440
  this._update_model(el, node);
422
441
  }
423
442
  };
424
443
  el.addEventListener(event_name, event_callback);
425
- if (!(node in this._event_listeners)) {
426
- this._event_listeners[node] = {};
444
+ let callbacks = this._event_listeners.get(node);
445
+ if (callbacks === undefined) {
446
+ this._event_listeners.set(node, callbacks = new Map());
427
447
  }
428
- this._event_listeners[node][event_name] = event_callback;
448
+ callbacks.set(event_name, event_callback);
429
449
  }
430
450
  }
431
451
  }
432
452
  _update(property = null) {
433
- if (property == null || (this.html.indexOf(`\${${property}}`) > -1)) {
453
+ if (property == null || this.html.includes(`\${${property}}`)) {
434
454
  const rendered = this._render_html(this.html);
435
455
  if (rendered == null) {
436
456
  return;
@@ -448,25 +468,26 @@ export class ReactiveHTMLView extends HTMLBoxView {
448
468
  if (this._changing) {
449
469
  return;
450
470
  }
471
+ const attr_infos = dict(this.model.attrs).get(name) ?? [];
451
472
  const attrs = {};
452
- for (const attr_info of this.model.attrs[name]) {
473
+ for (const attr_info of attr_infos) {
453
474
  const [attr, tokens, template] = attr_info;
454
- let value = attr === "children" ? el.innerHTML : el[attr];
475
+ let value = attr == "children" ? el.innerHTML : el[attr];
455
476
  if (tokens.length === 1 && (`{${tokens[0]}}` === template)) {
456
477
  attrs[tokens[0]] = value;
457
478
  }
458
479
  else if (isString(value)) {
459
- value = extractToken(template, value, tokens);
480
+ value = extract_token(template, value, tokens);
460
481
  if (value == null) {
461
482
  console.warn(`Could not resolve parameters in ${name} element ${attr} attribute value ${value}.`);
462
483
  }
463
484
  else {
464
- for (const param in value) {
465
- if (value[param] === undefined) {
485
+ for (const [param, param_val] of entries(value)) {
486
+ if (param_val === undefined) {
466
487
  console.warn(`Could not resolve ${param} in ${name} element ${attr} attribute value ${value}.`);
467
488
  }
468
489
  else {
469
- attrs[param] = value[param];
490
+ attrs[param] = param_val;
470
491
  }
471
492
  }
472
493
  }
@@ -492,17 +513,17 @@ export class ReactiveHTML extends HTMLBox {
492
513
  static __module__ = "panel.models.reactive_html";
493
514
  static {
494
515
  this.prototype.default_view = ReactiveHTMLView;
495
- this.define(({ List, Any, Str }) => ({
496
- attrs: [Any, {}],
497
- callbacks: [Any, {}],
498
- children: [Any, {}],
499
- data: [Any],
516
+ this.define(({ Bool, Str, List, Dict, Tuple, Or, Ref }) => ({
517
+ attrs: [Dict(List(Tuple(Str, List(Str), Str))), {}],
518
+ callbacks: [Dict(List(Tuple(Str, Str))), {}],
519
+ children: [Dict(Or(List(Or(Ref(UIElement), Str)), Str)), {}],
520
+ data: [Ref(Model)],
500
521
  event_params: [List(Str), []],
501
- events: [Any, {}],
522
+ events: [Dict(Dict(Bool)), {}],
502
523
  html: [Str, ""],
503
524
  looped: [List(Str), []],
504
525
  nodes: [List(Str), []],
505
- scripts: [Any, {}],
526
+ scripts: [Dict(List(Str)), {}],
506
527
  }));
507
528
  }
508
529
  }