@sia.soul/sia-react-ui 0.1.23 → 0.1.24

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 (42) hide show
  1. package/dist/{Select-BoVx-D0D.d.ts → Select-O_yY51vc.d.ts} +4 -2
  2. package/dist/{Select-BO3VyE2n.d.cts → Select-W_xpkpOW.d.cts} +4 -2
  3. package/dist/chart.cjs +1401 -1160
  4. package/dist/chart.css +21 -0
  5. package/dist/chart.js +4 -4
  6. package/dist/{chunk-VX2JECOU.js → chunk-6R7JY7IY.js} +20 -16
  7. package/dist/{chunk-TZGUBGA4.js → chunk-6WBABNAX.js} +4 -3
  8. package/dist/{chunk-OKP4DIQH.js → chunk-GUXAWU63.js} +29 -26
  9. package/dist/{chunk-QJ4OQNQ4.js → chunk-LA7O3PN4.js} +4 -3
  10. package/dist/{chunk-NPB33IXJ.js → chunk-LIYCU2GU.js} +349 -278
  11. package/dist/{chunk-KLI65JVD.js → chunk-LRPQ5FGE.js} +68 -186
  12. package/dist/chunk-NSSPGXNM.js +284 -0
  13. package/dist/{chunk-YT6E3X2K.js → chunk-OH6YILJW.js} +4 -3
  14. package/dist/{chunk-RWGSZW32.js → chunk-RGXVPS37.js} +7 -6
  15. package/dist/{chunk-GM47DAAN.js → chunk-T2QIW4OO.js} +8 -7
  16. package/dist/{core-Cqgl6Z7h.d.ts → core-BOzLwiXG.d.ts} +44 -5
  17. package/dist/{core-DF0pknX6.d.cts → core-tbb9ZiBY.d.cts} +44 -5
  18. package/dist/core.cjs +1637 -1443
  19. package/dist/core.css +52 -0
  20. package/dist/core.d.cts +2 -2
  21. package/dist/core.d.ts +2 -2
  22. package/dist/core.js +8 -6
  23. package/dist/index.cjs +3705 -3505
  24. package/dist/index.css +52 -0
  25. package/dist/index.d.cts +28 -15
  26. package/dist/index.d.ts +28 -15
  27. package/dist/index.js +90 -83
  28. package/dist/markdown-editor.cjs +269 -28
  29. package/dist/markdown-editor.css +20 -0
  30. package/dist/markdown-editor.js +2 -2
  31. package/dist/rich-text-editor.cjs +1156 -1031
  32. package/dist/rich-text-editor.css +20 -0
  33. package/dist/rich-text-editor.js +4 -4
  34. package/dist/{select-date-range-a6nzK7Wu.d.ts → select-date-range-BjqFUeU6.d.ts} +1 -1
  35. package/dist/{select-date-range-IOe6MMqu.d.cts → select-date-range-C9w8z3G8.d.cts} +1 -1
  36. package/dist/select-date-range.cjs +1188 -1059
  37. package/dist/select-date-range.css +20 -0
  38. package/dist/select-date-range.d.cts +2 -2
  39. package/dist/select-date-range.d.ts +2 -2
  40. package/dist/select-date-range.js +5 -5
  41. package/package.json +2 -2
  42. package/dist/chunk-3VJ4CZDQ.js +0 -43
