@24i/bigscreen-sdk 1.0.29-alpha.2491 → 1.0.30-alpha.2517

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@24i/bigscreen-sdk",
3
- "version": "1.0.29-alpha.2491",
3
+ "version": "1.0.30-alpha.2517",
4
4
  "description": "SmartApps BIGscreen SDK monorepo",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -44,9 +44,6 @@
44
44
  "@24i/smartapps-datalayer": "3.0.11-alpha.1019",
45
45
  "@sentry/browser": "7.35.0",
46
46
  "@sentry/types": "7.35.0",
47
- "@types/gtag.js": "^0.0.14",
48
- "@types/prop-types": "^15.7.7",
49
- "@types/scheduler": "^0.16.4",
50
47
  "csstype": "^3.1.2",
51
48
  "date-fns": "2.29.3",
52
49
  "lottie-web": "5.12.2",
@@ -56,14 +53,17 @@
56
53
  "@24i/bigscreen-sdk": "file:.",
57
54
  "@24i/eslint-config-smartapps-bigscreen-linters": "file:./libs/linters",
58
55
  "@24i/smartapps-bigscreen-changelog-generator": "0.17.0",
59
- "@babel/core": "^7.23.0",
60
- "@babel/preset-env": "^7.22.20",
61
- "@types/fs-extra": "^11.0.2",
62
- "@types/inquirer": "^9.0.3",
63
- "@types/jest": "^29.5.5",
64
- "@types/minimist": "^1.2.3",
65
- "@types/node": "^20.8.2",
56
+ "@babel/core": "^7.23.2",
57
+ "@babel/preset-env": "^7.23.2",
58
+ "@types/fs-extra": "^11.0.3",
59
+ "@types/gtag.js": "^0.0.17",
60
+ "@types/inquirer": "^9.0.6",
61
+ "@types/jest": "^29.5.7",
62
+ "@types/minimist": "^1.2.4",
63
+ "@types/node": "^20.8.10",
64
+ "@types/prop-types": "^15.7.9",
66
65
  "@types/react": "18.0.33",
66
+ "@types/scheduler": "^0.16.5",
67
67
  "babel-jest": "^29.7.0",
68
68
  "chalk": "^5.3.0",
69
69
  "coveralls": "^3.1.1",
@@ -78,7 +78,7 @@
78
78
  "jest-sonar": "^0.2.15",
79
79
  "minimist": "^1.2.8",
80
80
  "patch-package": "^8.0.0",
81
- "sass": "^1.68.0",
81
+ "sass": "^1.69.5",
82
82
  "sass-true": "^6.1.0",
83
83
  "ts-jest": "^29.1.1",
84
84
  "ts-node": "^10.9.1",
@@ -60,3 +60,6 @@ The reason for no styling is to limit unnecessary CSS code that would be overrid
60
60
  `.native-input-wrap` is set to `display: none`. This is the native input used on the background.
61
61
 
62
62
  `.input-field` is a custom component that should look like input field with possibility of custom styling.
63
+
64
+ `.caret` position is counted from the left side of the input in LTR layout and from the right side of the
65
+ input in RTL layout.
@@ -1,4 +1,4 @@
1
- import { createRef, Component, setText, DeclareProps } from '@24i/bigscreen-sdk/jsx';
1
+ import { createRef, Component, DeclareProps } from '@24i/bigscreen-sdk/jsx';
2
2
  import { Interactable } from '@24i/bigscreen-sdk/interactable';
3
3
  import { IFocusable } from '@24i/bigscreen-sdk/focus';
4
4
  import { isRtl } from '@24i/bigscreen-sdk/i18n';
@@ -9,7 +9,7 @@ import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
9
9
  import { addClass } from '@24i/bigscreen-sdk/utils/addClass';
10
10
  import { removeClass } from '@24i/bigscreen-sdk/utils/removeClass';
11
11
  import { offsetPosition } from '@24i/bigscreen-sdk/utils/offsetPosition';
12
- import { handleOverflow } from './handleOverflow';
12
+ import { checkCharOrderMishmash, handleOverflow } from './utils';
13
13
  import './Input.scss';
14
14
 
