neo.mjs 8.22.0 → 8.23.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.
- package/README.md +5 -0
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/ConfigurationViewport.mjs +21 -8
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/component/magicmovetext/MainContainer.mjs +90 -0
- package/examples/component/magicmovetext/app.mjs +6 -0
- package/examples/component/magicmovetext/index.html +11 -0
- package/examples/component/magicmovetext/neo-config.json +6 -0
- package/package.json +2 -2
- package/resources/scss/src/component/MagicMoveText.scss +45 -0
- package/resources/scss/src/examples/ConfigurationViewport.scss +27 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/MagicMoveText.mjs +306 -0
- package/src/data/Store.mjs +12 -0
- package/src/grid/Container.mjs +1 -1
- package/src/grid/View.mjs +89 -71
- package/src/grid/header/Toolbar.mjs +1 -1
- package/src/selection/grid/CellModel.mjs +7 -3
- package/src/selection/grid/RowModel.mjs +2 -2
- package/resources/scss/src/examples/ConfigurationPanel.scss +0 -25
package/README.md
CHANGED
@@ -21,6 +21,11 @@ without compilations. This makes debugging easier and reduces development costs.
|
|
21
21
|
In case you care about scalability, extensibility or solid architectures, congratulations,
|
22
22
|
you have found the right spot.
|
23
23
|
|
24
|
+
<p align="center">
|
25
|
+
<a href="https://youtu.be/pYfM28Pz6_0"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo33s.png"></a>
|
26
|
+
<a href="https://youtu.be/aEA5333WiWY"><img height="316px" width="400px" src="https://raw.githubusercontent.com/neomjs/pages/master/resources_pub/images/neo-movie.png"></a>
|
27
|
+
</p>
|
28
|
+
|
24
29
|
## Content
|
25
30
|
1. <a href="#introduction">Introduction</a>
|
26
31
|
2. <a href="#use-cases">Potential Use Cases</a>
|
package/apps/ServiceWorker.mjs
CHANGED
package/apps/portal/index.html
CHANGED
@@ -20,19 +20,19 @@ const themes = [
|
|
20
20
|
class ConfigurationViewport extends Viewport {
|
21
21
|
static config = {
|
22
22
|
/**
|
23
|
-
* @member {String} className='Neo.examples.
|
23
|
+
* @member {String} className='Neo.examples.ConfigurationViewport'
|
24
24
|
* @protected
|
25
25
|
*/
|
26
|
-
className: 'Neo.examples.
|
26
|
+
className: 'Neo.examples.ConfigurationViewport',
|
27
27
|
/**
|
28
28
|
* @member {String} ntype='configuration-viewport'
|
29
29
|
* @protected
|
30
30
|
*/
|
31
31
|
ntype: 'configuration-viewport',
|
32
32
|
/**
|
33
|
-
* @member {
|
33
|
+
* @member {String[]} baseCls=['neo-examples-configuration-viewport','neo-viewport']
|
34
34
|
*/
|
35
|
-
|
35
|
+
baseCls: ['neo-examples-configuration-viewport', 'neo-viewport'],
|
36
36
|
/**
|
37
37
|
* @member {Number} configItemLabelWidth=150
|
38
38
|
*/
|
@@ -45,6 +45,10 @@ class ConfigurationViewport extends Viewport {
|
|
45
45
|
* @member {Number} configPanelFlex=1
|
46
46
|
*/
|
47
47
|
configPanelFlex: 1,
|
48
|
+
/**
|
49
|
+
* @member {Number} configPanelMaxWidth=null
|
50
|
+
*/
|
51
|
+
configPanelMaxWidth: null,
|
48
52
|
/**
|
49
53
|
* @member {Number} configPanelMinWidth=350
|
50
54
|
*/
|
@@ -56,7 +60,11 @@ class ConfigurationViewport extends Viewport {
|
|
56
60
|
/**
|
57
61
|
* @member {Number} exampleComponentFlex=1
|
58
62
|
*/
|
59
|
-
exampleComponentFlex: 2
|
63
|
+
exampleComponentFlex: 2,
|
64
|
+
/**
|
65
|
+
* @member {Object} layout={ntype:'hbox', align:'stretch'}
|
66
|
+
*/
|
67
|
+
layout: {ntype: 'hbox', align: 'stretch'}
|
60
68
|
}
|
61
69
|
|
62
70
|
/**
|
@@ -114,6 +122,7 @@ class ConfigurationViewport extends Viewport {
|
|
114
122
|
|
115
123
|
me.items = [{
|
116
124
|
module: Container,
|
125
|
+
cls : ['neo-example-container'],
|
117
126
|
items : [me.exampleComponent],
|
118
127
|
flex : me.exampleComponentFlex,
|
119
128
|
layout: 'base',
|
@@ -123,11 +132,16 @@ class ConfigurationViewport extends Viewport {
|
|
123
132
|
module: Panel,
|
124
133
|
cls : ['neo-panel', 'neo-container', 'neo-configuration-panel'],
|
125
134
|
flex : me.configPanelFlex,
|
126
|
-
|
135
|
+
|
136
|
+
style: {
|
137
|
+
maxWidth: me.configPanelMaxWidth + 'px',
|
138
|
+
margin : '20px',
|
139
|
+
minWidth: me.configPanelMinWidth + 'px'
|
140
|
+
},
|
127
141
|
|
128
142
|
headers: [{
|
143
|
+
cls : ['neo-configuration-header-toolbar'],
|
129
144
|
dock : 'top',
|
130
|
-
style: {borderLeft:0, borderRight:0, borderTop:0},
|
131
145
|
items: [{
|
132
146
|
ntype: 'label',
|
133
147
|
text : 'Configuration'
|
@@ -145,7 +159,6 @@ class ConfigurationViewport extends Viewport {
|
|
145
159
|
items: [{
|
146
160
|
module: Container,
|
147
161
|
layout: {ntype: 'vbox', align: null},
|
148
|
-
style : {overflowY: 'auto', padding: '10px'},
|
149
162
|
cls : ['neo-configuration-panel-body'],
|
150
163
|
itemDefaults: {
|
151
164
|
clearToOriginalValue: true,
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import CheckBox from '../../../src/form/field/CheckBox.mjs';
|
2
|
+
import ConfigurationViewport from '../../ConfigurationViewport.mjs';
|
3
|
+
import MagicMoveText from '../../../src/component/MagicMoveText.mjs';
|
4
|
+
import NumberField from '../../../src/form/field/Number.mjs';
|
5
|
+
import TextField from '../../../src/form/field/Text.mjs';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @class Neo.examples.component.magicmovetext.MainContainer
|
9
|
+
* @extends Neo.examples.ConfigurationViewport
|
10
|
+
*/
|
11
|
+
class MainContainer extends ConfigurationViewport {
|
12
|
+
static config = {
|
13
|
+
className : 'Neo.examples.component.magicmovetext.MainContainer',
|
14
|
+
configItemLabelWidth: 150,
|
15
|
+
configItemWidth : 250,
|
16
|
+
configPanelMaxWidth : 300,
|
17
|
+
configPanelMinWidth : 300,
|
18
|
+
layout : {ntype: 'hbox', align: 'stretch'}
|
19
|
+
}
|
20
|
+
|
21
|
+
createConfigurationComponents() {
|
22
|
+
let me = this,
|
23
|
+
{exampleComponent} = me;
|
24
|
+
|
25
|
+
return [{
|
26
|
+
module : CheckBox,
|
27
|
+
checked : exampleComponent.autoCycle,
|
28
|
+
labelText: 'autoCycle',
|
29
|
+
listeners: {change: me.onConfigChange.bind(me, 'autoCycle')}
|
30
|
+
}, {
|
31
|
+
module : NumberField,
|
32
|
+
clearable: false,
|
33
|
+
labelText: 'autoCycleInterval',
|
34
|
+
listeners: {change: me.onConfigChange.bind(me, 'autoCycleInterval')},
|
35
|
+
maxValue : 10000,
|
36
|
+
minValue : 0,
|
37
|
+
stepSize : 1000,
|
38
|
+
style : {marginTop: '10px'},
|
39
|
+
value : exampleComponent.autoCycleInterval
|
40
|
+
}, {
|
41
|
+
module : TextField,
|
42
|
+
labelText: 'colorFadeIn',
|
43
|
+
listeners: {change: me.onConfigChange.bind(me, 'colorFadeIn')},
|
44
|
+
value : exampleComponent.colorFadeIn
|
45
|
+
}, {
|
46
|
+
module : TextField,
|
47
|
+
labelText: 'colorFadeOut',
|
48
|
+
listeners: {change: me.onConfigChange.bind(me, 'colorFadeOut')},
|
49
|
+
value : exampleComponent.colorFadeOut
|
50
|
+
}, {
|
51
|
+
module : TextField,
|
52
|
+
labelText: 'colorMove',
|
53
|
+
listeners: {change: me.onConfigChange.bind(me, 'colorMove')},
|
54
|
+
value : exampleComponent.colorMove
|
55
|
+
}, {
|
56
|
+
module : TextField,
|
57
|
+
clearable: false,
|
58
|
+
labelText: 'fontFamily',
|
59
|
+
listeners: {change: me.onConfigChange.bind(me, 'fontFamily')},
|
60
|
+
value : exampleComponent.fontFamily,
|
61
|
+
width : 280
|
62
|
+
}, {
|
63
|
+
module : NumberField,
|
64
|
+
clearable: false,
|
65
|
+
labelText: 'transitionTime',
|
66
|
+
listeners: {change: me.onConfigChange.bind(me, 'transitionTime')},
|
67
|
+
maxValue : 900,
|
68
|
+
minValue : 50,
|
69
|
+
stepSize : 50,
|
70
|
+
value : exampleComponent.transitionTime
|
71
|
+
}]
|
72
|
+
}
|
73
|
+
|
74
|
+
createExampleComponent() {
|
75
|
+
return {
|
76
|
+
module: MagicMoveText,
|
77
|
+
|
78
|
+
cycleTexts: [
|
79
|
+
'Magic Move',
|
80
|
+
'Move characters like magic',
|
81
|
+
'Animate between strings',
|
82
|
+
'Just like that',
|
83
|
+
'Simple to use',
|
84
|
+
'Would you use it?'
|
85
|
+
]
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
export default Neo.setupClass(MainContainer);
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>Neo Magic Move - Text</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<script src="../../../src/MicroLoader.mjs" type="module"></script>
|
10
|
+
</body>
|
11
|
+
</html>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.23.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -61,7 +61,7 @@
|
|
61
61
|
"monaco-editor": "0.50.0",
|
62
62
|
"neo-jsdoc": "1.0.1",
|
63
63
|
"neo-jsdoc-x": "1.0.5",
|
64
|
-
"postcss": "^8.5.
|
64
|
+
"postcss": "^8.5.3",
|
65
65
|
"sass": "^1.85.0",
|
66
66
|
"siesta-lite": "5.5.2",
|
67
67
|
"url": "^0.11.4",
|
@@ -0,0 +1,45 @@
|
|
1
|
+
.neo-magic-move-text {
|
2
|
+
--neo-height : 100px;
|
3
|
+
--neo-transition-time: 500ms;
|
4
|
+
--neo-width : 450px;
|
5
|
+
|
6
|
+
align-items : center;
|
7
|
+
background-color: #000;
|
8
|
+
color : #fff;
|
9
|
+
display : flex;
|
10
|
+
font-size : 30px;
|
11
|
+
font-weight : bold;
|
12
|
+
height : var(--neo-height);
|
13
|
+
justify-content : center;
|
14
|
+
width : var(--neo-width);
|
15
|
+
|
16
|
+
.neo-content {
|
17
|
+
height : 100%;
|
18
|
+
position: relative;
|
19
|
+
width : 100%;
|
20
|
+
|
21
|
+
> * {
|
22
|
+
position : absolute;
|
23
|
+
transition:
|
24
|
+
color 0.2s ease-out,
|
25
|
+
left var(--neo-transition-time) ease-out,
|
26
|
+
opacity var(--neo-transition-time) ease-out;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
.neo-measure-element-wrapper {
|
31
|
+
align-items : center;
|
32
|
+
background-color: #000;
|
33
|
+
display : flex;
|
34
|
+
height : var(--neo-height);
|
35
|
+
justify-content: center;
|
36
|
+
left : -5000px;
|
37
|
+
position : absolute;
|
38
|
+
top : -5000px;
|
39
|
+
width : var(--neo-width);
|
40
|
+
}
|
41
|
+
|
42
|
+
.neo-measure-element {
|
43
|
+
position: relative;
|
44
|
+
}
|
45
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
.neo-examples-configuration-viewport {
|
2
|
+
transition: width .5s ease-in-out;
|
3
|
+
|
4
|
+
@media (max-width: 450px) {
|
5
|
+
overflow-y: auto;
|
6
|
+
|
7
|
+
.neo-example-container {
|
8
|
+
flex: none !important;
|
9
|
+
}
|
10
|
+
|
11
|
+
&.neo-flex-container {
|
12
|
+
&.neo-flex-direction-row {
|
13
|
+
flex-direction: column;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
.neo-configuration-header-toolbar {
|
19
|
+
border : 0;
|
20
|
+
border-bottom: 1px solid var(--panel-border-color);
|
21
|
+
}
|
22
|
+
|
23
|
+
.neo-configuration-panel-body {
|
24
|
+
overflow-y: auto;
|
25
|
+
padding : 10px;
|
26
|
+
}
|
27
|
+
}
|
package/src/DefaultConfig.mjs
CHANGED
@@ -263,12 +263,12 @@ const DefaultConfig = {
|
|
263
263
|
useVdomWorker: true,
|
264
264
|
/**
|
265
265
|
* buildScripts/injectPackageVersion.mjs will update this value
|
266
|
-
* @default '8.
|
266
|
+
* @default '8.23.0'
|
267
267
|
* @memberOf! module:Neo
|
268
268
|
* @name config.version
|
269
269
|
* @type String
|
270
270
|
*/
|
271
|
-
version: '8.
|
271
|
+
version: '8.23.0'
|
272
272
|
};
|
273
273
|
|
274
274
|
Object.assign(DefaultConfig, {
|
@@ -0,0 +1,306 @@
|
|
1
|
+
import Component from '../component/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Deeply inspired by https://github.com/yangshun 's video on LinkedIn
|
5
|
+
* as well as Apple's Keynote Magic Move effect
|
6
|
+
* @class Neo.component.MagicMoveText
|
7
|
+
* @extends Neo.component.Base
|
8
|
+
*/
|
9
|
+
class MagicMoveText extends Component {
|
10
|
+
static config = {
|
11
|
+
/**
|
12
|
+
* @member {String} className='Neo.component.MagicMoveText'
|
13
|
+
* @protected
|
14
|
+
*/
|
15
|
+
className: 'Neo.component.MagicMoveText',
|
16
|
+
/**
|
17
|
+
* @member {String} ntype='magic-move-text'
|
18
|
+
* @protected
|
19
|
+
*/
|
20
|
+
ntype: 'magic-move-text',
|
21
|
+
/**
|
22
|
+
* @member {Boolean} autoCycle_=true
|
23
|
+
*/
|
24
|
+
autoCycle_: true,
|
25
|
+
/**
|
26
|
+
* @member {Number} autoCycleInterval_=2000
|
27
|
+
*/
|
28
|
+
autoCycleInterval_: 2000,
|
29
|
+
/**
|
30
|
+
* @member {String[]} baseCls=['neo-magic-move-text']
|
31
|
+
* @protected
|
32
|
+
*/
|
33
|
+
baseCls: ['neo-magic-move-text'],
|
34
|
+
/**
|
35
|
+
* @member {String|null} colorMove=null
|
36
|
+
*/
|
37
|
+
colorMove: null,
|
38
|
+
/**
|
39
|
+
* @member {String|null} colorFadeIn=null
|
40
|
+
*/
|
41
|
+
colorFadeIn: null,
|
42
|
+
/**
|
43
|
+
* @member {String|null} colorFadeOut=null
|
44
|
+
*/
|
45
|
+
colorFadeOut: null,
|
46
|
+
/**
|
47
|
+
* @member {String[]|null} cycleTexts=null
|
48
|
+
*/
|
49
|
+
cycleTexts: null,
|
50
|
+
/**
|
51
|
+
* @member {String} fontFamily_='Helvetica Neue'
|
52
|
+
*/
|
53
|
+
fontFamily_: 'Helvetica Neue',
|
54
|
+
/**
|
55
|
+
* @member {String} text_=null
|
56
|
+
*/
|
57
|
+
text_: null,
|
58
|
+
/**
|
59
|
+
* Time in ms for the fadeIn, fadeOut and move character OPs
|
60
|
+
* @member {Number} transitionTime_=500
|
61
|
+
*/
|
62
|
+
transitionTime_: 500,
|
63
|
+
/**
|
64
|
+
* @member {Object} _vdom
|
65
|
+
*/
|
66
|
+
_vdom:
|
67
|
+
{style: {}, cn: [
|
68
|
+
{cls: ['neo-content'], cn: []},
|
69
|
+
{cls: ['neo-measure-element-wrapper'], removeDom: true, cn: [
|
70
|
+
{cls: ['neo-measure-element'], cn:[]}
|
71
|
+
]}
|
72
|
+
]}
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @member {Object[]} chars=[]
|
77
|
+
*/
|
78
|
+
chars = []
|
79
|
+
/**
|
80
|
+
* @member {Number} currentIndex=0
|
81
|
+
*/
|
82
|
+
currentIndex = 0
|
83
|
+
/**
|
84
|
+
* @member {Number|null} intervalId=null
|
85
|
+
*/
|
86
|
+
intervalId = null
|
87
|
+
/**
|
88
|
+
* @member {Object[]} previousChars=[]
|
89
|
+
*/
|
90
|
+
previousChars = []
|
91
|
+
/**
|
92
|
+
* @member {Object} measureElement
|
93
|
+
*/
|
94
|
+
get measureElement() {
|
95
|
+
return this.vdom.cn[1].cn[0]
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Triggered after the autoCycle config got changed
|
100
|
+
* @param {Boolean} value
|
101
|
+
* @param {Boolean} oldValue
|
102
|
+
* @protected
|
103
|
+
*/
|
104
|
+
afterSetAutoCycle(value, oldValue) {
|
105
|
+
this.mounted && this.startAutoCycle(value)
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Triggered after the autoCycleInterval config got changed
|
110
|
+
* @param {Number} value
|
111
|
+
* @param {Number} oldValue
|
112
|
+
* @protected
|
113
|
+
*/
|
114
|
+
afterSetAutoCycleInterval(value, oldValue) {
|
115
|
+
let me = this;
|
116
|
+
|
117
|
+
if (oldValue && me.mounted) {
|
118
|
+
me.startAutoCycle(false);
|
119
|
+
me.startAutoCycle()
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Triggered after the fontFamily config got changed
|
125
|
+
* @param {String} value
|
126
|
+
* @param {String} oldValue
|
127
|
+
* @protected
|
128
|
+
*/
|
129
|
+
afterSetFontFamily(value, oldValue) {
|
130
|
+
this.vdom.style.fontFamily = value;
|
131
|
+
this.update()
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Triggered after the mounted config got changed
|
136
|
+
* @param {Boolean} value
|
137
|
+
* @param {Boolean} oldValue
|
138
|
+
* @protected
|
139
|
+
*/
|
140
|
+
afterSetMounted(value, oldValue) {
|
141
|
+
super.afterSetMounted(value, oldValue);
|
142
|
+
this.autoCycle && this.startAutoCycle(value)
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Triggered after the text config got changed
|
147
|
+
* @param {String} value
|
148
|
+
* @param {String} oldValue
|
149
|
+
* @returns {Promise<void>}
|
150
|
+
* @protected
|
151
|
+
*/
|
152
|
+
async afterSetText(value, oldValue) {
|
153
|
+
let me = this,
|
154
|
+
{measureElement} = me;
|
155
|
+
|
156
|
+
if (oldValue) {
|
157
|
+
me.previousChars = me.chars
|
158
|
+
}
|
159
|
+
|
160
|
+
if (value) {
|
161
|
+
me.chars = [];
|
162
|
+
measureElement.cn = [];
|
163
|
+
|
164
|
+
value?.split('').forEach(char => {
|
165
|
+
me.chars.push({name: char});
|
166
|
+
|
167
|
+
if (char === ' ') {
|
168
|
+
char = ' '
|
169
|
+
}
|
170
|
+
|
171
|
+
measureElement.cn.push({tag: 'span', html: char})
|
172
|
+
});
|
173
|
+
|
174
|
+
if (me.mounted) {
|
175
|
+
await me.measureChars()
|
176
|
+
}
|
177
|
+
|
178
|
+
await me.updateChars()
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Triggered after the transitionTime config got changed
|
184
|
+
* @param {Number} value
|
185
|
+
* @param {Number} oldValue
|
186
|
+
* @protected
|
187
|
+
*/
|
188
|
+
afterSetTransitionTime(value, oldValue) {
|
189
|
+
this.vdom.style['--neo-transition-time'] = value + 'ms';
|
190
|
+
this.update()
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @returns {Promise<void>}
|
195
|
+
*/
|
196
|
+
async measureChars() {
|
197
|
+
let me = this,
|
198
|
+
{measureElement} = me,
|
199
|
+
parentRect, rects;
|
200
|
+
|
201
|
+
delete me.vdom.cn[1].removeDom;
|
202
|
+
|
203
|
+
await me.promiseUpdate();
|
204
|
+
await me.timeout(20);
|
205
|
+
|
206
|
+
rects = await me.getDomRect([me.vdom.cn[1].id, ...measureElement.cn.map(node => node.id)]);
|
207
|
+
parentRect = rects.shift();
|
208
|
+
|
209
|
+
rects.forEach((rect, index) => {
|
210
|
+
me.chars[index].left = `${rect.left - parentRect.left}px`;
|
211
|
+
me.chars[index].top = `${rect.top - parentRect.top }px`;
|
212
|
+
});
|
213
|
+
|
214
|
+
me.vdom.cn[1].removeDom = true;
|
215
|
+
await me.promiseUpdate()
|
216
|
+
}
|
217
|
+
|
218
|
+
/**
|
219
|
+
* @param {Boolean} start=true
|
220
|
+
*/
|
221
|
+
startAutoCycle(start=true) {
|
222
|
+
let me = this;
|
223
|
+
|
224
|
+
if (start) {
|
225
|
+
me.intervalId = setInterval(() => {
|
226
|
+
me.text = me.cycleTexts[me.currentIndex];
|
227
|
+
me.currentIndex = (me.currentIndex + 1) % me.cycleTexts.length
|
228
|
+
}, me.autoCycleInterval)
|
229
|
+
|
230
|
+
me.text && me.measureChars()
|
231
|
+
} else {
|
232
|
+
clearInterval(me.intervalId)
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* @returns {Promise<void>}
|
238
|
+
*/
|
239
|
+
async updateChars() {
|
240
|
+
let me = this,
|
241
|
+
{chars, previousChars} = me,
|
242
|
+
charsContainer = me.vdom.cn[0].cn,
|
243
|
+
letters = chars.map(char => char.name),
|
244
|
+
char, charNode, index;
|
245
|
+
|
246
|
+
previousChars.forEach((previousChar, previousIndex) => {
|
247
|
+
index = letters.indexOf(previousChar.name);
|
248
|
+
|
249
|
+
if (index > -1) {
|
250
|
+
charNode = charsContainer[previousIndex];
|
251
|
+
|
252
|
+
charNode.style.color = me.colorMove;
|
253
|
+
charNode.style.left = chars[index].left;
|
254
|
+
letters[index] = null
|
255
|
+
} else {
|
256
|
+
charNode = charsContainer[previousIndex];
|
257
|
+
|
258
|
+
charNode.flag = 'remove'
|
259
|
+
}
|
260
|
+
});
|
261
|
+
|
262
|
+
letters.forEach((letter, index) => {
|
263
|
+
if (letter !== null) {
|
264
|
+
char = chars[index];
|
265
|
+
|
266
|
+
charsContainer.push({
|
267
|
+
html : char.name,
|
268
|
+
style: {color: me.colorFadeIn, left: char.left, opacity: 0, top: char.top}
|
269
|
+
})
|
270
|
+
}
|
271
|
+
});
|
272
|
+
|
273
|
+
await me.promiseUpdate();
|
274
|
+
|
275
|
+
charsContainer.forEach(charNode => {
|
276
|
+
if (charNode.flag === 'remove') {
|
277
|
+
charNode.style.color = me.colorFadeOut;
|
278
|
+
charNode.style.opacity = 0
|
279
|
+
} else {
|
280
|
+
charNode.style.opacity = 1
|
281
|
+
}
|
282
|
+
});
|
283
|
+
|
284
|
+
await me.promiseUpdate();
|
285
|
+
await me.timeout(me.transitionTime);
|
286
|
+
|
287
|
+
charsContainer.sort((a, b) => parseFloat(a.style.left) - parseFloat(b.style.left));
|
288
|
+
|
289
|
+
index = charsContainer.length - 1;
|
290
|
+
|
291
|
+
for (; index >= 0; index--) {
|
292
|
+
charNode = charsContainer[index];
|
293
|
+
|
294
|
+
delete charNode.flag;
|
295
|
+
delete charNode.style.color;
|
296
|
+
|
297
|
+
if (charNode.style.opacity === 0) {
|
298
|
+
charsContainer.splice(index, 1)
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
await me.promiseUpdate()
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
export default Neo.setupClass(MagicMoveText);
|
package/src/data/Store.mjs
CHANGED
@@ -330,6 +330,18 @@ class Store extends Base {
|
|
330
330
|
return this.keyProperty || this.model.keyProperty
|
331
331
|
}
|
332
332
|
|
333
|
+
/**
|
334
|
+
* Convenience shortcut to check for int based keyProperties
|
335
|
+
* @returns {String|null} lowercase value of the model field type
|
336
|
+
*/
|
337
|
+
getKeyType() {
|
338
|
+
let me = this,
|
339
|
+
{model} = me,
|
340
|
+
keyField = model?.getField(me.getKeyProperty());
|
341
|
+
|
342
|
+
return keyField?.type?.toLowerCase() || null
|
343
|
+
}
|
344
|
+
|
333
345
|
/**
|
334
346
|
* @param {Object} opts={}
|
335
347
|
* @param {Object} opts.data
|
package/src/grid/Container.mjs
CHANGED
@@ -522,7 +522,7 @@ class GridContainer extends BaseContainer {
|
|
522
522
|
if (!me.initialResizeEvent) {
|
523
523
|
await me.passSizeToView(true);
|
524
524
|
|
525
|
-
me.view.
|
525
|
+
me.view.updateMountedAndVisibleColumns();
|
526
526
|
|
527
527
|
await me.headerToolbar.passSizeToView()
|
528
528
|
} else {
|
package/src/grid/View.mjs
CHANGED
@@ -81,6 +81,12 @@ class GridView extends Component {
|
|
81
81
|
* @member {Object} keys
|
82
82
|
*/
|
83
83
|
keys: {},
|
84
|
+
/**
|
85
|
+
* Stores the indexes of the first & last mounted columns, including bufferColumnRange
|
86
|
+
* @member {Number[]} mountedColumns_=[0,0]
|
87
|
+
* @protected
|
88
|
+
*/
|
89
|
+
mountedColumns_: [0, 0],
|
84
90
|
/**
|
85
91
|
* Stores the indexes of the first & last mounted rows, including bufferRowRange
|
86
92
|
* @member {Number[]} mountedRows=[0,0]
|
@@ -118,10 +124,10 @@ class GridView extends Component {
|
|
118
124
|
store_: null,
|
119
125
|
/**
|
120
126
|
* Stores the indexes of the first & last painted columns
|
121
|
-
* @member {Number[]}
|
127
|
+
* @member {Number[]} visibleColumns=[0,0]
|
122
128
|
* @protected
|
123
129
|
*/
|
124
|
-
|
130
|
+
visibleColumns: [0, 0],
|
125
131
|
/**
|
126
132
|
* Stores the indexes of the first & last visible rows, excluding bufferRowRange
|
127
133
|
* @member {Number[]} visibleRows=[0,0]
|
@@ -229,9 +235,7 @@ class GridView extends Component {
|
|
229
235
|
* @protected
|
230
236
|
*/
|
231
237
|
afterSetAvailableRows(value, oldValue) {
|
232
|
-
|
233
|
-
this.createViewData()
|
234
|
-
}
|
238
|
+
value > 0 && this.createViewData()
|
235
239
|
}
|
236
240
|
|
237
241
|
/**
|
@@ -277,9 +281,7 @@ class GridView extends Component {
|
|
277
281
|
* @protected
|
278
282
|
*/
|
279
283
|
afterSetContainerWidth(value, oldValue) {
|
280
|
-
|
281
|
-
this.updateVisibleColumns()
|
282
|
-
}
|
284
|
+
value > 0 && this.updateMountedAndVisibleColumns()
|
283
285
|
}
|
284
286
|
|
285
287
|
/**
|
@@ -305,6 +307,16 @@ class GridView extends Component {
|
|
305
307
|
this.toggleCls('neo-is-scrolling', value)
|
306
308
|
}
|
307
309
|
|
310
|
+
/**
|
311
|
+
* Triggered after the mountedColumns config got changed
|
312
|
+
* @param {Number[]} value
|
313
|
+
* @param {Number[]} oldValue
|
314
|
+
* @protected
|
315
|
+
*/
|
316
|
+
afterSetMountedColumns(value, oldValue) {
|
317
|
+
oldValue !== undefined && this.createViewData()
|
318
|
+
}
|
319
|
+
|
308
320
|
/**
|
309
321
|
* Triggered after the rowHeight config got changed
|
310
322
|
* @param {Number} value
|
@@ -327,7 +339,7 @@ class GridView extends Component {
|
|
327
339
|
newStartIndex;
|
328
340
|
|
329
341
|
if (value.x !== oldValue?.x) {
|
330
|
-
me.
|
342
|
+
me.updateMountedAndVisibleColumns()
|
331
343
|
}
|
332
344
|
|
333
345
|
if (value.y !== oldValue?.y) {
|
@@ -362,18 +374,6 @@ class GridView extends Component {
|
|
362
374
|
oldValue !== undefined && this.createViewData()
|
363
375
|
}
|
364
376
|
|
365
|
-
/**
|
366
|
-
* Triggered after the visibleColumns config got changed
|
367
|
-
* @param {Number[]} value
|
368
|
-
* @param {Number[]} oldValue
|
369
|
-
* @protected
|
370
|
-
*/
|
371
|
-
afterSetVisibleColumns(value, oldValue) {
|
372
|
-
if (oldValue !== undefined) {
|
373
|
-
this.createViewData()
|
374
|
-
}
|
375
|
-
}
|
376
|
-
|
377
377
|
/**
|
378
378
|
* @param {Object} data
|
379
379
|
* @param {String} [data.cellId]
|
@@ -516,12 +516,12 @@ class GridView extends Component {
|
|
516
516
|
}
|
517
517
|
|
518
518
|
let me = this,
|
519
|
-
{
|
519
|
+
{mountedColumns, selectedRows} = me,
|
520
520
|
gridContainer = me.parent,
|
521
521
|
columns = gridContainer.headerToolbar.items,
|
522
522
|
id = me.getRowId(record, rowIndex),
|
523
523
|
rowCls = me.getRowClass(record, rowIndex),
|
524
|
-
config, column, columnPosition,
|
524
|
+
config, column, columnPosition, gridRow, i;
|
525
525
|
|
526
526
|
if (rowIndex % 2 !== 0) {
|
527
527
|
rowCls.push('neo-even')
|
@@ -549,10 +549,7 @@ class GridView extends Component {
|
|
549
549
|
}
|
550
550
|
};
|
551
551
|
|
552
|
-
|
553
|
-
startIndex = Math.max(0, visibleColumns[0] - bufferColumnRange);
|
554
|
-
|
555
|
-
for (i=startIndex; i <= endIndex; i++) {
|
552
|
+
for (i=mountedColumns[0]; i <= mountedColumns[1]; i++) {
|
556
553
|
column = columns[i];
|
557
554
|
config = me.applyRendererOutput({column, columnIndex: i, record, rowIndex});
|
558
555
|
|
@@ -593,7 +590,7 @@ class GridView extends Component {
|
|
593
590
|
me.availableRows < 1 ||
|
594
591
|
me._containerWidth < 1 || // we are not checking me.containerWidth, since we want to ignore the config symbol
|
595
592
|
me.columnPositions.getCount() < 1 ||
|
596
|
-
me.
|
593
|
+
me.mountedColumns[1] < 1
|
597
594
|
) {
|
598
595
|
return
|
599
596
|
}
|
@@ -729,6 +726,7 @@ class GridView extends Component {
|
|
729
726
|
|
730
727
|
/**
|
731
728
|
* Get the matching record by passing a row id, a cell id or an id inside a grid cell.
|
729
|
+
* Limited to mounted rows (must be inside the vdom).
|
732
730
|
* @param {String} nodeId
|
733
731
|
* @returns {Object|null}
|
734
732
|
*/
|
@@ -743,7 +741,7 @@ class GridView extends Component {
|
|
743
741
|
|
744
742
|
parentNodes = VDomUtil.getParentNodes(me.vdom, nodeId);
|
745
743
|
|
746
|
-
for (node of parentNodes) {
|
744
|
+
for (node of parentNodes || []) {
|
747
745
|
record = me.getRecordByRowId(node.id);
|
748
746
|
|
749
747
|
if (record) {
|
@@ -754,6 +752,22 @@ class GridView extends Component {
|
|
754
752
|
return null
|
755
753
|
}
|
756
754
|
|
755
|
+
/**
|
756
|
+
* @param {String} cellId
|
757
|
+
* @returns {Record}
|
758
|
+
*/
|
759
|
+
getRecordByCellId(cellId) {
|
760
|
+
let recordId = cellId.split('__')[1],
|
761
|
+
{store} = this,
|
762
|
+
keyType = store.getKeyType();
|
763
|
+
|
764
|
+
if (keyType === 'int' || keyType === 'integer') {
|
765
|
+
recordId = parseInt(recordId)
|
766
|
+
}
|
767
|
+
|
768
|
+
return store.get(recordId)
|
769
|
+
}
|
770
|
+
|
757
771
|
/**
|
758
772
|
* @param {String} rowId
|
759
773
|
* @returns {Record}
|
@@ -761,9 +775,7 @@ class GridView extends Component {
|
|
761
775
|
getRecordByRowId(rowId) {
|
762
776
|
let recordId = rowId.split('__')[2],
|
763
777
|
{store} = this,
|
764
|
-
|
765
|
-
keyField = model?.getField(store.getKeyProperty()),
|
766
|
-
keyType = keyField?.type?.toLowerCase();
|
778
|
+
keyType = store.getKeyType();
|
767
779
|
|
768
780
|
if (keyType === 'int' || keyType === 'integer') {
|
769
781
|
recordId = parseInt(recordId)
|
@@ -1020,16 +1032,45 @@ class GridView extends Component {
|
|
1020
1032
|
}
|
1021
1033
|
|
1022
1034
|
/**
|
1023
|
-
*
|
1035
|
+
*
|
1024
1036
|
*/
|
1025
|
-
|
1026
|
-
let me
|
1027
|
-
|
1028
|
-
{
|
1037
|
+
updateMountedAndVisibleColumns() {
|
1038
|
+
let me = this,
|
1039
|
+
{bufferColumnRange, columnPositions, visibleColumns} = me,
|
1040
|
+
{x} = me.scrollPosition,
|
1041
|
+
i = 0,
|
1042
|
+
len = columnPositions.getCount(),
|
1043
|
+
endIndex = len - 1,
|
1044
|
+
column, startIndex;
|
1029
1045
|
|
1030
|
-
if (
|
1031
|
-
|
1032
|
-
|
1046
|
+
if (len < 1) {
|
1047
|
+
return
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
for (; i < len; i++) {
|
1051
|
+
column = columnPositions.getAt(i);
|
1052
|
+
|
1053
|
+
if (x >= column.x && x <= column.x + column.width) {
|
1054
|
+
startIndex = i
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if (me.containerWidth + x < column.x) {
|
1058
|
+
endIndex = i - 1;
|
1059
|
+
break
|
1060
|
+
}
|
1061
|
+
}
|
1062
|
+
|
1063
|
+
if (
|
1064
|
+
Math.abs(startIndex - me.visibleColumns[0]) >= me.bufferColumnRange ||
|
1065
|
+
me.visibleColumns[1] < 1 // initial call
|
1066
|
+
) {
|
1067
|
+
visibleColumns[0] = startIndex;
|
1068
|
+
visibleColumns[1] = endIndex;
|
1069
|
+
|
1070
|
+
endIndex = Math.min(len - 1, visibleColumns[1] + bufferColumnRange);
|
1071
|
+
startIndex = Math.max(0, visibleColumns[0] - bufferColumnRange);
|
1072
|
+
|
1073
|
+
me.mountedColumns = [startIndex, endIndex]
|
1033
1074
|
}
|
1034
1075
|
}
|
1035
1076
|
|
@@ -1053,39 +1094,16 @@ class GridView extends Component {
|
|
1053
1094
|
}
|
1054
1095
|
|
1055
1096
|
/**
|
1056
|
-
*
|
1097
|
+
* @param {Boolean} silent=false
|
1057
1098
|
*/
|
1058
|
-
|
1059
|
-
let me
|
1060
|
-
|
1061
|
-
{
|
1062
|
-
i = 0,
|
1063
|
-
len = columnPositions.getCount(),
|
1064
|
-
endIndex = len - 1,
|
1065
|
-
column, startIndex;
|
1066
|
-
|
1067
|
-
if (len < 1) {
|
1068
|
-
return
|
1069
|
-
}
|
1070
|
-
|
1071
|
-
for (; i < len; i++) {
|
1072
|
-
column = columnPositions.getAt(i);
|
1073
|
-
|
1074
|
-
if (x >= column.x && x <= column.x + column.width) {
|
1075
|
-
startIndex = i
|
1076
|
-
}
|
1077
|
-
|
1078
|
-
if (me.containerWidth + x < column.x) {
|
1079
|
-
endIndex = i - 1;
|
1080
|
-
break
|
1081
|
-
}
|
1082
|
-
}
|
1099
|
+
updateScrollHeight(silent=false) {
|
1100
|
+
let me = this,
|
1101
|
+
countRecords = me.store.getCount(),
|
1102
|
+
{rowHeight} = me;
|
1083
1103
|
|
1084
|
-
if (
|
1085
|
-
|
1086
|
-
me.
|
1087
|
-
) {
|
1088
|
-
me.visibleColumns = [startIndex, endIndex]
|
1104
|
+
if (countRecords > 0 && rowHeight > 0) {
|
1105
|
+
me.vdom.cn[0].height = `${(countRecords + 1) * rowHeight}px`;
|
1106
|
+
!silent && me.update()
|
1089
1107
|
}
|
1090
1108
|
}
|
1091
1109
|
}
|
@@ -112,10 +112,10 @@ class CellModel extends BaseModel {
|
|
112
112
|
{view} = me,
|
113
113
|
{store} = view,
|
114
114
|
currentIndex = 0,
|
115
|
-
dataField, newIndex;
|
115
|
+
dataField, newIndex, record;
|
116
116
|
|
117
117
|
if (me.hasSelection()) {
|
118
|
-
currentIndex = store.indexOf(view.
|
118
|
+
currentIndex = store.indexOf(view.getRecordByCellId(me.items[0]));
|
119
119
|
dataField = view.getDataField(me.items[0])
|
120
120
|
} else {
|
121
121
|
dataField = me.dataFields[0]
|
@@ -127,7 +127,11 @@ class CellModel extends BaseModel {
|
|
127
127
|
newIndex += store.getCount()
|
128
128
|
}
|
129
129
|
|
130
|
-
|
130
|
+
record = store.getAt(newIndex);
|
131
|
+
|
132
|
+
me.select(view.getCellId(record, dataField));
|
133
|
+
|
134
|
+
view.scrollByRows(currentIndex, step)
|
131
135
|
}
|
132
136
|
|
133
137
|
/**
|
@@ -1,25 +0,0 @@
|
|
1
|
-
.neo-configuration-panel {
|
2
|
-
transition: width .5s ease-in-out;
|
3
|
-
|
4
|
-
.neo-details-label {
|
5
|
-
background-color: #323232;
|
6
|
-
color : #ddd;
|
7
|
-
font-size : 13px;
|
8
|
-
margin : 10px;
|
9
|
-
padding : 10px;
|
10
|
-
white-space : normal;
|
11
|
-
}
|
12
|
-
|
13
|
-
.neo-toolbar {
|
14
|
-
border : 0;
|
15
|
-
border-bottom: 1px solid var(--panel-border-color);
|
16
|
-
}
|
17
|
-
}
|
18
|
-
|
19
|
-
.neo-configuration-panel-body {
|
20
|
-
overflow-y: scroll;
|
21
|
-
}
|
22
|
-
|
23
|
-
.neo-examples-tab-component {
|
24
|
-
background-color: var(--examples-tab-component-background-color);
|
25
|
-
}
|