@perspective-dev/viewer 5.1.0 → 5.3.0

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 (245) hide show
  1. package/dist/cdn/perspective-viewer.js +9 -9
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/docs/perspective-docs.json +1 -0
  24. package/dist/esm/column-format.d.ts +19 -28
  25. package/dist/esm/perspective-viewer.d.ts +3 -0
  26. package/dist/esm/perspective-viewer.inline.js +9 -9
  27. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  28. package/dist/esm/perspective-viewer.js +9 -9
  29. package/dist/esm/perspective-viewer.js.map +4 -4
  30. package/dist/esm/plugin.d.ts +10 -0
  31. package/dist/esm/providers.d.ts +79 -0
  32. package/dist/esm/ts-rs/CompactDisplay.d.ts +5 -0
  33. package/dist/esm/ts-rs/CurrencyCode.d.ts +1 -0
  34. package/dist/esm/ts-rs/CurrencyDisplay.d.ts +1 -0
  35. package/dist/esm/ts-rs/CurrencyNumberFormatStyle.d.ts +8 -0
  36. package/dist/esm/ts-rs/CurrencySign.d.ts +1 -0
  37. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
  38. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +54 -0
  39. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +34 -0
  40. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
  41. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
  42. package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
  43. package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
  44. package/dist/esm/ts-rs/Notation.d.ts +17 -0
  45. package/dist/esm/ts-rs/NumberFormatStyle.d.ts +18 -0
  46. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +42 -2
  47. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +35 -0
  48. package/dist/esm/ts-rs/RestoreOptions.d.ts +19 -0
  49. package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
  50. package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
  51. package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
  52. package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
  53. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
  54. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +24 -0
  55. package/dist/esm/ts-rs/SplitRollupMode.d.ts +9 -0
  56. package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
  57. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
  58. package/dist/esm/ts-rs/Unit.d.ts +1 -0
  59. package/dist/esm/ts-rs/UnitDisplay.d.ts +1 -0
  60. package/dist/esm/ts-rs/UnitNumberFormatStyle.d.ts +6 -0
  61. package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
  62. package/dist/esm/ts-rs/ViewerConfig.d.ts +42 -2
  63. package/dist/esm/ts-rs/ViewerConfigInitial.d.ts +175 -0
  64. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +63 -0
  65. package/dist/esm/ts-rs/WindowSpec.d.ts +3 -2
  66. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
  67. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +20 -9
  68. package/dist/wasm/perspective-viewer.d.ts +157 -41
  69. package/dist/wasm/perspective-viewer.js +452 -151
  70. package/dist/wasm/perspective-viewer.wasm +0 -0
  71. package/dist/wasm/perspective-viewer.wasm.d.ts +22 -17
  72. package/package.json +2 -1
  73. package/src/css/_viewer_bundle.css +1 -0
  74. package/src/css/column-selector.css +8 -0
  75. package/src/css/column-settings-panel.css +9 -5
  76. package/src/css/column-style.css +209 -31
  77. package/src/css/config-selector.css +38 -3
  78. package/src/css/dom/select.css +2 -1
  79. package/src/css/form/code-editor.css +6 -7
  80. package/src/css/form/mirrored-textarea.css +81 -0
  81. package/src/css/viewer.css +328 -0
  82. package/src/rust/agent/client/mod.rs +19 -0
  83. package/src/rust/agent/client/protocol.rs +320 -0
  84. package/src/rust/agent/client/transport.rs +287 -0
  85. package/src/rust/agent/client/turn.rs +174 -0
  86. package/src/rust/agent/config.rs +159 -0
  87. package/src/rust/agent/docs.rs +293 -0
  88. package/src/rust/agent/markdown/block.rs +509 -0
  89. package/src/rust/agent/markdown/entity.rs +52 -0
  90. package/src/rust/agent/markdown/events.rs +71 -0
  91. package/src/rust/agent/markdown/inline.rs +497 -0
  92. package/src/rust/agent/markdown/mod.rs +466 -0
  93. package/src/rust/agent/mod.rs +23 -0
  94. package/src/rust/agent/preamble.md +16 -0
  95. package/src/rust/agent/runtime.rs +201 -0
  96. package/src/rust/agent/slot.rs +236 -0
  97. package/src/rust/agent/tools.rs +1141 -0
  98. package/src/rust/components/chat_panel.rs +289 -0
  99. package/src/rust/components/column_selector/active_column.rs +2 -2
  100. package/src/rust/components/column_selector/add_expression_button.rs +3 -3
  101. package/src/rust/components/column_selector/aggregate_selector.rs +2 -3
  102. package/src/rust/components/column_selector/config_selector.rs +84 -7
  103. package/src/rust/components/column_selector/expr_edit_button.rs +4 -10
  104. package/src/rust/components/column_selector/inactive_column.rs +2 -2
  105. package/src/rust/components/column_selector.rs +55 -4
  106. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +293 -126
  107. package/src/rust/components/column_settings_sidebar/style_tab.rs +262 -225
  108. package/src/rust/components/column_settings_sidebar.rs +88 -69
  109. package/src/rust/components/containers/dragdrop_list.rs +37 -0
  110. package/src/rust/components/editable_header.rs +85 -144
  111. package/src/rust/components/expression_editor.rs +9 -16
  112. package/src/rust/components/form/code_editor.rs +29 -28
  113. package/src/rust/components/form/debug.rs +9 -5
  114. package/src/rust/components/form/mirrored_textarea.rs +165 -0
  115. package/src/rust/components/form/mod.rs +4 -1
  116. package/src/rust/components/form/multi_stop_gradient_selector.rs +476 -0
  117. package/src/rust/components/form/named_value_picker.rs +86 -0
  118. package/src/rust/components/form/palette_selector.rs +440 -0
  119. package/src/rust/components/main_panel/msg.rs +2 -2
  120. package/src/rust/components/main_panel/reconcile.rs +4 -4
  121. package/src/rust/components/mod.rs +2 -0
  122. package/src/rust/components/plugin_tab.rs +4 -28
  123. package/src/rust/components/settings_panel.rs +105 -22
  124. package/src/rust/components/viewer/msg.rs +46 -7
  125. package/src/rust/components/viewer/panels.rs +17 -20
  126. package/src/rust/components/viewer/render.rs +10 -3
  127. package/src/rust/components/viewer/settings.rs +163 -23
  128. package/src/rust/components/viewer/snapshots.rs +46 -21
  129. package/src/rust/components/viewer/wiring.rs +14 -25
  130. package/src/rust/components/viewer.rs +12 -8
  131. package/src/rust/components/window_editor.rs +75 -103
  132. package/src/rust/config/column_config_schema.rs +100 -41
  133. package/src/rust/config/css_scale.rs +882 -0
  134. package/src/rust/config/datetime_column_style/custom.rs +17 -0
  135. package/src/rust/config/datetime_column_style/simple.rs +10 -0
  136. package/src/rust/config/mod.rs +2 -0
  137. package/src/rust/config/number_string_format.rs +33 -0
  138. package/src/rust/config/options.rs +28 -0
  139. package/src/rust/config/plugin_static_config.rs +120 -5
  140. package/src/rust/config/viewer_config.rs +216 -3
  141. package/src/rust/config/workspace_config.rs +138 -10
  142. package/src/rust/custom_elements/viewer.rs +369 -119
  143. package/src/rust/custom_events.rs +43 -1
  144. package/src/rust/js/regular_layout.rs +3 -3
  145. package/src/rust/lib.rs +15 -0
  146. package/src/rust/presentation/column_locator.rs +19 -5
  147. package/src/rust/presentation.rs +200 -36
  148. package/src/rust/queries/column_locator.rs +74 -44
  149. package/src/rust/queries/get_viewer_config.rs +2 -1
  150. package/src/rust/queries/mod.rs +2 -0
  151. package/src/rust/queries/palette_set.rs +82 -0
  152. package/src/rust/queries/plugin_column_styles.rs +6 -2
  153. package/src/rust/renderer/dispatch.rs +16 -4
  154. package/src/rust/renderer/plugin_config.rs +309 -47
  155. package/src/rust/renderer.rs +15 -42
  156. package/src/rust/session/column_defaults_update.rs +32 -7
  157. package/src/rust/session/metadata.rs +23 -21
  158. package/src/rust/session/props.rs +1 -0
  159. package/src/rust/session/replace_expression_update.rs +1 -0
  160. package/src/rust/session.rs +9 -6
  161. package/src/rust/tasks/create_panel.rs +46 -49
  162. package/src/rust/tasks/edit_expression.rs +19 -23
  163. package/src/rust/tasks/edit_window.rs +9 -11
  164. package/src/rust/tasks/eject.rs +4 -1
  165. package/src/rust/tasks/pipeline.rs +2 -2
  166. package/src/rust/tasks/presize_panels.rs +44 -5
  167. package/src/rust/tasks/reset_all.rs +4 -4
  168. package/src/rust/tasks/restore_and_render.rs +17 -44
  169. package/src/rust/tasks/restore_panel.rs +37 -22
  170. package/src/rust/tasks/set_edit_mode.rs +3 -1
  171. package/src/rust/tasks/sync_update_panels.rs +18 -24
  172. package/src/rust/tasks/update_theme.rs +23 -18
  173. package/src/rust/utils/css_vars.rs +26 -0
  174. package/src/rust/utils/mod.rs +2 -0
  175. package/src/rust/utils/spawn.rs +1 -1
  176. package/src/rust/workspace.rs +454 -199
  177. package/src/svg/lock.svg +7 -0
  178. package/src/themes/botanical.css +10 -0
  179. package/src/themes/defaults.css +12 -0
  180. package/src/themes/dracula.css +17 -0
  181. package/src/themes/gruvbox-dark.css +20 -0
  182. package/src/themes/gruvbox.css +10 -0
  183. package/src/themes/icons.css +1 -0
  184. package/src/themes/intl/de.css +16 -0
  185. package/src/themes/intl/es.css +16 -0
  186. package/src/themes/intl/fr.css +16 -0
  187. package/src/themes/intl/ja.css +16 -0
  188. package/src/themes/intl/pt.css +16 -0
  189. package/src/themes/intl/zh.css +16 -0
  190. package/src/themes/intl.css +16 -0
  191. package/src/themes/monokai.css +22 -0
  192. package/src/themes/phosphor.css +5 -0
  193. package/src/themes/pro-dark.css +10 -0
  194. package/src/themes/solarized-dark.css +7 -0
  195. package/src/themes/solarized.css +7 -0
  196. package/src/themes/vaporwave.css +20 -0
  197. package/src/ts/column-format.ts +67 -46
  198. package/src/ts/perspective-viewer.ts +3 -0
  199. package/src/ts/plugin.ts +14 -0
  200. package/src/ts/providers.ts +96 -0
  201. package/src/ts/ts-rs/CompactDisplay.ts +3 -0
  202. package/src/ts/ts-rs/CurrencyCode.ts +3 -0
  203. package/src/ts/ts-rs/CurrencyDisplay.ts +3 -0
  204. package/src/ts/ts-rs/CurrencyNumberFormatStyle.ts +6 -0
  205. package/src/ts/ts-rs/CurrencySign.ts +3 -0
  206. package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
  207. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +54 -0
  208. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +25 -0
  209. package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
  210. package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
  211. package/src/ts/ts-rs/FormatMode.ts +3 -0
  212. package/src/ts/ts-rs/FormatUnit.ts +3 -0
  213. package/src/ts/ts-rs/Notation.ts +11 -0
  214. package/src/ts/ts-rs/NumberFormatStyle.ts +12 -0
  215. package/src/ts/ts-rs/PanelViewerConfig.ts +48 -2
  216. package/src/ts/ts-rs/PluginStaticConfig.ts +36 -1
  217. package/src/ts/ts-rs/RestoreOptions.ts +20 -0
  218. package/src/ts/ts-rs/RoundingMode.ts +3 -0
  219. package/src/ts/ts-rs/RoundingPriority.ts +3 -0
  220. package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
  221. package/src/ts/ts-rs/SignDisplay.ts +3 -0
  222. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
  223. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +25 -0
  224. package/src/ts/ts-rs/SplitRollupMode.ts +11 -0
  225. package/src/ts/ts-rs/StringColorMode.ts +3 -0
  226. package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
  227. package/src/ts/ts-rs/Unit.ts +3 -0
  228. package/src/ts/ts-rs/UnitDisplay.ts +3 -0
  229. package/src/ts/ts-rs/UnitNumberFormatStyle.ts +5 -0
  230. package/src/ts/ts-rs/UseGrouping.ts +3 -0
  231. package/src/ts/ts-rs/ViewerConfig.ts +48 -2
  232. package/src/ts/ts-rs/ViewerConfigInitial.ts +164 -0
  233. package/src/ts/ts-rs/ViewerConfigUpdate.ts +72 -2
  234. package/src/ts/ts-rs/WindowSpec.ts +3 -2
  235. package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
  236. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +18 -9
  237. package/dist/esm/ts-rs/WindowAggregate.d.ts +0 -1
  238. package/src/rust/components/form/color_range_selector.rs +0 -137
  239. package/src/ts/ts-rs/WindowAggregate.ts +0 -3
  240. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
  241. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
  242. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
  243. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
  244. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
  245. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
