@bpmn-io/properties-panel 3.40.6 → 3.41.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.
@@ -569,6 +569,42 @@ textarea.bio-properties-panel-input {
569
569
  resize: vertical;
570
570
  }
571
571
 
572
+ /**
573
+ * JSON Editor (CodeMirror)
574
+ */
575
+
576
+ .bio-properties-panel-json-editor .cm-editor {
577
+ border: 1px solid var(--input-border-color);
578
+ border-radius: 2px;
579
+ background-color: var(--input-background-color);
580
+ font-size: var(--text-size-base);
581
+ font-family: var(--font-family-monospace);
582
+ }
583
+
584
+ .bio-properties-panel-json-editor .cm-editor.cm-focused {
585
+ outline: none;
586
+ background-color: var(--input-focus-background-color);
587
+ border-color: var(--input-focus-border-color);
588
+ }
589
+
590
+ .bio-properties-panel-json-editor .cm-scroller {
591
+ overflow: auto;
592
+ max-height: 215px;
593
+ }
594
+
595
+ .bio-properties-panel-json-editor .cm-content {
596
+ padding-right: 18px;
597
+ }
598
+
599
+ .bio-properties-panel-entry.has-error .bio-properties-panel-json-editor .cm-editor {
600
+ border-color: var(--input-error-border-color);
601
+ background-color: var(--input-error-background-color);
602
+ }
603
+
604
+ .bio-properties-panel-entry.has-error .bio-properties-panel-json-editor .cm-editor.cm-focused {
605
+ border-color: var(--input-error-focus-border-color);
606
+ }
607
+
572
608
  .bio-properties-panel-entry.has-error .bio-properties-panel-input,
573
609
  .bio-properties-panel-entry.has-error .bio-properties-panel-feel-editor__open-popup-placeholder {
574
610
  border-color: var(--input-error-border-color);
package/dist/index.esm.js CHANGED
@@ -1,13 +1,19 @@
1
1
  import { useContext, useState, useRef, useEffect, useLayoutEffect, useCallback, useMemo } from '../preact/hooks';
2
- import { isFunction, isArray, get, assign, set, isString, isNumber, debounce } from 'min-dash';
2
+ import { isFunction, isArray, get, assign, set, isObject, isString, isNumber, debounce } from 'min-dash';
3
3
  import { createPortal, forwardRef } from '../preact/compat';
4
4
  import { jsx, jsxs, Fragment } from '../preact/jsx-runtime';
5
5
  import { createContext, createElement as createElement$1, render } from '../preact';
6
6
  import classnames from 'classnames';
7
7
  import { query, domify } from 'min-dom';
8
+ import { EditorView, highlightSpecialChars, drawSelection, keymap, placeholder, lineNumbers } from '@codemirror/view';
9
+ import { Annotation, EditorState } from '@codemirror/state';
10
+ import { syntaxHighlighting, foldGutter, bracketMatching, defaultHighlightStyle, foldKeymap } from '@codemirror/language';
11
+ import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
12
+ import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
13
+ import { jsonParseLinter, json } from '@codemirror/lang-json';
14
+ import { linter } from '@codemirror/lint';
8
15
  import { FeelersEditor } from 'feelers';
9
16
  import Editor from '@bpmn-io/feel-editor';
10
- import { lineNumbers, EditorView } from '@codemirror/view';
11
17
  import * as focusTrap from 'focus-trap';
12
18
 
13
19
  var ArrowIcon = function ArrowIcon(props) {
@@ -1739,7 +1745,7 @@ function CheckboxEntry(props) {
1739
1745
  })]
1740
1746
  });
1741
1747
  }
1742
- function isEdited$8(node) {
1748
+ function isEdited$9(node) {
1743
1749
  return node && !!node.checked;
1744
1750
  }
1745
1751
 
@@ -1816,6 +1822,200 @@ function CheckboxGroup(props) {
1816
1822
  });
1817
1823
  }
1818
1824
 
