goblin-magic 1.0.3

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 (88) hide show
  1. package/.editorconfig +9 -0
  2. package/.zou-flow +2 -0
  3. package/eslint.config.js +65 -0
  4. package/magicNavigation.js +7 -0
  5. package/package.json +45 -0
  6. package/widgets/dialog/widget.js +78 -0
  7. package/widgets/element-helpers/element-has-direct-text.js +9 -0
  8. package/widgets/element-helpers/is-empty-area-element.js +17 -0
  9. package/widgets/element-helpers/is-flat-element.js +52 -0
  10. package/widgets/get-modifiers/get-modifiers.js +34 -0
  11. package/widgets/input-group/styles.js +74 -0
  12. package/widgets/input-group/widget.js +24 -0
  13. package/widgets/magic-action/styles.js +45 -0
  14. package/widgets/magic-action/widget.js +44 -0
  15. package/widgets/magic-background/bg-alps.jpg +0 -0
  16. package/widgets/magic-background/bg-fur.png +0 -0
  17. package/widgets/magic-background/bg-milkyway.png +0 -0
  18. package/widgets/magic-background/bg-space.jpg +0 -0
  19. package/widgets/magic-background/bg-synth.jpg +0 -0
  20. package/widgets/magic-background/bg-white.png +0 -0
  21. package/widgets/magic-background/styles.js +81 -0
  22. package/widgets/magic-background/widget.js +20 -0
  23. package/widgets/magic-box/styles.js +10 -0
  24. package/widgets/magic-box/widget.js +28 -0
  25. package/widgets/magic-box-old/styles.js +111 -0
  26. package/widgets/magic-box-old/widget.js +30 -0
  27. package/widgets/magic-button/styles.js +156 -0
  28. package/widgets/magic-button/widget.js +89 -0
  29. package/widgets/magic-checkbox/styles.js +116 -0
  30. package/widgets/magic-checkbox/widget.js +68 -0
  31. package/widgets/magic-color-field/styles.js +22 -0
  32. package/widgets/magic-color-field/widget.js +68 -0
  33. package/widgets/magic-date-field/styles.js +9 -0
  34. package/widgets/magic-date-field/widget.js +145 -0
  35. package/widgets/magic-datetime-field/styles.js +11 -0
  36. package/widgets/magic-datetime-field/widget.js +95 -0
  37. package/widgets/magic-dialog/styles.js +39 -0
  38. package/widgets/magic-dialog/widget.js +116 -0
  39. package/widgets/magic-div/styles.js +22 -0
  40. package/widgets/magic-div/widget.js +20 -0
  41. package/widgets/magic-emoji/styles.js +14 -0
  42. package/widgets/magic-emoji/widget.js +33 -0
  43. package/widgets/magic-emoji-picker/styles.js +21 -0
  44. package/widgets/magic-emoji-picker/widget.js +44 -0
  45. package/widgets/magic-inplace-input/styles.js +55 -0
  46. package/widgets/magic-inplace-input/widget.js +26 -0
  47. package/widgets/magic-input/styles.js +50 -0
  48. package/widgets/magic-input/widget.js +397 -0
  49. package/widgets/magic-label/styles.js +20 -0
  50. package/widgets/magic-label/widget.js +24 -0
  51. package/widgets/magic-navigation/service.js +1306 -0
  52. package/widgets/magic-navigation/styles.js +103 -0
  53. package/widgets/magic-navigation/view-context.js +15 -0
  54. package/widgets/magic-navigation/widget.js +540 -0
  55. package/widgets/magic-number-field/styles.js +10 -0
  56. package/widgets/magic-number-field/widget.js +103 -0
  57. package/widgets/magic-panel/styles.js +61 -0
  58. package/widgets/magic-panel/widget.js +63 -0
  59. package/widgets/magic-radio/styles.js +93 -0
  60. package/widgets/magic-radio/widget.js +74 -0
  61. package/widgets/magic-scroll/styles.js +22 -0
  62. package/widgets/magic-scroll/widget.js +20 -0
  63. package/widgets/magic-select/styles.js +16 -0
  64. package/widgets/magic-select/widget.js +134 -0
  65. package/widgets/magic-table/reducer.js +63 -0
  66. package/widgets/magic-table/styles.js +170 -0
  67. package/widgets/magic-table/widget.js +627 -0
  68. package/widgets/magic-tag/styles.js +32 -0
  69. package/widgets/magic-tag/widget.js +32 -0
  70. package/widgets/magic-text-field/styles.js +58 -0
  71. package/widgets/magic-text-field/widget.js +66 -0
  72. package/widgets/magic-time-field/styles.js +8 -0
  73. package/widgets/magic-time-field/widget.js +142 -0
  74. package/widgets/magic-timer/styles.js +30 -0
  75. package/widgets/magic-timer/widget.js +162 -0
  76. package/widgets/magic-zen/styles.js +61 -0
  77. package/widgets/magic-zen/widget.js +42 -0
  78. package/widgets/main-tabs/styles.js +106 -0
  79. package/widgets/main-tabs/widget.js +23 -0
  80. package/widgets/menu/styles.js +156 -0
  81. package/widgets/menu/test-menu.html +154 -0
  82. package/widgets/menu/widget.js +575 -0
  83. package/widgets/movable/widget.js +80 -0
  84. package/widgets/splitter/styles.js +57 -0
  85. package/widgets/splitter/widget.js +40 -0
  86. package/widgets/tab-layout/styles.js +31 -0
  87. package/widgets/tab-layout/widget.js +59 -0
  88. package/widgets/with-computed-size/widget.js +52 -0