@@ -34,28 +34,269 @@ __export(markdown_editor_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(markdown_editor_exports);
36
36
 
37
- // src/components/shared.ts
37
+ // src/components/titleTooltip.tsx
38
+ var import_client = require("react-dom/client");
39
+
40
+ // src/components/PopupPortal.tsx
38
41
  var import_react = require("react");
39
- var ControlSizeContext = (0, import_react.createContext)("medium");
40
- var PlaceholderModeContext = (0, import_react.createContext)("default");
42
+ var import_react_dom = require("react-dom");
43
+ var import_jsx_runtime = require("react/jsx-runtime");
44
+ function setForwardedRef(ref, value) {
45
+ if (typeof ref === "function") ref(value);
46
+ else if (ref) ref.current = value;
47
+ }
48
+ function clamp(value, min, max) {
49
+ return Math.min(Math.max(value, min), Math.max(min, max));
50
+ }
51
+ var PopupPortal = (0, import_react.forwardRef)(function PopupPortal2({
52
+ anchorRef,
53
+ open = true,
54
+ placement = "bottom-start",
55
+ offset = 6,
56
+ viewportPadding = 8,
57
+ className = "",
58
+ style,
59
+ children,
60
+ ...props
61
+ }, forwardedRef) {
62
+ const popupRef = (0, import_react.useRef)(null);
63
+ const [position, setPosition] = (0, import_react.useState)();
64
+ (0, import_react.useLayoutEffect)(() => {
65
+ if (!open || typeof document === "undefined") {
66
+ setPosition(void 0);
67
+ return;
68
+ }
69
+ const update = () => {
70
+ const anchor = anchorRef.current;
71
+ const popup = popupRef.current;
72
+ if (!anchor || !popup) return;
73
+ const anchorRect = anchor.getBoundingClientRect();
74
+ const popupRect = popup.getBoundingClientRect();
75
+ const viewportWidth = document.documentElement.clientWidth;
76
+ const viewportHeight = document.documentElement.clientHeight;
77
+ let resolvedPlacement = placement;
78
+ const availableBelow = Math.max(0, viewportHeight - viewportPadding - anchorRect.bottom - offset);
79
+ const availableAbove = Math.max(0, anchorRect.top - offset - viewportPadding);
80
+ const availableRight = Math.max(0, viewportWidth - viewportPadding - anchorRect.right - offset);
81
+ const availableLeft = Math.max(0, anchorRect.left - offset - viewportPadding);
82
+ const horizontalSide = placement.startsWith("right") || placement.startsWith("left");
83
+ if (horizontalSide) {
84
+ const prefersRight = placement.startsWith("right");
85
+ const fitsRight = popupRect.width <= availableRight;
86
+ const fitsLeft = popupRect.width <= availableLeft;
87
+ if (prefersRight && !fitsRight && (fitsLeft || availableLeft > availableRight)) {
88
+ resolvedPlacement = placement.replace("right", "left");
89
+ } else if (!prefersRight && !fitsLeft && (fitsRight || availableRight > availableLeft)) {
90
+ resolvedPlacement = placement.replace("left", "right");
91
+ }
92
+ } else {
93
+ const prefersBottom = placement.startsWith("bottom");
94
+ const fitsBelow = popupRect.height <= availableBelow;
95
+ const fitsAbove = popupRect.height <= availableAbove;
96
+ if (prefersBottom && !fitsBelow && (fitsAbove || availableAbove > availableBelow)) {
97
+ resolvedPlacement = placement.replace("bottom", "top");
98
+ } else if (!prefersBottom && !fitsAbove && (fitsBelow || availableBelow > availableAbove)) {
99
+ resolvedPlacement = placement.replace("top", "bottom");
100
+ }
101
+ }
102
+ const resolvedHorizontalSide = resolvedPlacement.startsWith("right") || resolvedPlacement.startsWith("left");
103
+ const top = resolvedHorizontalSide ? resolvedPlacement.endsWith("end") ? anchorRect.bottom - popupRect.height : anchorRect.top : resolvedPlacement.startsWith("bottom") ? anchorRect.bottom + offset : anchorRect.top - popupRect.height - offset;
104
+ const availableHeight = resolvedHorizontalSide ? Math.max(0, viewportHeight - viewportPadding * 2) : resolvedPlacement.startsWith("bottom") ? availableBelow : availableAbove;
105
+ const alignedLeft = resolvedHorizontalSide ? resolvedPlacement.startsWith("right") ? anchorRect.right + offset : anchorRect.left - popupRect.width - offset : resolvedPlacement.endsWith("end") ? anchorRect.right - popupRect.width : anchorRect.left;
106
+ const next = {
107
+ top: clamp(top, viewportPadding, viewportHeight - popupRect.height - viewportPadding),
108
+ left: clamp(alignedLeft, viewportPadding, viewportWidth - popupRect.width - viewportPadding),
109
+ anchorWidth: anchorRect.width,
110
+ availableHeight,
111
+ placement: resolvedPlacement
112
+ };
113
+ setPosition((current) => current && current.top === next.top && current.left === next.left && current.anchorWidth === next.anchorWidth && current.availableHeight === next.availableHeight && current.placement === next.placement ? current : next);
114
+ };
115
+ update();
116
+ window.addEventListener("resize", update);
117
+ window.addEventListener("scroll", update, true);
118
+ const observer = new ResizeObserver(update);
119
+ if (anchorRef.current) observer.observe(anchorRef.current);
120
+ if (popupRef.current) observer.observe(popupRef.current);
121
+ return () => {
122
+ window.removeEventListener("resize", update);
123
+ window.removeEventListener("scroll", update, true);
124
+ observer.disconnect();
125
+ };
126
+ }, [anchorRef, offset, open, placement, viewportPadding]);
127
+ if (!open || typeof document === "undefined") return null;
128
+ return (0, import_react_dom.createPortal)(
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
130
+ "div",
131
+ {
132
+ ref: (node) => {
133
+ popupRef.current = node;
134
+ setForwardedRef(forwardedRef, node);
135
+ },
136
+ className: `sia-popup-portal ${className}`.trim(),
137
+ "data-sia-popup-layer": "",
138
+ "data-placement": position?.placement ?? placement,
139
+ style: {
140
+ ...style,
141
+ "--sia-popup-anchor-width": `${position?.anchorWidth ?? anchorRef.current?.getBoundingClientRect().width ?? 0}px`,
142
+ "--sia-popup-available-height": position ? `${position.availableHeight}px` : void 0,
143
+ top: position?.top ?? 0,
144
+ left: position?.left ?? 0,
145
+ visibility: position ? "visible" : "hidden"
146
+ },
147
+ ...withTitleTooltip(props),
148
+ children
149
+ }
150
+ ),
151
+ document.body
152
+ );
153
+ });
154
+
155
+ // src/components/titleTooltip.tsx
156
+ var import_jsx_runtime2 = require("react/jsx-runtime");
157
+ var documents = /* @__PURE__ */ new WeakSet();
158
+ function withTitleTooltip(props) {
159
+ if (!props || typeof props !== "object" || !("title" in props)) return props;
160
+ const { title, ...rest } = props;
161
+ if (typeof document !== "undefined") installTitleTooltips(document);
162
+ return { ...rest, "data-sia-title": title == null ? void 0 : String(title) };
163
+ }
164
+ function installTitleTooltips(doc) {
165
+ if (documents.has(doc)) return;
166
+ documents.add(doc);
167
+ let hovered = null;
168
+ let focused = null;
169
+ let active = null;
170
+ let pending = null;
171
+ let timer;
172
+ let root;
173
+ let observer;
174
+ let tooltipId = "";
175
+ const candidate = (target) => {
176
+ if (!(target instanceof Element)) return null;
177
+ const element = target.closest("[data-sia-title]");
178
+ return element?.getAttribute("data-sia-title") && !element.closest(".sia-floating") ? element : null;
179
+ };
180
+ const hasExplicitTooltip = (target) => target instanceof Element && Boolean(target.closest(".sia-floating"));
181
+ const hide = () => {
182
+ clearTimeout(timer);
183
+ pending = null;
184
+ observer?.disconnect();
185
+ observer = void 0;
186
+ if (active) {
187
+ const ids = (active.getAttribute("aria-describedby") ?? "").split(/\s+/).filter((id) => id && id !== tooltipId);
188
+ if (ids.length) active.setAttribute("aria-describedby", ids.join(" "));
189
+ else active.removeAttribute("aria-describedby");
190
+ }
191
+ active = null;
192
+ root?.render(null);
193
+ };
194
+ const render = () => {
195
+ const text = active?.getAttribute("data-sia-title");
196
+ if (!active?.isConnected || !text) {
197
+ hide();
198
+ return;
199
+ }
200
+ if (!root) {
201
+ const host = doc.createElement("div");
202
+ host.setAttribute("data-sia-title-tooltip-host", "");
203
+ doc.body.append(host);
204
+ root = (0, import_client.createRoot)(host);
205
+ tooltipId = `sia-title-tooltip-${Math.random().toString(36).slice(2)}`;
206
+ }
207
+ const ids = new Set((active.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(Boolean));
208
+ ids.add(tooltipId);
209
+ active.setAttribute("aria-describedby", [...ids].join(" "));
210
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
211
+ PopupPortal,
212
+ {
213
+ anchorRef: { current: active },
214
+ placement: "top-start",
215
+ id: tooltipId,
216
+ role: "tooltip",
217
+ className: "sia-title-tooltip",
218
+ children: text
219
+ }
220
+ ));
221
+ };
222
+ const show = (element) => {
223
+ if (!element && !active && !pending) return;
224
+ if (element && (element === active || element === pending)) return;
225
+ hide();
226
+ if (!element) return;
227
+ pending = element;
228
+ timer = setTimeout(() => {
229
+ pending = null;
230
+ if (!element.isConnected) return;
231
+ active = element;
232
+ render();
233
+ if (!active) return;
234
+ observer = new MutationObserver(() => {
235
+ if (!active?.isConnected) hide();
236
+ else if (active.getAttribute("data-sia-title") !== doc.getElementById(tooltipId)?.textContent) render();
237
+ });
238
+ observer.observe(doc.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["data-sia-title"] });
239
+ }, 250);
240
+ };
241
+ doc.addEventListener("pointerover", (event) => {
242
+ if (event.pointerType === "touch") return;
243
+ hovered = candidate(event.target);
244
+ if (hasExplicitTooltip(event.target)) hide();
245
+ else show(hovered ?? focused);
246
+ }, true);
247
+ doc.addEventListener("pointermove", (event) => {
248
+ if (event.pointerType === "touch" || event.buttons) return;
249
+ hovered = candidate(event.target);
250
+ if (!hasExplicitTooltip(event.target)) show(hovered ?? focused);
251
+ }, true);
252
+ doc.addEventListener("pointerout", (event) => {
253
+ if (event.pointerType === "touch") return;
254
+ hovered = candidate(event.relatedTarget);
255
+ if (hasExplicitTooltip(event.relatedTarget)) hide();
256
+ else show(hovered ?? focused);
257
+ }, true);
258
+ doc.addEventListener("focusin", (event) => {
259
+ focused = candidate(event.target);
260
+ if (hasExplicitTooltip(event.target)) hide();
261
+ else show(focused ?? hovered);
262
+ }, true);
263
+ doc.addEventListener("focusout", (event) => {
264
+ focused = candidate(event.relatedTarget);
265
+ show(hovered ?? focused);
266
+ }, true);
267
+ doc.addEventListener("pointerdown", hide, true);
268
+ doc.addEventListener("keydown", (event) => {
269
+ if (event.key === "Escape") hide();
270
+ }, true);
271
+ doc.addEventListener("scroll", hide, true);
272
+ doc.defaultView?.addEventListener("blur", () => {
273
+ hovered = focused = null;
274
+ hide();
275
+ });
276
+ }
277
+
278
+ // src/components/shared.ts
279
+ var import_react2 = require("react");
280
+ var ControlSizeContext = (0, import_react2.createContext)("medium");
281
+ var PlaceholderModeContext = (0, import_react2.createContext)("default");
41
282
  function usePlaceholderMode(mode) {
42
- const inherited = (0, import_react.useContext)(PlaceholderModeContext);
283
+ const inherited = (0, import_react2.useContext)(PlaceholderModeContext);
43
284
  return mode ?? inherited;
44
285
  }