1825
+ const ExternalChange = Annotation.define();
1826
+ const parseJsonLinter = jsonParseLinter();
1827
+
1828
+ /**
1829
+ * A CodeMirror based JSON editor for the properties panel.
1830
+ *
1831
+ * @param {object} props
1832
+ * @param {string} props.id
1833
+ * @param {string} props.label
1834
+ * @param {string} [props.value]
1835
+ * @param {Function} props.onInput
1836
+ * @param {boolean} [props.disabled]
1837
+ * @param {string} [props.placeholder]
1838
+ * @param {string} [props.tooltip]
1839
+ * @param {object} [props.element]
1840
+ */
1841
+ function JsonEditor(props) {
1842
+ const {
1843
+ id,
1844
+ label,
1845
+ debounce,
1846
+ value = '',
1847
+ onInput: commitValue,
1848
+ disabled,
1849
+ placeholder: placeholder$1,
1850
+ tooltip,
1851
+ element
1852
+ } = props;
1853
+ const containerRef = useRef(null);
1854
+ const viewRef = useRef(null);
1855
+ const editorRef = useShowEntryEvent(id);
1856
+ const onInput = useStaticCallback(newValue => {
1857
+ commitValue(newValue === '' ? undefined : newValue);
1858
+ });
1859
+ const handleChange = useDebounce(onInput, debounce);
1860
+ useEffect(() => {
1861
+ const view = new EditorView({
1862
+ state: EditorState.create({
1863
+ doc: value,
1864
+ extensions: [highlightSpecialChars(), history(), drawSelection(), syntaxHighlighting(defaultHighlightStyle, {
1865
+ fallback: true
1866
+ }), foldGutter(), bracketMatching(), closeBrackets(), keymap.of([...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap, ...foldKeymap]), EditorState.tabSize.of(2), EditorView.lineWrapping, EditorState.readOnly.of(!!disabled), ...(placeholder$1 ? [placeholder(placeholder$1)] : []), EditorView.updateListener.of(update => {
1867
+ const isExternal = update.transactions.some(tr => tr.annotation(ExternalChange));
1868
+ if (update.docChanged && !isExternal) {
1869
+ handleChange(update.state.doc.toString());
1870
+ }
1871
+ }), json(), linter(view => {
1872
+ const content = view.state.doc.toString();
1873
+ if (!content.trim()) return [];
1874
+ return parseJsonLinter(view);
1875
+ }, {
1876
+ delay: 300
1877
+ })]
1878
+ }),
1879
+ parent: containerRef.current
1880
+ });
1881
+ viewRef.current = view;
1882
+
1883
+ // Allow useShowEntryEvent to focus the editor
1884
+ editorRef.current = view.contentDOM;
1885
+ const prefixedId = `bio-properties-panel-${id}`;
1886
+ view.contentDOM.setAttribute('id', prefixedId);
1887
+ view.contentDOM.setAttribute('aria-label', label);
1888
+ return () => {
1889
+ view.destroy();
1890
+ };
1891
+ }, []);
1892
+
1893
+ // Sync external value changes into the editor
1894
+ useEffect(() => {
1895
+ const view = viewRef.current;
1896
+ if (!view) return;
1897
+ const currentValue = view.state.doc.toString();
1898
+ if (value === currentValue) return;
1899
+ view.dispatch({
1900
+ changes: {
1901
+ from: 0,
1902
+ to: currentValue.length,
1903
+ insert: value || ''
1904
+ },
1905
+ annotations: ExternalChange.of(true)
1906
+ });
1907
+ }, [value]);
1908
+
1909
+ // Focus editor on label click manually, as the native `for`
1910
+ // attribute does not work with contenteditable elements
1911
+ const focusEditor = () => {
1912
+ viewRef.current && viewRef.current.focus();
1913
+ };
1914
+ return jsxs("div", {
1915
+ class: "bio-properties-panel-json-editor",
1916
+ children: [jsx("label", {
1917
+ class: "bio-properties-panel-label",
1918
+ onClick: focusEditor,
1919
+ children: jsx(TooltipWrapper, {
1920
+ value: tooltip,
1921
+ forId: id,
1922
+ element: element,
1923
+ children: label
1924
+ })
1925
+ }), jsx("div", {
1926
+ ref: containerRef
1927
+ })]
1928
+ });
1929
+ }
1930
+
1931
+ /**
1932
+ * Entry wrapper for JsonEditor, handles getValue/setValue, built-in JSON validation, error display, and description.
1933
+ *
1934
+ * @param {object} props
1935
+ * @param {object} props.element
1936
+ * @param {string} props.id
1937
+ * @param {string} [props.description]
1938
+ * @param {string} props.label
1939
+ * @param {Function} props.debounce
1940
+ * @param {Function} props.getValue
1941
+ * @param {Function} props.setValue
1942
+ * @param {boolean} [props.disabled]
1943
+ * @param {string} [props.placeholder]
1944
+ * @param {string} [props.tooltip]
1945
+ */
1946
+ function JsonEditorEntry(props) {
1947
+ const {
1948
+ element,
1949
+ id,
1950
+ description,
1951
+ debounce,
1952
+ label,
1953
+ getValue,
1954
+ setValue,
1955
+ disabled,
1956
+ placeholder,
1957
+ tooltip
1958
+ } = props;
1959
+ const globalError = useError(id);
1960
+ let value = getValue(element);
1961
+ const [editorValue, setEditorValue] = useState(value);
1962
+ useEffect(() => {
1963
+ if (value === editorValue) {
1964
+ return;
1965
+ }
1966
+ setEditorValue(value);
1967
+ }, [value]);
1968
+ const onInput = useStaticCallback(newValue => {
1969
+ setEditorValue(newValue);
1970
+ const currentValue = getValue(element);
1971
+ if (newValue !== currentValue) {
1972
+ setValue(newValue);
1973
+ }
1974
+ });
1975
+ const error = globalError || validateJson(editorValue);
1976
+ return jsxs("div", {
1977
+ class: classnames('bio-properties-panel-entry', error && 'has-error'),
1978
+ "data-entry-id": id,
1979
+ children: [jsx(JsonEditor, {
1980
+ id: id,
1981
+ label: label,
1982
+ debounce: debounce,
1983
+ value: value,
1984
+ onInput: onInput,
1985
+ disabled: disabled,
1986
+ placeholder: placeholder,
1987
+ tooltip: tooltip,
1988
+ element: element
1989
+ }, element), error && jsx("div", {
1990
+ class: "bio-properties-panel-error",
1991
+ children: error
1992
+ }), jsx(Description, {
1993
+ forId: id,
1994
+ element: element,
1995
+ value: description
1996
+ })]
1997
+ });
1998
+ }
1999
+
2000
+ /**
2001
+ * Check if the JSON editor entry has been edited.
2002
+ */
2003
+ function isEdited$8(node) {
2004
+ const cmContent = node && node.querySelector('.cm-content');
2005
+ return cmContent ? cmContent.textContent.trim().length > 0 : false;
2006
+ }
2007
+
2008
+ // helpers /////////////////
2009
+
2010
+ function validateJson(value) {
2011
+ if (!value || !value.trim()) return null;
2012
+ try {
2013
+ return isObject(JSON.parse(value)) ? null : 'JSON contains errors';
2014
+ } catch (e) {
2015
+ return 'JSON contains errors';
2016
+ }
2017
+ }
2018
+
1819
2019
  /**
1820
2020
  * Button to open popups.
1821
2021
  *
@@ -4899,5 +5099,5 @@ var index = {
4899
5099
  feelPopupRenderer: ['type', FeelPopupRenderer]
4900
5100
  };
4901
5101
 
4902
- export { ArrowIcon, CheckboxEntry, CheckboxGroup, CloseIcon, CollapsibleEntry, CreateIcon, index$1 as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DragIcon, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelCheckboxEntry, FeelEntry, FeelIcon$1 as FeelIcon, FeelLanguageContext, FeelNumberEntry, index as FeelPopupModule, FeelTemplatingEntry, FeelTextAreaEntry, FeelToggleSwitchEntry, Group, Header, HeaderButton, LaunchIcon, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, OpenPopupIcon, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TemplatingEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, TooltipContext, TooltipWrapper as TooltipEntry, isEdited$8 as isCheckboxEntryEdited, isEdited$5 as isFeelEntryEdited, isEdited$6 as isNumberFieldEntryEdited, isEdited$3 as isSelectEntryEdited, isEdited$2 as isSimpleEntryEdited, isEdited$4 as isTemplatingEntryEdited, isEdited$1 as isTextAreaEntryEdited, isEdited as isTextFieldEntryEdited, isEdited$7 as isToggleSwitchEntryEdited, useDebounce, useDescriptionContext, useElementVisible, useError, useErrors, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver, useTooltipContext };
5102
+ export { ArrowIcon, CheckboxEntry, CheckboxGroup, CloseIcon, CollapsibleEntry, CreateIcon, index$1 as DebounceInputModule, DeleteIcon, DescriptionContext, Description as DescriptionEntry, DragIcon, DropdownButton, ErrorsContext, EventContext, ExternalLinkIcon, FeelCheckboxEntry, FeelEntry, FeelIcon$1 as FeelIcon, FeelLanguageContext, FeelNumberEntry, index as FeelPopupModule, FeelTemplatingEntry, FeelTextAreaEntry, FeelToggleSwitchEntry, Group, Header, HeaderButton, JsonEditorEntry, LaunchIcon, LayoutContext, List as ListEntry, ListGroup, ListItem, NumberFieldEntry, OpenPopupIcon, Placeholder, PropertiesPanel, LayoutContext as PropertiesPanelContext, SelectEntry, Simple as SimpleEntry, TemplatingEntry, TextAreaEntry, TextfieldEntry as TextFieldEntry, ToggleSwitchEntry, TooltipContext, TooltipWrapper as TooltipEntry, isEdited$9 as isCheckboxEntryEdited, isEdited$5 as isFeelEntryEdited, isEdited$8 as isJsonEditorEntryEdited, isEdited$6 as isNumberFieldEntryEdited, isEdited$3 as isSelectEntryEdited, isEdited$2 as isSimpleEntryEdited, isEdited$4 as isTemplatingEntryEdited, isEdited$1 as isTextAreaEntryEdited, isEdited as isTextFieldEntryEdited, isEdited$7 as isToggleSwitchEntryEdited, useDebounce, useDescriptionContext, useElementVisible, useError, useErrors, useEvent, useKeyFactory, useLayoutState, usePrevious, useShowEntryEvent, useStaticCallback, useStickyIntersectionObserver, useTooltipContext };
4903
5103
  //# sourceMappingURL=index.esm.js.map