@quicktvui/web-renderer 1.0.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.
Files changed (56) hide show
  1. package/package.json +24 -0
  2. package/src/adapters/es3-video-player.js +828 -0
  3. package/src/components/Modal.js +119 -0
  4. package/src/components/QtAnimationView.js +678 -0
  5. package/src/components/QtBaseComponent.js +165 -0
  6. package/src/components/QtFastListView.js +1920 -0
  7. package/src/components/QtFlexView.js +799 -0
  8. package/src/components/QtImage.js +203 -0
  9. package/src/components/QtItemFrame.js +239 -0
  10. package/src/components/QtItemStoreView.js +93 -0
  11. package/src/components/QtItemView.js +125 -0
  12. package/src/components/QtListView.js +331 -0
  13. package/src/components/QtLoadingView.js +55 -0
  14. package/src/components/QtPageRootView.js +19 -0
  15. package/src/components/QtPlayMark.js +168 -0
  16. package/src/components/QtProgressBar.js +199 -0
  17. package/src/components/QtQRCode.js +78 -0
  18. package/src/components/QtReplaceChild.js +149 -0
  19. package/src/components/QtRippleView.js +166 -0
  20. package/src/components/QtSeekBar.js +409 -0
  21. package/src/components/QtText.js +679 -0
  22. package/src/components/QtTransitionImage.js +170 -0
  23. package/src/components/QtView.js +706 -0
  24. package/src/components/QtWebView.js +613 -0
  25. package/src/components/TabsView.js +420 -0
  26. package/src/components/ViewPager.js +206 -0
  27. package/src/components/index.js +24 -0
  28. package/src/components/plugins/TextV2Component.js +70 -0
  29. package/src/components/plugins/index.js +7 -0
  30. package/src/core/SceneBuilder.js +58 -0
  31. package/src/core/TVFocusManager.js +2014 -0
  32. package/src/core/asyncLocalStorage.js +175 -0
  33. package/src/core/autoProxy.js +165 -0
  34. package/src/core/componentRegistry.js +84 -0
  35. package/src/core/constants.js +6 -0
  36. package/src/core/index.js +8 -0
  37. package/src/core/moduleUtils.js +36 -0
  38. package/src/core/patches.js +958 -0
  39. package/src/core/templateBinding.js +666 -0
  40. package/src/index.js +246 -0
  41. package/src/modules/AndroidDevelopModule.js +101 -0
  42. package/src/modules/AndroidDeviceModule.js +341 -0
  43. package/src/modules/AndroidNetworkModule.js +178 -0
  44. package/src/modules/AndroidSharedPreferencesModule.js +100 -0
  45. package/src/modules/ESDeviceInfoModule.js +450 -0
  46. package/src/modules/ESGroupDataModule.js +195 -0
  47. package/src/modules/ESIJKAudioPlayerModule.js +477 -0
  48. package/src/modules/ESLocalStorageModule.js +100 -0
  49. package/src/modules/ESLogModule.js +65 -0
  50. package/src/modules/ESModule.js +106 -0
  51. package/src/modules/ESNetworkSpeedModule.js +117 -0
  52. package/src/modules/ESToastModule.js +172 -0
  53. package/src/modules/EsNativeModule.js +117 -0
  54. package/src/modules/FastListModule.js +101 -0
  55. package/src/modules/FocusModule.js +145 -0
  56. package/src/modules/RuntimeDeviceModule.js +176 -0