15
15
  type Props = {
@@ -42,6 +42,8 @@ export class Input extends Component<Props> implements IFocusable {
42
42
 
43
43
  caret = createRef<HTMLDivElement>();
44
44
 
45
+ caretPosition = 0;
46
+
45
47
  isInputting = false;
46
48
 
47
49
  lastValue: string;
@@ -68,6 +70,11 @@ export class Input extends Component<Props> implements IFocusable {
68
70
  this.isPlaceholderVisible = value.length === 0;
69
71
  }
70
72
 
73
+ componentDidMount() {
74
+ const { value } = this.props;
75
+ if (value) this.setText(value);
76
+ }
77
+
71
78
  getType() {
72
79
  return this.nativeInput.current?.type;
73
80
  }
@@ -85,13 +92,10 @@ export class Input extends Component<Props> implements IFocusable {
85
92
 
86
93
  onCaretPositionChange = (position: number | null) => {
87
94
  if (position === null) return;
88
- const { shouldHandleOverflow } = this.props;
89
95
  const value = this.getValue();
90
- setText(this.text, this.filterText(value));
91
- this.setCaretPosition(position);
92
- if (shouldHandleOverflow) {
93
- handleOverflow(this.wrap.current, this.caret.current, this.text.current);
94
- }
96
+ this.setText(value);
97
+ this.updateCaretPosition(position);
98
+ this.updateTextOverflow();
95
99
  if (this.lastValue !== value) {
96
100
  const { onChange } = this.props;
97
101
  this.lastValue = value;
@@ -106,33 +110,59 @@ export class Input extends Component<Props> implements IFocusable {
106
110
 
107
111
  setValue(value: string) {
108
112
  this.nativeInput.current!.value = value;
113
+ this.setText(value);
109
114
  }
110
115
 
111
116
  setType(type: Props['type']) {
112
117
  if (this.getType() === type) return;
113
118
  const value = this.getValue();
114
119
  this.nativeInput.current!.type = type;
115
- setText(this.text, this.filterText(value));
120
+ this.setText(value);
116
121
  }
117
122
 
118
- setCaretPosition(position: number) {
123
+ setText(text: string): void {
124
+ const filtered = this.filterText(text);
125
+ this.renderText(filtered);
126
+ }
127
+
128
+ updateCaretPosition(position: number) {
119
129
  const textNode = this.text.current!.firstChild;
120
- if (!textNode) {
130
+ if (!textNode || position <= 0) {
131
+ this.caretPosition = 0;
121
132
  offsetPosition(this.caret, { x: 0 });
122
133
  return;
123
134
  }
124
- const range = new Range();
125
- range.selectNode(textNode);
126
- const wholeText = range.getBoundingClientRect();
127
- range.setStart(textNode, 0);
128
- range.setEnd(textNode, position);
129
- const caretText = range.getBoundingClientRect();
130
- const [initialCaretPosition, currentCarretPosition, offsetBase] = isRtl()
131
- ? [wholeText.right, caretText.left, wholeText.left]
132
- : [wholeText.left, caretText.right, wholeText.right];
133
- const caretPos = position === 0 ? initialCaretPosition : currentCarretPosition;
134
- const offset = offsetBase - caretPos;
135
- offsetPosition(this.caret, { x: -offset }, false);
135
+ const wholeTextDimensions = this.text.current!.getBoundingClientRect();
136
+ if (position >= this.text.current!.children.length) {
137
+ const offset = wholeTextDimensions.right - wholeTextDimensions.left;
138
+ this.caretPosition = isRtl() ? -offset : offset;
139
+ offsetPosition(this.caret, { x: this.caretPosition }, false);
140
+ return;
141
+ }
142
+ const charNode = this.text.current!.children[position - 1];
143
+ const charDimensions = charNode!.getBoundingClientRect();
144
+ const isCharMishmash = checkCharOrderMishmash(this.text.current!, position - 1);
145
+ if (isRtl()) {
146
+ if (isCharMishmash) {
147
+ this.caretPosition = charDimensions.right - wholeTextDimensions.right;
148
+ } else {
149
+ this.caretPosition = charDimensions.left - wholeTextDimensions.right;
150
+ }
151
+ } else if (isCharMishmash) {
152
+ this.caretPosition = charDimensions.left - wholeTextDimensions.left;
153
+ } else {
154
+ this.caretPosition = charDimensions.right - wholeTextDimensions.left;
155
+ }
156
+ offsetPosition(this.caret, { x: this.caretPosition }, false);
157
+ }
158
+
159
+ updateTextOverflow() {
160
+ const { shouldHandleOverflow } = this.props;
161
+ if (!shouldHandleOverflow) return;
162
+ const textMargin = handleOverflow(
163
+ this.wrap.current, this.caret.current, this.text.current,
164
+ );
165
+ offsetPosition(this.caret, { x: this.caretPosition - textMargin }, false);
136
166
  }
137
167
 
138
168
  /**
@@ -196,6 +226,14 @@ export class Input extends Component<Props> implements IFocusable {
196
226
  return text.replace(/ /g, NBSP);
197
227
  }
198
228
 
229
+ renderText(text: string): void {
230
+ const chars: JSX.Element[] = [];
231
+ for (let i = 0; i < text.length; i++) {
232
+ chars.push(<span className="char">{text[i]}</span>);
233
+ }
234
+ this.clearMount(chars, this.text);
235
+ }
236
+
199
237
  render() {
200
238
  const { value, type, placeholder, onFocus, onBlur } = this.props;
201
239
  return (
@@ -220,9 +258,7 @@ export class Input extends Component<Props> implements IFocusable {
220
258
  >
221
259
  {placeholder}
222
260
  </div>
223
- <div ref={this.text} className="text">
224
- {this.filterText(value)}
225
- </div>
261
+ <div ref={this.text} className="text" />
226
262
  <div ref={this.caret} className="caret" />
227
263
  </div>
228
264
  </Interactable>
@@ -0,0 +1,71 @@
1
+ import { isRtl } from '@24i/bigscreen-sdk/i18n';
2
+
3
+ /**
4
+ * Handles overflowing text in the input by moving with the inner
5
+ * text element.
6
+ * @param wrap input wrapper
7
+ * @param caret caret element
8
+ * @param text input inner text element
9
+ * @returns value how much is text element moved
10
+ */
11
+ export const handleOverflow = (
12
+ wrap: HTMLDivElement | null,
13
+ caret: HTMLDivElement | null,
14
+ text: HTMLDivElement | null,
15
+ ) => {
16
+ if (!wrap || !caret || !text) return 0;
17
+ const { width: wrapWidth, left: wrapLeft } = wrap.getBoundingClientRect();
18
+ const { width: caretWidth, left: caretLeft } = caret.getBoundingClientRect();
19
+ const caretOffset = isRtl() ? caretLeft - caretWidth : caretLeft + caretWidth;
20
+ if (!isRtl() && wrapWidth + wrapLeft < caretOffset) {
21
+ const margin = -(caretOffset - wrapWidth - wrapLeft);
22
+ text.style.marginLeft = `${margin}px`;
23
+ text.style.marginRight = '0px';
24
+ return margin;
25
+ }
26
+ if (isRtl() && caretOffset < wrapLeft) {
27
+ const margin = -(wrapLeft - caretOffset);
28
+ text.style.marginLeft = '0px';
29
+ text.style.marginRight = `${margin}px`;
30
+ return margin;
31
+ }
32
+ text.style.marginLeft = '0px';
33
+ text.style.marginRight = '0px';
34
+ return 0;
35
+ };
36
+
37
+ /**
38
+ * Check for char element position mishmash in the input's text element. This is
39
+ * necessary to recognize if the char is in the wrong direction than it's position
40
+ * in children collection, this means that the char is LTR char in RTL scheme or RTL
41
+ * char in LTR scheme.
42
+ * @param text input inner text element
43
+ * @param position position of the char in children collection
44
+ * @returns true if char has position mishmash
45
+ */
46
+ export const checkCharOrderMishmash = (text: HTMLDivElement, position: number) => {
47
+ const { children } = text!;
48
+ if (!children.length) return false;
49
+ const charOffsets: {
50
+ index: number,
51
+ offset: number,
52
+ }[] = [];
53
+ for (let i = 0; i < children.length; i++) {
54
+ charOffsets.push({
55
+ index: i,
56
+ offset: (children[i] as HTMLElement).getBoundingClientRect().left,
57
+ });
58
+ }
59
+ charOffsets.sort((a, b) => {
60
+ if (a.offset < b.offset) return isRtl() ? 1 : -1;
61
+ if (a.offset > b.offset) return isRtl() ? -1 : 1;
62
+ return 0;
63
+ });
64
+ const lastPos = children.length - 1;
65
+ const posBefore = position - 1;
66
+ const mishmashBefore = posBefore < 0
67
+ ? false : charOffsets[posBefore].index !== posBefore;
68
+ const mishmashCurrent = charOffsets[position].index !== position;
69
+ if (mishmashCurrent) return true;
70
+ return !mishmashCurrent && mishmashBefore && position !== lastPos;
71
+ };
@@ -1,29 +0,0 @@
1
- import { isRtl } from '@24i/bigscreen-sdk/i18n';
2
-
3
- export const handleOverflow = (
4
- wrap: HTMLDivElement | null,
5
- caret: HTMLDivElement | null,
6
- text: HTMLDivElement | null,
7
- ) => {
8
- if (!wrap || !caret || !text) return;
9
- const { width: wrapWidth, left: wrapLeft } = wrap.getBoundingClientRect();
10
- const { width: caretWidth, left: caretLeft } = caret.getBoundingClientRect();
11
- const textMargin = parseInt(text.dataset.margin!, 10) || 0;
12
- const caretOffset = isRtl()
13
- ? caretLeft + textMargin - caretWidth
14
- : caretLeft - textMargin + caretWidth;
15
- if (!isRtl() && wrapWidth + wrapLeft < caretOffset) {
16
- const margin = -(caretOffset - wrapWidth - wrapLeft);
17
- text.style.marginLeft = `${margin}px`;
18
- text.style.marginRight = '0px';
19
- text.dataset.margin = margin.toString();
20
- } else if (isRtl() && caretOffset < wrapLeft) {
21
- const margin = -(wrapLeft - caretOffset);
22
- text.style.marginLeft = '0px';
23
- text.style.marginRight = `${margin}px`;
24
- text.dataset.margin = margin.toString();
25
- } else {
26
- text.style.marginLeft = '0px';
27
- text.style.marginRight = '0px';
28
- }
29
- };