45
286
 
46
287
  // src/components/FloatingPlaceholder.tsx
47
- var import_jsx_runtime = require("react/jsx-runtime");
288
+ var import_jsx_runtime3 = require("react/jsx-runtime");
48
289
  function useFloatingPlaceholder({ mode, placeholder, filled, active = false }) {
49
290
  const enabled = usePlaceholderMode(mode) === "floating" && Boolean(placeholder);
50
291
  return {
51
292
  enabled,
52
293
  className: enabled ? ` sia-placeholder-floating${filled ? " sia-placeholder-floating--filled" : ""}${active ? " sia-placeholder-floating--active" : ""}` : "",
53
- label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
294
+ label: enabled ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "sia-floating-placeholder", "aria-hidden": "true", children: placeholder }) : null
54
295
  };
55
296
  }
56
297
 
57
298
  // src/components/MarkdownEditor.tsx
58
- var import_react2 = require("react");
299
+ var import_react3 = require("react");
59
300
  var import_dist = require("vditor/dist/index.css");
60
301
 
61
302
  // src/components/markdownPaste.ts
@@ -164,7 +405,7 @@ var VDITOR_ZH_CN = {
164
405
  };
165
406
 
166
407
  // src/components/MarkdownEditor.tsx
167
- var import_jsx_runtime2 = require("react/jsx-runtime");
408
+ var import_jsx_runtime4 = require("react/jsx-runtime");
168
409
  var vditorLoaders = /* @__PURE__ */ new Map();
