@bpmn-io/properties-panel 3.40.5 → 3.41.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.
@@ -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 { json, jsonParseLinter } 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,195 @@ function CheckboxGroup(props) {
1816
1822
  });
1817
1823
  }
1818
1824
 
1825
+ const ExternalChange = Annotation.define();
1826
+
1827
+ /**
1828
+ * A CodeMirror based JSON editor for the properties panel.
1829
+ *
1830
+ * @param {object} props
1831
+ * @param {string} props.id
1832
+ * @param {string} props.label
1833
+ * @param {string} [props.value]
1834
+ * @param {Function} props.onInput
1835
+ * @param {boolean} [props.disabled]
1836
+ * @param {string} [props.placeholder]
1837
+ * @param {string} [props.tooltip]
1838
+ * @param {object} [props.element]
1839
+ */
1840
+ function JsonEditor(props) {
1841
+ const {
1842
+ id,
1843
+ label,
1844
+ debounce,
1845
+ value = '',
1846
+ onInput: commitValue,
1847
+ disabled,
1848
+ placeholder: placeholder$1,
1849
+ tooltip,
1850
+ element
1851
+ } = props;
1852
+ const containerRef = useRef(null);
1853
+ const viewRef = useRef(null);
1854
+ const editorRef = useShowEntryEvent(id);
1855
+ const onInput = useStaticCallback(newValue => {
1856
+ commitValue(newValue === '' ? undefined : newValue);
1857
+ });
1858
+ const handleChange = useDebounce(onInput, debounce);
1859
+ useEffect(() => {
1860
+ const view = new EditorView({
1861
+ state: EditorState.create({
1862
+ doc: value,
1863
+ extensions: [highlightSpecialChars(), history(), drawSelection(), syntaxHighlighting(defaultHighlightStyle, {
1864
+ fallback: true
1865
+ }), 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 => {
1866
+ const isExternal = update.transactions.some(tr => tr.annotation(ExternalChange));
1867
+ if (update.docChanged && !isExternal) {
1868
+ handleChange(update.state.doc.toString());
1869
+ }
1870
+ }), json(), linter(jsonParseLinter(), {
1871
+ delay: 300
1872
+ })]
1873
+ }),
1874
+ parent: containerRef.current
1875
+ });
1876
+ viewRef.current = view;
1877
+
1878
+ // Allow useShowEntryEvent to focus the editor
1879
+ editorRef.current = view.contentDOM;
1880
+ const prefixedId = `bio-properties-panel-${id}`;
1881
+ view.contentDOM.setAttribute('id', prefixedId);
1882
+ view.contentDOM.setAttribute('aria-label', label);
1883
+ return () => {
1884
+ view.destroy();
1885
+ };
1886
+ }, []);
1887
+
1888
+ // Sync external value changes into the editor
1889
+ useEffect(() => {
1890
+ const view = viewRef.current;
1891
+ if (!view) return;
1892
+ const currentValue = view.state.doc.toString();
1893
+ if (value === currentValue) return;
1894
+ view.dispatch({
1895
+ changes: {
1896
+ from: 0,
1897
+ to: currentValue.length,
1898
+ insert: value || ''
1899
+ },
1900
+ annotations: ExternalChange.of(true)
1901
+ });
1902
+ }, [value]);
1903
+
1904
+ // Focus editor on label click manually, as the native `for`
1905
+ // attribute does not work with contenteditable elements
1906
+ const focusEditor = () => {
1907
+ viewRef.current && viewRef.current.focus();
1908
+ };
1909
+ return jsxs("div", {
1910
+ class: "bio-properties-panel-json-editor",
1911
+ children: [jsx("label", {
1912
+ class: "bio-properties-panel-label",
1913
+ onClick: focusEditor,
1914
+ children: jsx(TooltipWrapper, {
1915
+ value: tooltip,
1916
+ forId: id,
1917
+ element: element,
1918
+ children: label
1919
+ })
1920
+ }), jsx("div", {
1921
+ ref: containerRef
1922
+ })]
1923
+ });
1924
+ }
1925
+
1926
+ /**
1927
+ * Entry wrapper for JsonEditor, handles getValue/setValue, built-in JSON validation, error display, and description.
1928
+ *
1929
+ * @param {object} props
1930
+ * @param {object} props.element
1931
+ * @param {string} props.id
1932
+ * @param {string} [props.description]
1933
+ * @param {string} props.label
1934
+ * @param {Function} props.debounce
1935
+ * @param {Function} props.getValue
1936
+ * @param {Function} props.setValue
1937
+ * @param {boolean} [props.disabled]
1938
+ * @param {string} [props.placeholder]
1939
+ * @param {string} [props.tooltip]
1940
+ */
1941
+ function JsonEditorEntry(props) {
1942
+ const {
1943
+ element,
1944
+ id,
1945
+ description,
1946
+ debounce,
1947
+ label,
1948
+ getValue,
1949
+ setValue,
1950
+ disabled,
1951
+ placeholder,
1952
+ tooltip
1953
+ } = props;
1954
+ const globalError = useError(id);
1955
+ let value = getValue(element);
1956
+ const [editorValue, setEditorValue] = useState(value);
1957
+ useEffect(() => {
1958
+ if (value === editorValue) {
1959
+ return;
1960
+ }
1961
+ setEditorValue(value);
1962
+ }, [value]);
1963
+ const onInput = useStaticCallback(newValue => {
1964
+ setEditorValue(newValue);
1965
+ const currentValue = getValue(element);
1966
+ if (newValue !== currentValue) {
1967
+ setValue(newValue);
1968
+ }
1969
+ });
1970
+ const error = globalError || validateJson(editorValue);
1971
+ return jsxs("div", {
1972
+ class: classnames('bio-properties-panel-entry', error && 'has-error'),
1973
+ "data-entry-id": id,
1974
+ children: [jsx(JsonEditor, {
1975
+ id: id,
1976
+ label: label,
1977
+ debounce: debounce,
1978
+ value: value,
1979
+ onInput: onInput,
1980
+ disabled: disabled,
1981
+ placeholder: placeholder,
1982
+ tooltip: tooltip,
1983
+ element: element
1984
+ }, element), error && jsx("div", {
1985
+ class: "bio-properties-panel-error",
1986
+ children: error
1987
+ }), jsx(Description, {
1988
+ forId: id,
1989
+ element: element,
1990
+ value: description
1991
+ })]
1992
+ });
1993
+ }
1994
+
1995
+ /**
1996
+ * Check if the JSON editor entry has been edited.
1997
+ */
1998
+ function isEdited$8(node) {
1999
+ const cmContent = node && node.querySelector('.cm-content');
2000
+ return cmContent ? cmContent.textContent.trim().length > 0 : false;
2001
+ }
2002
+
2003
+ // helpers /////////////////
2004
+
2005
+ function validateJson(value) {
2006
+ if (!value) return null;
2007
+ try {
2008
+ return isObject(JSON.parse(value)) ? null : 'JSON contains errors';
2009
+ } catch (e) {
2010
+ return 'JSON contains errors';
2011
+ }
2012
+ }
2013
+
1819
2014
  /**
1820
2015
  * Button to open popups.
1821
2016
  *
@@ -2024,6 +2219,7 @@ const FeelEditor = forwardRef((props, ref) => {
2024
2219
  onKeyDown: onKeyDown,
2025
2220
  onLint: onLint,
2026
2221
  placeholder: placeholder,
2222
+ readOnly: disabled,
2027
2223
  tooltipContainer: tooltipContainer,
2028
2224
  value: localValue,
2029
2225
  variables,
@@ -4898,5 +5094,5 @@ var index = {
4898
5094
  feelPopupRenderer: ['type', FeelPopupRenderer]
4899
5095
  };
4900
5096
 
4901
- 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 };
5097
+ 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 };
4902
5098
  //# sourceMappingURL=index.esm.js.map