@@ -18,6 +18,7 @@ use web_sys::*;
18
18
  use yew::prelude::*;
19
19
 
20
20
  use crate::components::form::highlight::highlight;
21
+ use crate::components::form::mirrored_textarea::MirroredTextarea;
21
22
  use crate::components::function_dropdown::{FunctionDropDownElement, FunctionDropDownPortal};
22
23
  use crate::exprtk::{Cursor, tokenize};
23
24
  use crate::utils::*;
@@ -44,6 +45,9 @@ pub struct CodeEditorProps {
44
45
  #[prop_or_default]
45
46
  pub error: Option<ExprValidationError>,
46
47
 
48
+ #[prop_or_default]
49
+ pub contained: bool,
50
+
47
51
  /// Selected theme name, threaded for PortalModal consumers.
48
52
  #[prop_or_default]
49
53
  pub theme: String,
@@ -164,40 +168,37 @@ pub fn code_editor(props: &CodeEditorProps) -> Html {
164
168
  ))
165
169
  .collect::<Html>();
166
170
 
167
- let class = if props.wordwrap {
168
- "wordwrap scrollable"
169
- } else {
170
- "scrollable"
171
+ let mut outer_class = classes!("scrollable");
172
+ let mut inner_class = classes!("editor-inner", "scrollable");
173
+
174
+ if props.wordwrap {
175
+ outer_class.push("wordwrap");
176
+ inner_class.push("wordwrap");
171
177
  };
178
+
179
+ if props.contained {
180
+ outer_class.push("contained")
181
+ }
182
+
172
183
  clone!(props.disabled);
173
184
  html! {
174
185
  <>
175
- <div id="editor" {class}>
186
+ <div id="editor" class={outer_class}>
176
187
  <div id="line_numbers" ref={lineno_ref}>{ line_numbers }</div>
177
- <div id="editor-inner" {class}>
178
- <textarea
179
- {disabled}
180
- id="textarea_editable"
181
- class="scrollable"
182
- ref={textarea_ref}
183
- spellcheck="false"
184
- {oninput}
185
- {onscroll}
186
- {onkeydown}
187
- />
188
+ <MirroredTextarea
189
+ id="textarea_editable"
190
+ mirror_id="content"
191
+ class={inner_class}
192
+ mirror={terms}
193
+ {textarea_ref}
194
+ mirror_ref={content_ref}
195
+ {disabled}
196
+ {oninput}
197
+ {onscroll}
198
+ {onkeydown}
199
+ >
188
200
  <div id="editor-height-sizer" />
189
- <pre id="content" ref={content_ref}>
190
- { terms }
191
- { {
192
- // A linebreak which pushs a textarea into scroll overflow
193
- // may not necessarily do so in the `<pre>`, because there is
194
- // no cursor when the last line has no content, so add
195
- // some space here to make sure overlfow is in sync
196
- // with the text area.
197
- " "
198
- } }
199
- </pre>
200
- </div>
201
+ </MirroredTextarea>
201
202
  </div>
202
203
  <FunctionDropDownPortal
203
204
  element={(*portal_dropdown).clone()}
@@ -23,12 +23,14 @@ use crate::presentation::*;
23
23
  use crate::renderer::*;
24
24
  use crate::session::*;
25
25
  use crate::utils::*;
26
+ use crate::workspace::Workspace;
26
27
 
27
28
  #[derive(Clone, PartialEq, Properties)]
28
29
  pub struct DebugPanelProps {
29
30
  pub presentation: Presentation,
30
31
  pub renderer: Renderer,
31
32
  pub session: Session,
33
+ pub workspace: Workspace,
32
34
 
33
35
  /// Trap-door width pinned by the parent `SettingsPanel` so switching
34
36
  /// tabs doesn't shrink the panel. Threaded into the hidden sizer
@@ -176,6 +178,7 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
176
178
  <CodeEditor
177
179
  expr={&*expr}
178
180
  disabled=false
181
+ contained=true
179
182
  {oninput}
180
183
  {onsave}
181
184
  select_all={select_all.subscriber()}
@@ -236,15 +239,16 @@ impl DebugPanelProps {
236
239
  ApiFuture::spawn(async move {
237
240
  match serde_json::from_str(&text) {
238
241
  Ok(config) => {
239
- match crate::tasks::restore_and_render(
242
+ let active =
243
+ props.workspace.active_renderer().as_ref() == Some(&props.renderer);
244
+ match crate::tasks::restore_panel(
240
245
  &props.session,
241
246
  &props.renderer,
242
247
  &props.presentation,
243
- // A user-pasted config apply — an explicit request,
244
- // same affordance as the public `restore()`.
245
- crate::tasks::RunOrigin::Public,
248
+ &props.workspace,
249
+ crate::tasks::RestoreMode::Existing { active },
246
250
  config,
247
- async { Ok(()) },
251
+ crate::tasks::RestoreErrors::Suppress,
248
252
  )
249
253
  .await
250
254
  {
@@ -0,0 +1,165 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ //! A `<textarea>` whose box is sized by its own text.
14
+ //!
15
+ //! A textarea cannot grow with its content — it scrolls at whatever height
16
+ //! it was given. The technique here, shared by [`CodeEditor`] and the chat
17
+ //! prompt, is to make the textarea a purely functional layer: it takes the
18
+ //! keystrokes, the selection and the caret, but paints no glyphs
19
+ //! (`color: transparent`). Underneath sits a `<pre>` mirror of the same
20
+ //! characters which IS painted and IS in flow, so the ordinary layout
21
+ //! engine sizes the box.
22
+ //!
23
+ //! This is deliberately not a JS measure-and-resize loop
24
+ //! (`scrollHeight` → inline `height`): that re-measures on a reflow it
25
+ //! just forced, drifts whenever font-size or the loaded font changes
26
+ //! without an `input` event to re-trigger it, and jitters at fractional
27
+ //! line heights. Here the two layers share their text metrics, so growth
28
+ //! is exact by construction and free at runtime.
29
+ //!
30
+ //! Consumers decide the AXES: the mirror's `white-space` governs growth,
31
+ //! so `pre-wrap` grows only downward (the chat prompt) while `pre` also
32
+ //! grows sideways (the expression editor). They also own the box —
33
+ //! padding, borders, min/max size — with `mirrored-textarea.css` fixing
34
+ //! only what MUST agree between the two layers.
35
+ //!
36
+ //! [`CodeEditor`]: super::code_editor::CodeEditor
37
+
38
+ use yew::html::IntoPropValue;
39
+ use yew::prelude::*;
40
+
41
+ #[derive(Clone)]
42
+ pub struct Mirror(pub Html);
43
+
44
+ impl PartialEq for Mirror {
45
+ fn eq(&self, _other: &Self) -> bool {
46
+ false
47
+ }
48
+ }
49
+
50
+ impl IntoPropValue<Mirror> for Html {
51
+ fn into_prop_value(self) -> Mirror {
52
+ Mirror(self)
53
+ }
54
+ }
55
+
56
+ #[derive(Properties, PartialEq)]
57
+ pub struct MirroredTextareaProps {
58
+ /// `id` of the `<textarea>` itself — consumer CSS and tests key on it.
59
+ pub id: AttrValue,
60
+
61
+ /// The mirror's content: the same characters the textarea holds,
62
+ /// as plain text, or marked up (`CodeEditor` passes highlighted
63
+ /// spans). A stale mirror means a caret that drifts from the glyphs,
64
+ /// so this is re-rendered unconditionally — see [`Mirror`].
65
+ pub mirror: Mirror,
66
+
67
+ /// `id` of the mirror `<pre>`, for consumers that style it directly.
68
+ #[prop_or_default]
69
+ pub mirror_id: Option<AttrValue>,
70
+
71
+ /// Extra classes for the positioned container.
72
+ #[prop_or_default]
73
+ pub class: Classes,
74
+
75
+ #[prop_or_default]
76
+ pub textarea_ref: NodeRef,
77
+
78
+ #[prop_or_default]
79
+ pub mirror_ref: NodeRef,
80
+
81
+ /// The `placeholder` ATTRIBUTE, which exists for assistive tech: it
82
+ /// is painted transparent, because a CSS-`content` label cannot be
83
+ /// written into an attribute and so cannot be localized. The text a
84
+ /// sighted user reads comes from the mirror instead — see
85
+ /// [`Self::is_empty`].
86
+ #[prop_or_default]
87
+ pub placeholder: AttrValue,
88
+
89
+ /// Whether [`Self::mirror`] is empty, which the component cannot see
90
+ /// for itself (the mirror is opaque `Html`). Tags the mirror with
91
+ /// `is-empty` so a consumer's stylesheet can supply placeholder text
92
+ /// through `content`, and therefore through the intl label
93
+ /// mechanism, rather than through the untranslatable attribute.
94
+ #[prop_or_default]
95
+ pub is_empty: bool,
96
+
97
+ #[prop_or_default]
98
+ pub disabled: bool,
99
+
100
+ #[prop_or_default]
101
+ pub oninput: Callback<InputEvent>,
102
+
103
+ #[prop_or_default]
104
+ pub onkeydown: Callback<KeyboardEvent>,
105
+
106
+ #[prop_or_default]
107
+ pub onscroll: Callback<Event>,
108
+
109
+ /// Extra nodes inside the positioned container, e.g. `CodeEditor`'s
110
+ /// minimum-height sizer.
111
+ #[prop_or_default]
112
+ pub children: Children,
113
+ }
114
+
115
+ #[function_component]
116
+ pub fn MirroredTextarea(props: &MirroredTextareaProps) -> Html {
117
+ html! {
118
+ <div class={classes!("mirrored-textarea", props.class.clone())}>
119
+ // `scrollable` is the viewer's webkit scrollbar styling: the
120
+ // input layer scrolls wherever a consumer lets it (the
121
+ // expression editor syncs its `scrollTop` to the mirror and
122
+ // line numbers), and is inert where it does not (the chat
123
+ // prompt, whose wrapper scrolls instead).
124
+ <textarea
125
+ id={props.id.clone()}
126
+ ref={props.textarea_ref.clone()}
127
+ class="mirrored-textarea-input scrollable"
128
+ spellcheck="false"
129
+ placeholder={props.placeholder.clone()}
130
+ disabled={props.disabled}
131
+ oninput={props.oninput.clone()}
132
+ onkeydown={props.onkeydown.clone()}
133
+ onscroll={props.onscroll.clone()}
134
+ />
135
+ { props.children.iter().collect::<Html>() }
136
+ <pre
137
+ id={props.mirror_id.clone()}
138
+ ref={props.mirror_ref.clone()}
139
+ class={classes!(
140
+ "mirrored-textarea-mirror",
141
+ props.is_empty.then_some("is-empty"),
142
+ )}
143
+ >
144
+ { props.mirror.0.clone() }
145
+ // A trailing newline opens a line box in the textarea but
146
+ // not in the `<pre>` (which has no caret to place there),
147
+ // so the mirror would come up one line short exactly when
148
+ // the text is about to overflow. The space forces it.
149
+ { " " }
150
+ </pre>
151
+ </div>
152
+ }
153
+ }
154
+
155
+ #[cfg(test)]
156
+ mod tests {
157
+ use super::*;
158
+
159
+ #[test]
160
+ fn mirror_never_compares_equal() {
161
+ let mirror = Mirror(Html::default());
162
+ assert!(mirror != mirror.clone());
163
+ assert!(Mirror(Html::default()) != Mirror(Html::default()));
164
+ }
165
+ }
@@ -17,13 +17,16 @@
17
17
  //! parent module directly.
18
18
 
19
19
  pub mod code_editor;
20
- pub mod color_range_selector;
21
20
  pub mod color_selector;
22
21
  pub mod debug;
22
+ pub mod mirrored_textarea;
23
+ pub mod multi_stop_gradient_selector;
24
+ pub mod named_value_picker;
23
25
  pub mod number_field;
24
26
  pub mod number_input;
25
27
  pub mod number_range_field;
26
28
  pub mod optional_field;
29
+ pub mod palette_selector;
27
30
  pub mod select_enum_field;
28
31
  pub mod select_value_field;
29
32