169
410
  var BASE_TOOLBAR = [
170
411
  "headings",
@@ -282,21 +523,21 @@ function MarkdownEditor({
282
523
  style,
283
524
  ...props
284
525
  }) {
285
- const mountRef = (0, import_react2.useRef)(null);
286
- const editorRef = (0, import_react2.useRef)(null);
287
- const onChangeRef = (0, import_react2.useRef)(onChange);
288
- const saveShortcutRef = (0, import_react2.useRef)(saveShortcut);
289
- const uploadImageRef = (0, import_react2.useRef)(onUploadImage);
290
- const disabledRef = (0, import_react2.useRef)(disabled);
291
- const smartMarkdownPasteRef = (0, import_react2.useRef)(smartMarkdownPaste);
292
- const valueRef = (0, import_react2.useRef)(value ?? defaultValue);
293
- const lastEmittedValueRef = (0, import_react2.useRef)(value ?? defaultValue);
294
- const [hasContent, setHasContent] = (0, import_react2.useState)(Boolean((value ?? defaultValue).trim()));
526
+ const mountRef = (0, import_react3.useRef)(null);
527
+ const editorRef = (0, import_react3.useRef)(null);
528
+ const onChangeRef = (0, import_react3.useRef)(onChange);
529
+ const saveShortcutRef = (0, import_react3.useRef)(saveShortcut);
530
+ const uploadImageRef = (0, import_react3.useRef)(onUploadImage);
531
+ const disabledRef = (0, import_react3.useRef)(disabled);
532
+ const smartMarkdownPasteRef = (0, import_react3.useRef)(smartMarkdownPaste);
533
+ const valueRef = (0, import_react3.useRef)(value ?? defaultValue);
534
+ const lastEmittedValueRef = (0, import_react3.useRef)(value ?? defaultValue);
535
+ const [hasContent, setHasContent] = (0, import_react3.useState)(Boolean((value ?? defaultValue).trim()));
295
536
  const floating = useFloatingPlaceholder({ mode: placeholderMode, placeholder, filled: hasContent });
296
- const [ready, setReady] = (0, import_react2.useState)(false);
297
- const [loadError, setLoadError] = (0, import_react2.useState)("");
537
+ const [ready, setReady] = (0, import_react3.useState)(false);
538
+ const [loadError, setLoadError] = (0, import_react3.useState)("");
298
539
  const hasImageUpload = Boolean(onUploadImage);
299
- (0, import_react2.useEffect)(() => {
540
+ (0, import_react3.useEffect)(() => {
300
541
  if (value !== void 0) setHasContent(Boolean(value.trim()));
301
542
  }, [value]);
302
543
  onChangeRef.current = onChange;
@@ -305,7 +546,7 @@ function MarkdownEditor({
305
546
  disabledRef.current = disabled;
306
547
  smartMarkdownPasteRef.current = smartMarkdownPaste;
307
548
  valueRef.current = value ?? valueRef.current;
308
- (0, import_react2.useEffect)(() => {
549
+ (0, import_react3.useEffect)(() => {
309
550
  const mount = mountRef.current;
310
551
  if (!mount) return;
311
552
  let disposed = false;
@@ -407,14 +648,14 @@ function MarkdownEditor({
407
648
  if (editorInitialized) createdEditor?.destroy();
408
649
  };
409
650
  }, [ariaLabel, assetBaseUrl, hasImageUpload, minHeight, mode, placeholder, floating.enabled]);
410
- (0, import_react2.useEffect)(() => {
651
+ (0, import_react3.useEffect)(() => {
411
652
  const editor = editorRef.current;
412
653
  if (!editor || value === void 0) return;
413
654
  if (value === lastEmittedValueRef.current || editor.getValue() === value) return;
414
655
  lastEmittedValueRef.current = value;
415
656
  editor.setValue(value, true);
416
657
  }, [ready, value]);
417
- (0, import_react2.useEffect)(() => {
658
+ (0, import_react3.useEffect)(() => {
418
659
  const editor = editorRef.current;
419
660
  if (!editor) return;
420
661
  if (disabled) editor.disabled();
@@ -424,13 +665,13 @@ function MarkdownEditor({
424
665
  ...style,
425
666
  "--sia-markdown-editor-min-height": typeof minHeight === "number" ? `${minHeight}px` : minHeight
426
667
  };
427
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
668
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
428
669
  "div",
429
670
  {
430
671
  className: ["sia-markdown-editor", floating.className, disabled ? "is-disabled" : "", className].filter(Boolean).join(" "),
431
672
  style: editorStyle,
432
673
  "data-ready": ready ? "true" : "false",
433
- ...props,
674
+ ...withTitleTooltip(props),
434
675
  onBlurCapture: (event) => {
435
676
  props.onBlurCapture?.(event);
436
677
  const current = editorRef.current?.getValue();
@@ -438,8 +679,8 @@ function MarkdownEditor({
438
679
  },
439
680
  children: [
440
681
  floating.label,
441
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: mountRef, className: "sia-markdown-editor__mount" }),
442
- !ready ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "sia-markdown-editor__loading", "aria-live": "polite", children: loadError || "\u6B63\u5728\u52A0\u8F7D\u7F16\u8F91\u5668\u2026" }) : null
682
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: mountRef, className: "sia-markdown-editor__mount" }),
683
+ !ready ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "sia-markdown-editor__loading", "aria-live": "polite", children: loadError || "\u6B63\u5728\u52A0\u8F7D\u7F16\u8F91\u5668\u2026" }) : null
443
684
  ]
444
685
  }
445
686
  );
@@ -0,0 +1,20 @@
1
+ /* src/title-tooltip.css */
2
+ body > .sia-title-tooltip[data-sia-popup-layer] {
3
+ position: fixed;
4
+ z-index: 1800;
5
+ display: block;
6
+ box-sizing: border-box;
7
+ width: max-content;
8
+ max-width: min(320px, calc(100vw - 16px));
9
+ max-height: calc(100vh - 16px);
10
+ padding: 7px 10px;
11
+ color: var(--sia-color-text, #1f2329);
12
+ background: var(--sia-color-surface, #fff);
13
+ border: 1px solid var(--sia-color-border, #d9d9d9);
14
+ border-radius: var(--sia-radius, 6px);
15
+ box-shadow: var(--sia-shadow-popup, 0 4px 16px rgb(0 0 0 / 12%));
16
+ font: 400 13px/1.5 system-ui, sans-serif;
17
+ pointer-events: none;
18
+ overflow-wrap: anywhere;
19
+ white-space: pre-wrap;
20
+ }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MarkdownEditor
3
- } from "./chunk-QJ4OQNQ4.js";
4
- import "./chunk-3VJ4CZDQ.js";
3
+ } from "./chunk-LA7O3PN4.js";
4
+ import "./chunk-NSSPGXNM.js";
5
5
  export {
6
6
  MarkdownEditor
7
7
  };