@@ -0,0 +1,203 @@
1
+ // QtImage component for image handling
2
+ import { QtBaseComponent } from './QtBaseComponent'
3
+ import { registerComponent } from '../core/componentRegistry'
4
+
5
+ export class QtImage extends QtBaseComponent {
6
+ constructor(context, id, pId) {
7
+ super(context, id, pId)
8
+ this.tagName = 'img'
9
+ this.dom = document.createElement('img')
10
+
11
+ // Default values - 默认拉伸填满
12
+ this._resizeMode = 'fill'
13
+ this._currentSrc = ''
14
+
15
+ // Default styles for images - 默认拉伸填满
16
+ this.dom.style.objectFit = 'fill'
17
+ this.dom.style.display = 'block'
18
+
19
+ // Register for callUIFunction support
20
+ registerComponent(id, this)
21
+ }
22
+
23
+ // Handle src attribute via setter (called by HippyWebView.updateProperty)
24
+ set src(value) {
25
+ this._setSrc(value)
26
+ }
27
+
28
+ get src() {
29
+ return this.dom.src
30
+ }
31
+
32
+ // Handle source attribute (React Native style)
33
+ set source(value) {
34
+ this._setSrc(value)
35
+ }
36
+
37
+ // Handle resizeMode
38
+ set resizeMode(value) {
39
+ this._setResizeMode(value)
40
+ }
41
+
42
+ // Handle src attribute
43
+ updateProperty(key, value) {
44
+ switch (key) {
45
+ case 'src':
46
+ case 'source':
47
+ this._setSrc(value)
48
+ break
49
+ case 'resizeMode':
50
+ this._setResizeMode(value)
51
+ break
52
+ case 'tintColor':
53
+ this.dom.style.filter = `drop-shadow(0 0 0 ${value})`
54
+ break
55
+ default:
56
+ if (typeof value === 'string' && value.startsWith('${')) {
57
+ this.dom.setAttribute(key, value)
58
+ }
59
+ super.updateProperty(key, value)
60
+ }
61
+ }
62
+
63
+ _setSrc(value) {
64
+ console.log('[QtImage] _setSrc called with:', value, 'type:', typeof value)
65
+
66
+ if (!value) {
67
+ this.dom.src = ''
68
+ this.dom.style.backgroundImage = ''
69
+ return
70
+ }
71
+
72
+ // Handle different src formats
73
+ let srcValue = ''
74
+ if (typeof value === 'string') {
75
+ // Handle file:// protocol - convert to relative path
76
+ if (value.startsWith('file://')) {
77
+ let rest = value.slice(7)
78
+ rest = rest.replace(/^\/+/, '')
79
+ if (rest.startsWith('./')) rest = rest.slice(2)
80
+ srcValue = './' + rest
81
+ }
82
+ // Handle assets/ prefix - convert to relative path for web
83
+ else if (value.startsWith('assets/')) {
84
+ srcValue = './' + value
85
+ }
86
+ // Direct URL or path
87
+ else if (
88
+ value.startsWith('data:') ||
89
+ value.startsWith('http') ||
90
+ value.startsWith('/') ||
91
+ value.startsWith('.')
92
+ ) {
93
+ srcValue = value
94
+ } else {
95
+ // Assume it's a URL
96
+ srcValue = value
97
+ }
98
+ } else if (typeof value === 'object') {
99
+ // Handle {uri: '...'} format (React Native style)
100
+ if (value.uri) {
101
+ srcValue = value.uri
102
+ // Handle file:// protocol in uri
103
+ if (srcValue.startsWith('file://')) {
104
+ let rest = srcValue.slice(7)
105
+ rest = rest.replace(/^\/+/, '')
106
+ if (rest.startsWith('./')) rest = rest.slice(2)
107
+ srcValue = './' + rest
108
+ }
109
+ // Handle assets/ prefix in uri
110
+ else if (srcValue.startsWith('assets/')) {
111
+ srcValue = './' + srcValue
112
+ }
113
+ }
114
+ }
115
+
116
+ // Store for later use (e.g., repeat mode)
117
+ this._currentSrc = srcValue
118
+
119
+ // Handle repeat mode specially - use background image
120
+ if (this._resizeMode === 'repeat') {
121
+ this.dom.style.backgroundImage = `url(${srcValue})`
122
+ this.dom.src =
123
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' // 透明占位
124
+ } else {
125
+ this.dom.src = srcValue
126
+ this.dom.style.backgroundImage = ''
127
+ }
128
+
129
+ console.log('[QtImage] Set dom.src to:', this.dom.src)
130
+ }
131
+
132
+ _setResizeMode(value) {
133
+ // Store current resizeMode for reference in _setSrc
134
+ this._resizeMode = value
135
+
136
+ // Map resizeMode to CSS object-fit and related styles
137
+ // QTImageResizeMode enum values:
138
+ // - QT_IMAGE_RESIZE_MODE_CONTAIN = "contain"
139
+ // - QT_IMAGE_RESIZE_MODE_COVER = "cover"
140
+ // - QT_IMAGE_RESIZE_MODE_CENTER = "center"
141
+ // - QT_IMAGE_RESIZE_MODE_ORIGIN = "origin"
142
+ // - QT_IMAGE_RESIZE_MODE_FIT_XY = "fitxy"
143
+
144
+ // Reset position styles
145
+ this.dom.style.objectFit = ''
146
+ this.dom.style.objectPosition = ''
147
+ this.dom.style.backgroundSize = ''
148
+ this.dom.style.backgroundPosition = ''
149
+ this.dom.style.backgroundRepeat = ''
150
+
151
+ switch (value) {
152
+ case 'contain':
153
+ // 保持宽高比缩放,完整显示图片
154
+ this.dom.style.objectFit = 'contain'
155
+ break
156
+
157
+ case 'cover':
158
+ // 保持宽高比缩放,填满容器(可能裁剪)
159
+ this.dom.style.objectFit = 'cover'
160
+ break
161
+
162
+ case 'center':
163
+ // 居中显示,等比缩小适应容器(类似 contain,确保居中)
164
+ // Android 行为:如果图片大于容器则等比缩小,小于容器则原样居中
165
+ this.dom.style.objectFit = 'contain'
166
+ this.dom.style.objectPosition = 'center center'
167
+ break
168
+
169
+ case 'origin':
170
+ // 原始大小显示,从左上角开始
171
+ this.dom.style.objectFit = 'none'
172
+ this.dom.style.objectPosition = 'left top'
173
+ break
174
+
175
+ case 'fitxy':
176
+ // 拉伸填满容器,不保持宽高比
177
+ this.dom.style.objectFit = 'fill'
178
+ break
179
+
180
+ case 'stretch':
181
+ // 同 fitxy,拉伸填满
182
+ this.dom.style.objectFit = 'fill'
183
+ break
184
+
185
+ case 'repeat':
186
+ // 平铺重复 - 使用 CSS background 方式实现
187
+ if (this._currentSrc) {
188
+ this.dom.style.backgroundImage = `url(${this._currentSrc})`
189
+ this.dom.src =
190
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' // 透明占位
191
+ }
192
+ this.dom.style.objectFit = 'none'
193
+ this.dom.style.backgroundSize = 'auto'
194
+ this.dom.style.backgroundPosition = 'left top'
195
+ this.dom.style.backgroundRepeat = 'repeat'
196
+ break
197
+
198
+ default:
199
+ // 默认使用 cover
200
+ this.dom.style.objectFit = 'cover'
201
+ }
202
+ }
203
+ }
@@ -0,0 +1,239 @@
1
+ // QtItemFrame - A simple container component for item-frame
2
+ // Acts as a div wrapper, preserving all child elements
3
+ import { QtBaseComponent } from './QtBaseComponent'
4
+ import { registerComponent } from '../core/componentRegistry'
5
+
6
+ export class QtItemFrame extends QtBaseComponent {
7
+ constructor(context, id, pId) {
8
+ super(context, id, pId)
9
+ this.tagName = 'ItemFrameComponent'
10
+ this.dom = document.createElement('div')
11
+ this.dom.setAttribute('data-component-name', 'QtItemFrame')
12
+
13
+ this.dom.style.display = 'block'
14
+ this.dom.style.position = 'relative'
15
+ this.dom.style.boxSizing = 'border-box'
16
+
17
+ registerComponent(id, this)
18
+ }
19
+
20
+ defaultStyle() {
21
+ return {
22
+ display: 'block',
23
+ position: 'relative',
24
+ boxSizing: 'border-box',
25
+ }
26
+ }
27
+
28
+ // Handle property updates from Hippy renderer
29
+ updateProperty(key, value) {
30
+ // Handle focusable property - enable keyboard focus
31
+ if (key === 'focusable') {
32
+ if (value === true || value === 'true') {
33
+ this.dom.setAttribute('tabindex', '0')
34
+ this.dom.setAttribute('focusable', 'true')
35
+ this._focusable = true
36
+ } else {
37
+ this.dom.removeAttribute('tabindex')
38
+ this.dom.setAttribute('focusable', 'false')
39
+ this._focusable = false
40
+ }
41
+ return
42
+ }
43
+
44
+ // Handle focusScale property - store for focus animation
45
+ if (key === 'focusScale' || key === 'focusscale') {
46
+ this._focusScale = typeof value === 'number' ? value : parseFloat(value) || 1.1
47
+ this.dom.setAttribute('data-focus-scale', String(this._focusScale))
48
+ if (!this._focusListenerAdded) {
49
+ this._setupFocusAnimation()
50
+ }
51
+ return
52
+ }
53
+
54
+ // Handle itemShowShimmer property
55
+ if (key === 'itemShowShimmer' || key === 'itemshowshimmer') {
56
+ this.dom.setAttribute('data-show-shimmer', String(value))
57
+ return
58
+ }
59
+
60
+ // Handle hideShadow property
61
+ if (key === 'hideShadow' || key === 'hideshadow') {
62
+ this.dom.setAttribute('data-hide-shadow', String(value))
63
+ return
64
+ }
65
+
66
+ // Handle shimmerSize property
67
+ if (key === 'shimmerSize' || key === 'shimmersize') {
68
+ this.dom.setAttribute('data-shimmer-size', String(value))
69
+ return
70
+ }
71
+
72
+ if (key === 'flexStyle' || key === 'flexstyle') {
73
+ if (typeof value === 'object' && value !== null) {
74
+ this._applyFlexStyle(value)
75
+ } else if (typeof value === 'string' && value.startsWith('${')) {
76
+ this.dom.setAttribute(key, value)
77
+ }
78
+ return
79
+ }
80
+
81
+ if (key === 'layout') {
82
+ if (typeof value === 'string' && value.startsWith('${')) {
83
+ this.dom.setAttribute(key, value)
84
+ } else {
85
+ this._applyLayout(value)
86
+ }
87
+ return
88
+ }
89
+
90
+ if (typeof value === 'string' && value.startsWith('${')) {
91
+ this.dom.setAttribute(key, value)
92
+ }
93
+ super.updateProperty(key, value)
94
+ }
95
+
96
+ // Setup focus animation for scale effect
97
+ _setupFocusAnimation() {
98
+ this._focusListenerAdded = true
99
+ this.dom.addEventListener('focus', () => {
100
+ if (this._focusScale && this._focusScale !== 1) {
101
+ this.dom.style.transform = `scale(${this._focusScale})`
102
+ this.dom.style.transition = 'transform 0.2s ease-out'
103
+ this.dom.style.zIndex = '10'
104
+ }
105
+ })
106
+ this.dom.addEventListener('blur', () => {
107
+ this.dom.style.transform = 'scale(1)'
108
+ this.dom.style.zIndex = ''
109
+ })
110
+ }
111
+
112
+ // Support flexStyle attribute
113
+ set flexStyle(styleObj) {
114
+ this.props.flexStyle = styleObj
115
+ this._applyFlexStyle(styleObj)
116
+ }
117
+
118
+ get flexStyle() {
119
+ return this.props.flexStyle
120
+ }
121
+
122
+ // Apply flexStyle object to DOM
123
+ _applyFlexStyle(styleObj) {
124
+ if (!styleObj || typeof styleObj !== 'object') return
125
+
126
+ Object.keys(styleObj).forEach((key) => {
127
+ let value = styleObj[key]
128
+
129
+ if (typeof value === 'number') {
130
+ const needsPx = [
131
+ 'width',
132
+ 'height',
133
+ 'minWidth',
134
+ 'minHeight',
135
+ 'maxWidth',
136
+ 'maxHeight',
137
+ 'padding',
138
+ 'paddingTop',
139
+ 'paddingRight',
140
+ 'paddingBottom',
141
+ 'paddingLeft',
142
+ 'margin',
143
+ 'marginTop',
144
+ 'marginRight',
145
+ 'marginBottom',
146
+ 'marginLeft',
147
+ 'top',
148
+ 'left',
149
+ 'right',
150
+ 'bottom',
151
+ 'borderRadius',
152
+ 'borderWidth',
153
+ 'fontSize',
154
+ 'lineHeight',
155
+ 'letterSpacing',
156
+ 'gap',
157
+ 'rowGap',
158
+ 'columnGap',
159
+ ].includes(key)
160
+
161
+ if (needsPx) {
162
+ value = value + 'px'
163
+ }
164
+ }
165
+
166
+ try {
167
+ if (key !== 'x' && key !== 'y') {
168
+ this.dom.style[key] = value
169
+ }
170
+ } catch (e) {
171
+ try {
172
+ if (key !== 'x' && key !== 'y') {
173
+ const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()
174
+ this.dom.style.setProperty(cssKey, value)
175
+ }
176
+ } catch (err) {}
177
+ }
178
+ })
179
+ }
180
+
181
+ // Apply layout object/array to DOM
182
+ _applyLayout(layout) {
183
+ if (!layout) return
184
+
185
+ if (Array.isArray(layout)) {
186
+ const x = layout[0]
187
+ const y = layout[1]
188
+ const width = layout[2]
189
+ const height = layout[3]
190
+
191
+ if (x !== undefined && x !== null) {
192
+ this.dom.style.left = typeof x === 'number' ? `${x}px` : x
193
+ }
194
+ if (y !== undefined && y !== null) {
195
+ this.dom.style.top = typeof y === 'number' ? `${y}px` : y
196
+ }
197
+ if (width !== undefined && width !== null) {
198
+ this.dom.style.width = typeof width === 'number' ? `${width}px` : width
199
+ }
200
+ if (height !== undefined && height !== null) {
201
+ this.dom.style.height = typeof height === 'number' ? `${height}px` : height
202
+ }
203
+
204
+ if ((x !== undefined || y !== undefined) && !this.dom.style.position) {
205
+ this.dom.style.position = 'absolute'
206
+ }
207
+ // If position is relative (default for QtItemFrame), change to absolute if x/y are provided
208
+ if ((x !== undefined || y !== undefined) && this.dom.style.position === 'relative') {
209
+ this.dom.style.position = 'absolute'
210
+ }
211
+
212
+ return
213
+ }
214
+
215
+ if (typeof layout === 'object') {
216
+ const { x, y, width, height } = layout
217
+
218
+ if (x !== undefined && x !== null) {
219
+ this.dom.style.left = typeof x === 'number' ? `${x}px` : x
220
+ }
221
+ if (y !== undefined && y !== null) {
222
+ this.dom.style.top = typeof y === 'number' ? `${y}px` : y
223
+ }
224
+ if (width !== undefined && width !== null) {
225
+ this.dom.style.width = typeof width === 'number' ? `${width}px` : width
226
+ }
227
+ if (height !== undefined && height !== null) {
228
+ this.dom.style.height = typeof height === 'number' ? `${height}px` : height
229
+ }
230
+
231
+ if ((x !== undefined || y !== undefined) && !this.dom.style.position) {
232
+ this.dom.style.position = 'absolute'
233
+ }
234
+ if ((x !== undefined || y !== undefined) && this.dom.style.position === 'relative') {
235
+ this.dom.style.position = 'absolute'
236
+ }
237
+ }
238
+ }
239
+ }
@@ -0,0 +1,93 @@
1
+ // QtItemStoreView - A container component for shared item templates
2
+ // Used in waterfall to provide shared item templates across sections
3
+ import { QtBaseComponent } from './QtBaseComponent'
4
+ import { registerComponent } from '../core/componentRegistry'
5
+
6
+ function _qtGetSharedTemplateStore() {
7
+ const w = window
8
+ if (!w.__QT_SHARED_TEMPLATES__) {
9
+ w.__QT_SHARED_TEMPLATES__ = new Map()
10
+ }
11
+ return w.__QT_SHARED_TEMPLATES__
12
+ }
13
+
14
+ export class QtItemStoreView extends QtBaseComponent {
15
+ constructor(context, id, pId) {
16
+ super(context, id, pId)
17
+ this.tagName = 'ItemStoreView'
18
+ this.dom = document.createElement('div')
19
+ this.dom.setAttribute('data-component-name', 'QtItemStoreView')
20
+
21
+ this.dom.style.display = 'none' // Item store templates are hidden by default
22
+ this.dom.style.position = 'relative'
23
+ this.dom.style.boxSizing = 'border-box'
24
+
25
+ this._templateChildren = []
26
+
27
+ registerComponent(id, this)
28
+ }
29
+
30
+ defaultStyle() {
31
+ return {
32
+ display: 'none',
33
+ position: 'relative',
34
+ boxSizing: 'border-box',
35
+ }
36
+ }
37
+
38
+ // Get all template children for this item store
39
+ getTemplateChildren() {
40
+ return this._templateChildren
41
+ }
42
+
43
+ // Add a template child
44
+ addTemplateChild(template) {
45
+ if (template && template.dom && !this._templateChildren.some((t) => t.dom === template.dom)) {
46
+ // Extract type from the template
47
+ let type = null
48
+ if (template.props && template.props.type !== undefined) {
49
+ type = template.props.type
50
+ } else if (template.dom.hasAttribute('type')) {
51
+ const typeAttr = template.dom.getAttribute('type')
52
+ type = typeAttr !== null ? (isNaN(Number(typeAttr)) ? typeAttr : Number(typeAttr)) : null
53
+ }
54
+
55
+ this._templateChildren.push({
56
+ dom: template.dom,
57
+ tagName: template.tagName,
58
+ component: template,
59
+ type: type,
60
+ })
61
+
62
+ if (type !== null && type !== undefined) {
63
+ const store = _qtGetSharedTemplateStore()
64
+ store.set(type, {
65
+ dom: template.dom,
66
+ tagName: template.tagName,
67
+ component: template,
68
+ type,
69
+ })
70
+ }
71
+
72
+ template.dom.setAttribute('data-template', 'true')
73
+ template.dom.setAttribute('data-shared-template', 'true')
74
+ template.dom.style.display = 'none'
75
+ }
76
+ }
77
+
78
+ // Handle child mount - collect templates
79
+ async beforeChildMount(child, childPosition) {
80
+ if (child && child.dom) {
81
+ this.addTemplateChild(child)
82
+ }
83
+ await super.beforeChildMount(child, childPosition)
84
+ }
85
+
86
+ // Handle property updates from Hippy renderer
87
+ updateProperty(key, value) {
88
+ if (typeof value === 'string' && value.startsWith('${')) {
89
+ this.dom.setAttribute(key, value)
90
+ }
91
+ super.updateProperty(key, value)
92
+ }
93
+ }
@@ -0,0 +1,125 @@
1
+ // QtItemView - An item view container for waterfall sections
2
+ // Used by tv-item and FastItemView components
3
+ import { QtBaseComponent } from './QtBaseComponent'
4
+ import { registerComponent } from '../core/componentRegistry'
5
+
6
+ export class QtItemView extends QtBaseComponent {
7
+ constructor(context, id, pId) {
8
+ super(context, id, pId)
9
+ this.tagName = 'FastItemView'
10
+ this.dom = document.createElement('div')
11
+ this.dom.setAttribute('data-component-name', 'QtItemView')
12
+
13
+ this.dom.style.cssText = `
14
+ display: block;
15
+ position: relative;
16
+ box-sizing: border-box;
17
+ `
18
+
19
+ this.props = {}
20
+
21
+ registerComponent(id, this)
22
+ }
23
+
24
+ defaultStyle() {
25
+ return {
26
+ display: 'block',
27
+ position: 'relative',
28
+ boxSizing: 'border-box',
29
+ }
30
+ }
31
+
32
+ // Handle child elements (slot content)
33
+ insertChild(child, index) {
34
+ if (child && child.dom && !this.dom.contains(child.dom)) {
35
+ this.dom.appendChild(child.dom)
36
+ }
37
+ }
38
+
39
+ // Handle child elements before mount
40
+ async beforeChildMount(child, childPosition) {
41
+ if (child && child.dom && !this.dom.contains(child.dom)) {
42
+ this.dom.appendChild(child.dom)
43
+ }
44
+ await super.beforeChildMount(child, childPosition)
45
+ }
46
+
47
+ updateProperty(key, value) {
48
+ switch (key) {
49
+ case 'singleton':
50
+ // Store singleton flag for QtFastListView to detect
51
+ this.props = this.props || {}
52
+ this.props.singleton = true
53
+ this.dom.setAttribute('singleton', 'true')
54
+ break
55
+ case 'type':
56
+ // Store type for singleton templates to match with data
57
+ this.props = this.props || {}
58
+ this.props.type = value
59
+ this.dom.setAttribute('type', value)
60
+ break
61
+ case 'flexStyle':
62
+ case 'flexstyle':
63
+ if (typeof value === 'object' && value !== null) {
64
+ this._applyFlexStyle(value)
65
+ }
66
+ break
67
+ default:
68
+ super.updateProperty(key, value)
69
+ }
70
+ }
71
+
72
+ _applyFlexStyle(styleObj) {
73
+ if (!styleObj || typeof styleObj !== 'object') return
74
+
75
+ Object.keys(styleObj).forEach((key) => {
76
+ let value = styleObj[key]
77
+
78
+ if (typeof value === 'number') {
79
+ const needsPx = [
80
+ 'width',
81
+ 'height',
82
+ 'minWidth',
83
+ 'minHeight',
84
+ 'maxWidth',
85
+ 'maxHeight',
86
+ 'padding',
87
+ 'paddingTop',
88
+ 'paddingRight',
89
+ 'paddingBottom',
90
+ 'paddingLeft',
91
+ 'margin',
92
+ 'marginTop',
93
+ 'marginRight',
94
+ 'marginBottom',
95
+ 'marginLeft',
96
+ 'top',
97
+ 'left',
98
+ 'right',
99
+ 'bottom',
100
+ 'borderRadius',
101
+ 'borderWidth',
102
+ 'fontSize',
103
+ 'lineHeight',
104
+ 'letterSpacing',
105
+ 'gap',
106
+ 'rowGap',
107
+ 'columnGap',
108
+ ].includes(key)
109
+
110
+ if (needsPx) {
111
+ value = value + 'px'
112
+ }
113
+ }
114
+
115
+ try {
116
+ this.dom.style[key] = value
117
+ } catch (e) {
118
+ try {
119
+ const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()
120
+ this.dom.style.setProperty(cssKey, value)
121
+ } catch (err) {}
122
+ }
123
+ })
124
+ }
125
+ }