@@ -0,0 +1,145 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c';
5
+ import MagicTextField from '../magic-text-field/widget.js';
6
+
7
+ import {date as DateConverters} from 'xcraft-core-converters';
8
+ import InputGroup from '../input-group/widget.js';
9
+ import MagicButton from '../magic-button/widget.js';
10
+ import Icon from '@mdi/react';
11
+ import {mdiCalendarMonth} from '@mdi/js';
12
+
13
+ class MagicDateFieldNC extends Widget {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.styles = styles;
17
+ this.inputRef = this.props.inputRef || React.createRef();
18
+ }
19
+
20
+ get input() {
21
+ return this.inputRef.current;
22
+ }
23
+
24
+ static parseEdited(value) {
25
+ if (!value) {
26
+ return null;
27
+ }
28
+ const edited = DateConverters.parseEdited(value);
29
+ // if (edited.error) {
30
+ // return null;
31
+ // }
32
+ return edited.value;
33
+ }
34
+
35
+ parse = (value) => {
36
+ const newValue = MagicDateFieldNC.parseEdited(value);
37
+ if (this.props.required && !newValue) {
38
+ return DateConverters.getNowCanonical();
39
+ }
40
+ return newValue;
41
+ };
42
+
43
+ format = (value) => {
44
+ if (!value) {
45
+ return '';
46
+ }
47
+ return DateConverters.getDisplayed(value.split('T', 1)[0]);
48
+ };
49
+
50
+ getPositionKind(position) {
51
+ // Value: d d . m m . y y y y
52
+ // Pos: 0 1 2 3 4 5 6 7 8 9 10
53
+ // Kind: 0 | 1 | 2
54
+ if (position <= 2) {
55
+ return 0;
56
+ }
57
+ if (position <= 5) {
58
+ return 1;
59
+ }
60
+ return 2;
61
+ }
62
+
63
+ getSelection(positionKind, max) {
64
+ // Kind: 0 | 1 | 2
65
+ // Pos: 0 1 2 3 4 5 6 7 8 9 10
66
+ // Value: d d . m m . y y y y
67
+ if (positionKind === 0) {
68
+ return [0, 2];
69
+ }
70
+ if (positionKind === 1) {
71
+ return [3, 5];
72
+ }
73
+ return [6, max];
74
+ }
75
+
76
+ handleKeyDown = (event) => {
77
+ this.props.onKeyDown?.(event);
78
+
79
+ if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
80
+ const {value, selectionStart, selectionEnd} = event.target;
81
+ const cursorPosition =
82
+ selectionStart < 2
83
+ ? selectionStart
84
+ : (selectionStart + selectionEnd) / 2;
85
+ const step = event.shiftKey && cursorPosition <= 2 ? 7 : 1;
86
+ const direction = event.key === 'ArrowUp' ? 1 : -1;
87
+ const result = DateConverters.incEdited(
88
+ value,
89
+ cursorPosition,
90
+ direction,
91
+ step
92
+ );
93
+ if (result.edited) {
94
+ this.input.changeAndSelect(
95
+ result.edited,
96
+ result.selectionStart,
97
+ result.selectionEnd
98
+ );
99
+ }
100
+ event.preventDefault();
101
+ }
102
+
103
+ if (event.ctrlKey && event.key === 'ArrowRight') {
104
+ const {value, selectionEnd} = event.target;
105
+ const positionKind = this.getPositionKind(selectionEnd);
106
+ const newKind = positionKind < 2 ? positionKind + 1 : 2;
107
+ this.input.htmlInput.setSelectionRange(
108
+ ...this.getSelection(newKind, value.length)
109
+ );
110
+ event.preventDefault();
111
+ }
112
+ if (event.ctrlKey && event.key === 'ArrowLeft') {
113
+ const {value, selectionStart} = event.target;
114
+ const positionKind = this.getPositionKind(selectionStart);
115
+ const newKind = positionKind > 0 ? positionKind - 1 : 0;
116
+ this.input.htmlInput.setSelectionRange(
117
+ ...this.getSelection(newKind, value.length)
118
+ );
119
+ event.preventDefault();
120
+ }
121
+ };
122
+
123
+ render() {
124
+ const {className = '', required, ...props} = this.props;
125
+ return (
126
+ <InputGroup>
127
+ <MagicTextField
128
+ inputRef={this.inputRef}
129
+ format={this.format}
130
+ parse={this.parse}
131
+ {...props}
132
+ onKeyDown={this.handleKeyDown}
133
+ className={this.styles.classNames.dateField + ' ' + className}
134
+ />
135
+ <MagicButton disabled>
136
+ <Icon path={mdiCalendarMonth} size={0.8} />
137
+ </MagicButton>
138
+ </InputGroup>
139
+ );
140
+ }
141
+ }
142
+ const MagicDateField = withC(MagicDateFieldNC, {value: 'onChange'});
143
+ MagicDateField.displayName = 'MagicDateField';
144
+
145
+ export default MagicDateField;
@@ -0,0 +1,11 @@
1
+ export default function styles() {
2
+ const datetimeContainer = {
3
+ display: 'inline-flex',
4
+ flexDirection: 'row',
5
+ gap: '5px;',
6
+ };
7
+
8
+ return {
9
+ datetimeContainer,
10
+ };
11
+ }
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c';
5
+ import MagicDateField from '../magic-date-field/widget.js';
6
+ import MagicTimeField from '../magic-time-field/widget.js';
7
+ import {
8
+ nowZonedDateTimeISO,
9
+ parseZonedDateTime,
10
+ zonedDateTimeFromParts,
11
+ } from 'xcraft-core-utils/lib/calendar.js';
12
+
13
+ class MagicDatetimeFieldNC extends Widget {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.styles = styles;
17
+ }
18
+
19
+ changeDate = (date) => {
20
+ const {value} = this.props;
21
+ let newDate;
22
+ if (date && value && value.includes('T')) {
23
+ const afterDate = value.slice(value.indexOf('T') + 1);
24
+ newDate = `${date}T${afterDate}`;
25
+ } else {
26
+ newDate = date;
27
+ }
28
+ this.props.onChange?.(newDate);
29
+ };
30
+
31
+ changeTime = (time) => {
32
+ const {value, initialDate} = this.props;
33
+ let newDate;
34
+ if (value) {
35
+ const current = parseZonedDateTime(value);
36
+ if (time) {
37
+ const timezone = current.time
38
+ ? current.timezone
39
+ : Intl.DateTimeFormat().resolvedOptions().timeZone;
40
+ newDate = zonedDateTimeFromParts({...current, time, timezone});
41
+ } else {
42
+ newDate = current.date;
43
+ }
44
+ } else {
45
+ if (time) {
46
+ const initial = parseZonedDateTime(
47
+ initialDate || nowZonedDateTimeISO()
48
+ );
49
+ const timezone = initial.time
50
+ ? initial.timezone
51
+ : Intl.DateTimeFormat().resolvedOptions().timeZone;
52
+ newDate = zonedDateTimeFromParts({
53
+ ...initial,
54
+ time,
55
+ timezone,
56
+ });
57
+ } else {
58
+ newDate = null;
59
+ }
60
+ }
61
+ this.props.onChange?.(newDate);
62
+ };
63
+
64
+ render() {
65
+ const {
66
+ value,
67
+ onChange,
68
+ requiredDate,
69
+ requiredTime,
70
+ dispatch,
71
+ ...props
72
+ } = this.props;
73
+ const {date, time} = value ? parseZonedDateTime(value) : {};
74
+ return (
75
+ <div className={this.styles.classNames.datetimeContainer}>
76
+ <MagicDateField
77
+ {...props}
78
+ value={date}
79
+ onChange={this.changeDate}
80
+ required={requiredDate || requiredTime}
81
+ />
82
+ <MagicTimeField
83
+ {...props}
84
+ value={time}
85
+ onChange={this.changeTime}
86
+ required={requiredTime}
87
+ />
88
+ </div>
89
+ );
90
+ }
91
+ }
92
+ const MagicDatetimeField = withC(MagicDatetimeFieldNC, {value: 'onChange'});
93
+ MagicDatetimeField.displayName = 'MagicDatetimeField';
94
+
95
+ export default MagicDatetimeField;
@@ -0,0 +1,39 @@
1
+ export default function styles() {
2
+ const dialog = {
3
+ 'border': 'none',
4
+ 'backgroundColor': 'transparent',
5
+ 'padding': '0',
6
+ 'maxHeight': '100%',
7
+ 'position': 'fixed',
8
+ 'top': '0',
9
+ 'left': '0',
10
+ 'right': '0',
11
+ 'bottom': '0',
12
+ 'margin': 'auto',
13
+ 'overflow': 'visible',
14
+
15
+ '&::backdrop': {
16
+ backgroundColor: 'rgba(0,0,0,0.3)',
17
+ },
18
+
19
+ '&[data-modal=false]': {
20
+ boxShadow:
21
+ 'rgba(0, 0, 0, 0.2) 0px 4px 120px, rgba(0, 0, 0, 0.1) 0px 4px 80px',
22
+ borderRadius: '8px',
23
+ // '&:not(:focus-within)': {
24
+ // 'boxShadow': 'rgba(0, 0, 0, 0.2) 0px 4px 120px',
25
+ // '& :not(div)': {
26
+ // opacity: 0.7,
27
+ // },
28
+ // },
29
+ },
30
+
31
+ '& > *': {
32
+ maxHeight: '95vh',
33
+ },
34
+ };
35
+
36
+ return {
37
+ dialog,
38
+ };
39
+ }
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import Dialog from '../dialog/widget.js';
5
+ import Movable from '../movable/widget.js';
6
+ import isEmptyAreaElement from '../element-helpers/is-empty-area-element.js';
7
+
8
+ /**
9
+ * @param {PointerEvent} event1
10
+ * @param {PointerEvent} event2
11
+ * @returns {number}
12
+ */
13
+ function eventDistance(event1, event2) {
14
+ return Math.sqrt(
15
+ Math.pow(event2.clientX - event1.clientX, 2) +
16
+ Math.pow(event2.clientY - event1.clientY, 2)
17
+ );
18
+ }
19
+
20
+ class MagicDialog extends Widget {
21
+ constructor() {
22
+ super(...arguments);
23
+ this.styles = styles;
24
+ /** @type {React.RefObject<Dialog>} */
25
+ this.dialog = React.createRef();
26
+ }
27
+
28
+ componentDidMount() {
29
+ this.updateOutsideClickHandler();
30
+ }
31
+
32
+ componentDidUpdate() {
33
+ this.updateOutsideClickHandler();
34
+ }
35
+
36
+ componentWillUnmount() {
37
+ this.removeOutsideClickHandler();
38
+ }
39
+
40
+ updateOutsideClickHandler() {
41
+ if (!this.props.modal && this.props.onClose && this.props.open) {
42
+ this.addOutsideClickHandler();
43
+ } else {
44
+ this.removeOutsideClickHandler();
45
+ }
46
+ }
47
+
48
+ addOutsideClickHandler() {
49
+ window.addEventListener('pointerdown', this.handlePointerDown);
50
+ window.addEventListener('pointerup', this.handlePointerUp);
51
+ }
52
+
53
+ removeOutsideClickHandler() {
54
+ window.removeEventListener('pointerdown', this.handlePointerDown);
55
+ window.removeEventListener('pointerup', this.handlePointerUp);
56
+ }
57
+
58
+ handlePointerDown = (event) => {
59
+ const {target} = event;
60
+ const dialog = this.dialog.current?.dialog.current;
61
+ if (dialog && !dialog.contains(target)) {
62
+ if (isEmptyAreaElement(target)) {
63
+ this.downEvent = event;
64
+ return;
65
+ }
66
+ }
67
+ };
68
+
69
+ handlePointerUp = (event) => {
70
+ if (this.downEvent) {
71
+ const distance = eventDistance(event, this.downEvent);
72
+ this.downEvent = null;
73
+ if (distance < 8) {
74
+ this.dialog.current.close();
75
+ }
76
+ }
77
+ };
78
+
79
+ handleKeyDown = (event) => {
80
+ this.props.onKeyDown?.(event);
81
+ if (event.defaultPrevented) {
82
+ return;
83
+ }
84
+ if (event.key === 'Escape') {
85
+ this.dialog.current.close();
86
+ event.stopPropagation();
87
+ }
88
+ };
89
+
90
+ render() {
91
+ const {className = '', ...props} = this.props;
92
+ return (
93
+ <Movable>
94
+ {(moveableProps) => (
95
+ <Dialog
96
+ {...props}
97
+ {...moveableProps}
98
+ onKeyDown={
99
+ !this.props.modal && this.props.onClose
100
+ ? this.handleKeyDown
101
+ : undefined
102
+ }
103
+ ref={this.dialog}
104
+ className={this.styles.classNames.dialog + ' ' + className}
105
+ open={this.props.open}
106
+ modal={this.props.modal}
107
+ data-modal={this.props.modal || false}
108
+ onClose={this.props.onClose}
109
+ />
110
+ )}
111
+ </Movable>
112
+ );
113
+ }
114
+ }
115
+
116
+ export default MagicDialog;
@@ -0,0 +1,22 @@
1
+ export default function styles() {
2
+ const div = {
3
+ '--border-radius': '8px',
4
+ // 'color': 'white',
5
+ 'background': 'rgba(248, 241, 248, 0.1)',
6
+ 'borderRadius': '8px',
7
+ 'boxShadow': '0 4px 30px rgba(0, 0, 0, 0.1)',
8
+ 'backdropFilter': 'blur(10px) saturate(100%)',
9
+ 'border':
10
+ '1px solid color-mix(in srgb, var(--text-color), transparent 78%)',
11
+
12
+ '@media (prefers-color-scheme: light)': {
13
+ background: 'rgba(248, 241, 248, 0.85)',
14
+ },
15
+ };
16
+
17
+ return {
18
+ div,
19
+ };
20
+ }
21
+
22
+ /******************************************************************************/
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+
5
+ export default class MagicDiv extends Widget {
6
+ constructor() {
7
+ super(...arguments);
8
+ this.styles = styles;
9
+ }
10
+
11
+ render() {
12
+ const {className = '', ...props} = this.props;
13
+ return (
14
+ <div
15
+ className={this.styles.classNames.div + ' ' + className}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ export default function styles(theme) {
2
+ const button = {
3
+ '& > span': {
4
+ fontSize: '140%',
5
+ margin: '-4px',
6
+ },
7
+ };
8
+
9
+ const content = {
10
+ border: 'none',
11
+ };
12
+
13
+ return {button, content};
14
+ }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import Menu from '../menu/widget.js';
5
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c.js';
6
+ import MagicEmojiPickerNC from '../magic-emoji-picker/widget.js';
7
+
8
+ class MagicEmojiNC extends Widget {
9
+ constructor() {
10
+ super(...arguments);
11
+ this.styles = styles;
12
+ }
13
+
14
+ render() {
15
+ const {onChange, ...props} = this.props;
16
+ const onEmojiSelect = (emoji) => onChange(emoji?.native);
17
+
18
+ return (
19
+ <Menu>
20
+ <Menu.Button className={this.styles.classNames.button}>
21
+ {props.value}
22
+ </Menu.Button>
23
+ <Menu.Content className={this.styles.classNames.content}>
24
+ <MagicEmojiPickerNC onEmojiSelect={onEmojiSelect} {...props} />
25
+ </Menu.Content>
26
+ </Menu>
27
+ );
28
+ }
29
+ }
30
+
31
+ const MagicEmoji = withC(MagicEmojiNC, {value: 'onChange'});
32
+
33
+ export default MagicEmoji;
@@ -0,0 +1,21 @@
1
+ export default function styles(theme) {
2
+ const picker = {
3
+ '& div > em-emoji-picker': {
4
+ '--font-family': theme.typo.font,
5
+ '--border-radius': '4px',
6
+
7
+ '--rgb-background': '25, 45, 70, 1',
8
+ '--rgb-accent': '255, 255, 255',
9
+ '--shadow': '0 0 1px 1px rgba(255, 255, 255, 0.4)',
10
+ '@media (prefers-color-scheme: light)': {
11
+ '--rgb-background': '237, 234, 242, 1',
12
+ '--rgb-accent': '0, 0, 0',
13
+ '--shadow': '0 0 1px 1px rgba(0, 0, 0, 0.4)',
14
+ },
15
+ },
16
+ };
17
+
18
+ return {
19
+ picker,
20
+ };
21
+ }
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import data from '@emoji-mart/data';
5
+ import Picker from '@emoji-mart/react';
6
+
7
+ class MagicEmojiPickerNC extends Widget {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.styles = styles;
11
+ }
12
+
13
+ render() {
14
+ const {className = '', ...props} = this.props;
15
+
16
+ let theme = 'light';
17
+ let emojiButtonColors = 'rgba(0, 0, 0, 0.1)';
18
+ if (
19
+ window.matchMedia &&
20
+ window.matchMedia('(prefers-color-scheme: dark)').matches
21
+ ) {
22
+ theme = 'dark';
23
+ emojiButtonColors = 'rgba(255, 255, 255, 0.2)';
24
+ }
25
+
26
+ return (
27
+ <div className={this.styles.classNames.picker + ' ' + className}>
28
+ <Picker
29
+ data={data}
30
+ locale="fr"
31
+ set="native"
32
+ theme={theme}
33
+ previewPosition="none"
34
+ autoFocus={true}
35
+ emojiButtonRadius="4px"
36
+ emojiButtonColors={[emojiButtonColors]}
37
+ {...props}
38
+ />
39
+ </div>
40
+ );
41
+ }
42
+ }
43
+
44
+ export default MagicEmojiPickerNC;
@@ -0,0 +1,55 @@
1
+ /******************************************************************************/
2
+
3
+ export default function styles() {
4
+ const disabledStyle = {
5
+ ':hover': {
6
+ outline: 'none',
7
+ backgroundColor: 'transparent',
8
+ },
9
+ };
10
+
11
+ const inplaceInput = {
12
+ 'display': 'flex',
13
+ 'width': '100%',
14
+ 'minHeight': '1em', // For contenteditable
15
+ 'backgroundColor': 'transparent',
16
+ 'borderRadius': '5px',
17
+ 'border': 'none',
18
+ 'padding': '0',
19
+ 'marginTop': '0',
20
+ 'fontSize': 'inherit',
21
+ 'color': 'inherit',
22
+ 'resize': 'vertical',
23
+ '::placeholder': {
24
+ color: 'color-mix(in srgb, var(--text-color), transparent 75%)',
25
+ fontWeight: 'bold',
26
+ },
27
+ ':hover': {
28
+ outline:
29
+ '1px solid color-mix(in srgb, var(--text-color), transparent 70%)',
30
+ backgroundColor:
31
+ 'color-mix(in srgb, var(--field-background-color), transparent 20%)',
32
+ marginLeft: '-0.3em',
33
+ paddingLeft: '0.3em',
34
+ },
35
+ ':focus': {
36
+ outline:
37
+ '1px solid color-mix(in srgb, var(--text-color), transparent 70%)',
38
+ backgroundColor:
39
+ 'color-mix(in srgb, var(--field-background-color), transparent 20%)',
40
+ marginLeft: '-0.3em',
41
+ paddingLeft: '0.3em',
42
+ },
43
+ '&::selection': {
44
+ backgroundColor: 'rgba(0,170,255,0.55)',
45
+ },
46
+ '&[data-disabled=true]': disabledStyle,
47
+ '&:disabled': disabledStyle,
48
+ };
49
+
50
+ return {
51
+ inplaceInput,
52
+ };
53
+ }
54
+
55
+ /******************************************************************************/
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import Widget from 'goblin-laboratory/widgets/widget';
3
+ import * as styles from './styles.js';
4
+ import withC from 'goblin-laboratory/widgets/connect-helpers/with-c';
5
+ import MagicInput from '../magic-input/widget.js';
6
+
7
+ class MagicInplaceInputNC extends Widget {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.styles = styles;
11
+ }
12
+
13
+ render() {
14
+ const {className = '', ...props} = this.props;
15
+ return (
16
+ <MagicInput
17
+ {...props}
18
+ className={this.styles.classNames.inplaceInput + ' ' + className}
19
+ />
20
+ );
21
+ }
22
+ }
23
+
24
+ const MagicInplaceInput = withC(MagicInplaceInputNC, {value: 'onChange'});
25
+ MagicInplaceInput.displayName = 'MagicInplaceInput';
26
+ export default MagicInplaceInput;