@mpxjs/webpack-plugin 2.10.18-beta.1 → 2.10.18-beta.10-titlebar
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/lib/index.js +1 -1
- package/lib/platform/style/wx/index.js +1 -1
- package/lib/platform/template/wx/component-config/titlebar.js +12 -0
- package/lib/react/LoadAsyncChunkModule.js +2 -5
- package/lib/runtime/components/react/dist/mpx-input.jsx +4 -1
- package/lib/runtime/components/react/mpx-input.tsx +6 -10
- package/lib/runtime/components/react/mpx-picker-view-column/index.tsx +10 -0
- package/lib/runtime/components/react/mpx-swiper.tsx +18 -3
- package/lib/runtime/components/react/utils.tsx +1 -1
- package/lib/runtime/components/web/mpx-titlebar.vue +248 -0
- package/lib/style-compiler/strip-conditional.js +12 -11
- package/lib/template-compiler/compiler.js +8 -1
- package/lib/web/processMainScript.js +3 -1
- package/package.json +1 -1
- package/lib/runtime/components/react/dist/mpx-icon/icons/cancel.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/clear.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/download.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/info.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/search.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/success_no_circle.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/waiting.png +0 -0
- package/lib/runtime/components/react/dist/mpx-icon/icons/warn.png +0 -0
package/lib/index.js
CHANGED
|
@@ -654,7 +654,7 @@ class MpxWebpackPlugin {
|
|
|
654
654
|
}, (chunk, set) => {
|
|
655
655
|
compilation.addRuntimeModule(
|
|
656
656
|
chunk,
|
|
657
|
-
new LoadAsyncChunkModule(
|
|
657
|
+
new LoadAsyncChunkModule()
|
|
658
658
|
)
|
|
659
659
|
return true
|
|
660
660
|
})
|
|
@@ -422,7 +422,7 @@ module.exports = function getSpec({ warn, error }) {
|
|
|
422
422
|
if (Array.isArray(value) || cssVariableExp.test(value)) return { prop, value }
|
|
423
423
|
const values = parseValues(value)
|
|
424
424
|
// Todo transform 排序不一致时,transform动画会闪烁,故这里同样的排序输出 transform
|
|
425
|
-
values.sort()
|
|
425
|
+
// values.sort()
|
|
426
426
|
const transform = []
|
|
427
427
|
values.forEach(item => {
|
|
428
428
|
const match = item.match(/([/\w]+)\((.+)\)/)
|
|
@@ -3,9 +3,8 @@ const Template = require('webpack/lib/Template')
|
|
|
3
3
|
const HelperRuntimeModule = require('webpack/lib/runtime/HelperRuntimeModule')
|
|
4
4
|
|
|
5
5
|
class LoadAsyncChunkRuntimeModule extends HelperRuntimeModule {
|
|
6
|
-
constructor (
|
|
6
|
+
constructor () {
|
|
7
7
|
super('load async chunk')
|
|
8
|
-
this.timeout = timeout || 10000
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
generate () {
|
|
@@ -32,7 +31,7 @@ class LoadAsyncChunkRuntimeModule extends HelperRuntimeModule {
|
|
|
32
31
|
]),
|
|
33
32
|
'}',
|
|
34
33
|
'inProgress[url] = [done]',
|
|
35
|
-
'var callback = function (type
|
|
34
|
+
'var callback = function (type) {',
|
|
36
35
|
Template.indent([
|
|
37
36
|
'var event = {',
|
|
38
37
|
Template.indent([
|
|
@@ -45,7 +44,6 @@ class LoadAsyncChunkRuntimeModule extends HelperRuntimeModule {
|
|
|
45
44
|
]),
|
|
46
45
|
Template.indent([
|
|
47
46
|
'var doneFns = inProgress[url]',
|
|
48
|
-
'clearTimeout(timeout)',
|
|
49
47
|
'delete inProgress[url]',
|
|
50
48
|
`doneFns && doneFns.forEach(${runtimeTemplate.returningFunction(
|
|
51
49
|
'fn(event)',
|
|
@@ -53,7 +51,6 @@ class LoadAsyncChunkRuntimeModule extends HelperRuntimeModule {
|
|
|
53
51
|
)})`
|
|
54
52
|
]),
|
|
55
53
|
'}',
|
|
56
|
-
`var timeout = setTimeout(callback.bind(null, 'timeout'), ${this.timeout})`,
|
|
57
54
|
`var loadChunkAsyncFn = ${RuntimeGlobals.global}.__mpx.config.rnConfig && ${RuntimeGlobals.global}.__mpx.config.rnConfig.loadChunkAsync`,
|
|
58
55
|
'try {',
|
|
59
56
|
Template.indent([
|
|
@@ -145,7 +145,10 @@ const Input = forwardRef((props, ref) => {
|
|
|
145
145
|
};
|
|
146
146
|
const setKeyboardAvoidContext = () => {
|
|
147
147
|
if (keyboardAvoid) {
|
|
148
|
-
|
|
148
|
+
// readyToShow 仅在从另一个输入框切换聚焦时为 true(ref 不同),
|
|
149
|
+
// 避免同一个输入框重复调用(onTouchStart + useEffect)或单次聚焦时误设为 true 导致无法正常失焦
|
|
150
|
+
const readyToShow = !!(keyboardAvoid.current && keyboardAvoid.current.ref !== nodeRef);
|
|
151
|
+
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition, holdKeyboard, readyToShow };
|
|
149
152
|
}
|
|
150
153
|
};
|
|
151
154
|
const onTouchStart = () => {
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
NativeTouchEvent
|
|
55
55
|
} from 'react-native'
|
|
56
56
|
import { warn } from '@mpxjs/utils'
|
|
57
|
-
import { useUpdateEffect, useTransformStyle, useLayout, extendObject,
|
|
57
|
+
import { useUpdateEffect, useTransformStyle, useLayout, extendObject, isIOS } from './utils'
|
|
58
58
|
import useInnerProps, { getCustomEvent } from './getInnerListeners'
|
|
59
59
|
import useNodesRef, { HandlerRef } from './useNodesRef'
|
|
60
60
|
import { FormContext, FormFieldValue, KeyboardAvoidContext } from './context'
|
|
@@ -285,7 +285,10 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
285
285
|
|
|
286
286
|
const setKeyboardAvoidContext = () => {
|
|
287
287
|
if (keyboardAvoid) {
|
|
288
|
-
|
|
288
|
+
// readyToShow 仅在从另一个输入框切换聚焦时为 true(ref 不同),
|
|
289
|
+
// 避免同一个输入框重复调用(onTouchStart + useEffect)或单次聚焦时误设为 true 导致无法正常失焦
|
|
290
|
+
const readyToShow = !!(keyboardAvoid.current && keyboardAvoid.current.ref !== nodeRef)
|
|
291
|
+
keyboardAvoid.current = { cursorSpacing, ref: nodeRef, adjustPosition, holdKeyboard, readyToShow }
|
|
289
292
|
}
|
|
290
293
|
}
|
|
291
294
|
|
|
@@ -469,12 +472,6 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
469
472
|
: (nodeRef.current as TextInput)?.blur()
|
|
470
473
|
}, [isAutoFocus])
|
|
471
474
|
|
|
472
|
-
// 使用 multiline 来修复光标位置问题
|
|
473
|
-
// React Native 的 TextInput 在 textAlign center + placeholder 时光标会跑到右边
|
|
474
|
-
// 这个问题只在 Android 上出现
|
|
475
|
-
// 参考:https://github.com/facebook/react-native/issues/28794 (Android only)
|
|
476
|
-
const needMultilineFix = isAndroid && !multiline
|
|
477
|
-
|
|
478
475
|
const innerProps = useInnerProps(
|
|
479
476
|
extendObject(
|
|
480
477
|
{},
|
|
@@ -498,7 +495,7 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
498
495
|
underlineColorAndroid: 'rgba(0,0,0,0)',
|
|
499
496
|
textAlignVertical: textAlignVertical,
|
|
500
497
|
placeholderTextColor: placeholderStyle?.color,
|
|
501
|
-
multiline: multiline
|
|
498
|
+
multiline: !!multiline,
|
|
502
499
|
onTouchStart,
|
|
503
500
|
onTouchEnd,
|
|
504
501
|
onFocus,
|
|
@@ -508,7 +505,6 @@ const Input = forwardRef<HandlerRef<TextInput, FinalInputProps>, FinalInputProps
|
|
|
508
505
|
onContentSizeChange,
|
|
509
506
|
onSubmitEditing: bindconfirm && onSubmitEditing
|
|
510
507
|
},
|
|
511
|
-
needMultilineFix ? { numberOfLines: 1 } : {},
|
|
512
508
|
!!multiline && confirmType === 'return' ? {} : { enterKeyHint: confirmType }
|
|
513
509
|
),
|
|
514
510
|
[
|
|
@@ -136,6 +136,16 @@ const _PickerViewColumn = forwardRef<HandlerRef<ScrollView & View, ColumnProps>,
|
|
|
136
136
|
}
|
|
137
137
|
}, [])
|
|
138
138
|
|
|
139
|
+
// `contentOffset` prop sets visual position but does not fire scroll events,
|
|
140
|
+
// so `offsetYShared` (from `useScrollViewOffset`) stays at 0 until the user scrolls.
|
|
141
|
+
// Directly sync it whenever `itemRawH` is established so wheel animation renders correctly.
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!itemRawH || dragging.current || scrolling.current) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
offsetYShared.value = activeIndex.current * itemRawH
|
|
147
|
+
}, [itemRawH])
|
|
148
|
+
|
|
139
149
|
useEffect(() => {
|
|
140
150
|
if (
|
|
141
151
|
!scrollViewRef.current ||
|
|
@@ -79,6 +79,7 @@ interface SwiperProps {
|
|
|
79
79
|
disableGesture?: boolean
|
|
80
80
|
'display-multiple-items'?: number
|
|
81
81
|
bindchange?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
82
|
+
bindchangestart?: (event: NativeSyntheticEvent<TouchEvent> | unknown) => void
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
/**
|
|
@@ -159,7 +160,8 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
159
160
|
circular = false,
|
|
160
161
|
disableGesture = false,
|
|
161
162
|
current: propCurrent = 0,
|
|
162
|
-
bindchange
|
|
163
|
+
bindchange,
|
|
164
|
+
bindchangestart
|
|
163
165
|
} = props
|
|
164
166
|
|
|
165
167
|
const dotCommonStyle = {
|
|
@@ -425,6 +427,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
425
427
|
nextIndex += 1
|
|
426
428
|
// targetOffset = -nextIndex * step.value - preMarginShared.value
|
|
427
429
|
targetOffset = -nextIndex * step.value
|
|
430
|
+
runOnJSCallback('handleSwiperChangeStart', nextIndex)
|
|
428
431
|
offset.value = withTiming(targetOffset, {
|
|
429
432
|
duration: easeDuration,
|
|
430
433
|
easing: easeMap[easeingFunc]
|
|
@@ -438,6 +441,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
438
441
|
nextIndex = 0
|
|
439
442
|
targetOffset = -(childrenLength.value + patchElmNumShared.value) * step.value + preMarginShared.value
|
|
440
443
|
// 执行动画到下一帧
|
|
444
|
+
runOnJSCallback('handleSwiperChangeStart', nextIndex)
|
|
441
445
|
offset.value = withTiming(targetOffset, {
|
|
442
446
|
duration: easeDuration
|
|
443
447
|
}, () => {
|
|
@@ -451,6 +455,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
451
455
|
nextIndex = currentIndex.value + 1
|
|
452
456
|
targetOffset = -(nextIndex + patchElmNumShared.value) * step.value + preMarginShared.value
|
|
453
457
|
// 执行动画到下一帧
|
|
458
|
+
runOnJSCallback('handleSwiperChangeStart', nextIndex)
|
|
454
459
|
offset.value = withTiming(targetOffset, {
|
|
455
460
|
duration: easeDuration,
|
|
456
461
|
easing: easeMap[easeingFunc]
|
|
@@ -489,11 +494,17 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
489
494
|
bindchange && bindchange(eventData)
|
|
490
495
|
}
|
|
491
496
|
|
|
497
|
+
function handleSwiperChangeStart (current: number) {
|
|
498
|
+
const eventData = getCustomEvent('changestart', {}, { detail: { current }, layoutRef: layoutRef })
|
|
499
|
+
bindchangestart && bindchangestart(eventData)
|
|
500
|
+
}
|
|
501
|
+
|
|
492
502
|
const runOnJSCallbackRef = useRef({
|
|
493
503
|
loop,
|
|
494
504
|
pauseLoop,
|
|
495
505
|
resumeLoop,
|
|
496
|
-
handleSwiperChange
|
|
506
|
+
handleSwiperChange,
|
|
507
|
+
handleSwiperChangeStart
|
|
497
508
|
})
|
|
498
509
|
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)
|
|
499
510
|
|
|
@@ -514,6 +525,7 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
514
525
|
if (targetOffset !== offset.value) {
|
|
515
526
|
// 内部基于props.current!==currentIndex.value决定是否使用动画及更新currentIndex.value
|
|
516
527
|
if (propCurrent !== undefined && propCurrent !== currentIndex.value) {
|
|
528
|
+
runOnJSCallback('handleSwiperChangeStart', propCurrent)
|
|
517
529
|
offset.value = withTiming(targetOffset, {
|
|
518
530
|
duration: easeDuration,
|
|
519
531
|
easing: easeMap[easeingFunc]
|
|
@@ -805,7 +817,10 @@ const SwiperWrapper = forwardRef<HandlerRef<View, SwiperProps>, SwiperProps>((pr
|
|
|
805
817
|
const offsetHalf = computeHalf()
|
|
806
818
|
if (childrenLength.value > 1 && offsetHalf) {
|
|
807
819
|
const { selectedIndex } = getTargetPosition({ transdir: moveDistance } as EventEndType)
|
|
808
|
-
currentIndex.value
|
|
820
|
+
if (selectedIndex !== currentIndex.value) {
|
|
821
|
+
currentIndex.value = selectedIndex
|
|
822
|
+
runOnJS(runOnJSCallback)('handleSwiperChangeStart', selectedIndex)
|
|
823
|
+
}
|
|
809
824
|
}
|
|
810
825
|
// 2. 非循环: 处理用户一直拖拽到临界点的场景,如果放到onFinalize无法阻止offset.value更新为越界的值
|
|
811
826
|
if (!circularShared.value) {
|
|
@@ -335,7 +335,7 @@ export function parseValues (str: string, char = ' ') {
|
|
|
335
335
|
function parseTransform (transformStr: string) {
|
|
336
336
|
const values = parseValues(transformStr)
|
|
337
337
|
// Todo transform 排序不一致时,transform动画会闪烁,故这里同样的排序输出 transform
|
|
338
|
-
values.sort()
|
|
338
|
+
// values.sort()
|
|
339
339
|
const transform: { [propName: string]: string | number | number[] }[] = []
|
|
340
340
|
values.forEach(item => {
|
|
341
341
|
const match = item.match(/([/\w]+)\((.+)\)/)
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import mpx from '@mpxjs/core'
|
|
3
|
+
|
|
4
|
+
const isIOS = /iP(hone|od|ad)/.test(navigator.userAgent)
|
|
5
|
+
const innerHeight = (isIOS ? 44 : 48) + 'px'
|
|
6
|
+
|
|
7
|
+
const safeStyle = { paddingTop: 'var(--safe-area-inset-top)' }
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
name: 'mpx-titlebar',
|
|
11
|
+
props: {
|
|
12
|
+
// 来自 app.json 中 window 的 titlebar 相关配置
|
|
13
|
+
windowConfig: {
|
|
14
|
+
type: Object,
|
|
15
|
+
default: () => global.__mpxPageConfig
|
|
16
|
+
},
|
|
17
|
+
// 来自 页面 json 中的 titlebar 相关配置,会覆盖 windowConfig
|
|
18
|
+
pageConfig: {
|
|
19
|
+
type: Object,
|
|
20
|
+
default: () => ({})
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
computed: {
|
|
24
|
+
// 合并全局 window 配置与页面配置(页面配置覆盖全局配置)
|
|
25
|
+
cfg() {
|
|
26
|
+
return Object.assign({}, this.windowConfig || {}, this.pageConfig || {})
|
|
27
|
+
},
|
|
28
|
+
// 标题文本(兼容常见字段名)
|
|
29
|
+
titleText() {
|
|
30
|
+
return this.cfg.navigationBarTitleText || this.cfg.title || ''
|
|
31
|
+
},
|
|
32
|
+
// 背景色(兼容常见字段)
|
|
33
|
+
backgroundColor() {
|
|
34
|
+
return this.cfg.navigationBarBackgroundColor || '#ffffff'
|
|
35
|
+
},
|
|
36
|
+
// 文本颜色,微信小程序中 navigationBarTextStyle 为 white 或 black
|
|
37
|
+
textColor() {
|
|
38
|
+
const style = this.cfg.navigationBarTextStyle || 'black'
|
|
39
|
+
return style === 'white' ? '#ffffff' : '#000000'
|
|
40
|
+
},
|
|
41
|
+
// navigationStyle: 'default' | 'custom',custom 表示需要自定义绘制
|
|
42
|
+
navigationStyle() {
|
|
43
|
+
return this.cfg.navigationStyle || 'default'
|
|
44
|
+
},
|
|
45
|
+
// 是否隐藏(navigationStyle 为 'custom' 时也应隐藏)
|
|
46
|
+
hidden() {
|
|
47
|
+
return mpx.config?.webConfig?.enableTitleBar !== true || this.navigationStyle === 'custom'
|
|
48
|
+
},
|
|
49
|
+
// 是否展示返回按钮:根据浏览器历史判断(不依赖额外 page 配置)
|
|
50
|
+
showBack() {
|
|
51
|
+
console.log('showBack', this.$router.stack.length)
|
|
52
|
+
try {
|
|
53
|
+
return this.$router.stack.length > 1
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
// 安卓安全高度
|
|
59
|
+
safeAreaInsetTop() {
|
|
60
|
+
if (typeof mpx.config.webConfig.safeAreaInsetTop === 'number' && mpx.config.webConfig.safeAreaInsetTop >= 0) {
|
|
61
|
+
return mpx.config.webConfig.safeAreaInsetTop
|
|
62
|
+
}
|
|
63
|
+
return 24
|
|
64
|
+
},
|
|
65
|
+
warpStyle() {
|
|
66
|
+
return {
|
|
67
|
+
'--titlebar-height': innerHeight,
|
|
68
|
+
'--safe-area-inset-top': `${isIOS ? 'env(safe-area-inset-top, constant(safe-area-inset-top), 0px)' : this.safeAreaInsetTop + 'px'}`,
|
|
69
|
+
paddingTop: 'calc(var(--safe-area-inset-top) + var(--titlebar-height))',
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
rootStyle() {
|
|
73
|
+
return {
|
|
74
|
+
background: this.backgroundColor,
|
|
75
|
+
color: this.textColor
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
innerStyle() {
|
|
79
|
+
return {
|
|
80
|
+
height: innerHeight
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
methods: {
|
|
85
|
+
// 左侧点击:派发事件并在可回退时回退
|
|
86
|
+
onLeftClick(e) {
|
|
87
|
+
this.$emit('click-left', e)
|
|
88
|
+
if (this.showBack) {
|
|
89
|
+
try { window.history.back() } catch (err) { }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
render(h) {
|
|
94
|
+
const leftChildren = []
|
|
95
|
+
|
|
96
|
+
// default back button (SVG) — no left slot support
|
|
97
|
+
if (this.showBack) {
|
|
98
|
+
leftChildren.push(
|
|
99
|
+
h('button', {
|
|
100
|
+
class: 'mpx-titlebar__back',
|
|
101
|
+
attrs: { 'aria-label': 'back', type: 'button' }
|
|
102
|
+
}, [
|
|
103
|
+
h('svg', {
|
|
104
|
+
attrs: {
|
|
105
|
+
viewBox: '0 0 24 24',
|
|
106
|
+
width: '20',
|
|
107
|
+
height: '20',
|
|
108
|
+
fill: 'none',
|
|
109
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
110
|
+
focusable: 'false',
|
|
111
|
+
'aria-hidden': 'true'
|
|
112
|
+
}
|
|
113
|
+
}, [
|
|
114
|
+
h('path', {
|
|
115
|
+
attrs: {
|
|
116
|
+
d: 'M15 18l-6-6 6-6',
|
|
117
|
+
stroke: 'currentColor',
|
|
118
|
+
'stroke-width': '2',
|
|
119
|
+
'stroke-linecap': 'round',
|
|
120
|
+
'stroke-linejoin': 'round'
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
])
|
|
124
|
+
])
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// center shows title; only default slot (page content) is supported
|
|
129
|
+
const centerChildren = [
|
|
130
|
+
h('div', { class: 'mpx-titlebar__title', style: { color: this.textColor } }, [this.titleText])
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
// top-level wrapper: contains fixed titlebar and page content wrapper
|
|
134
|
+
return h('div', { class: 'mpx-page-warp', style: this.hidden ? {} : this.warpStyle }, [
|
|
135
|
+
this.hidden ? null : h('div', {
|
|
136
|
+
class: ['mpx-titlebar'],
|
|
137
|
+
style: this.rootStyle
|
|
138
|
+
}, [
|
|
139
|
+
h('div', { class: 'mpx-titlebar__safe', style: safeStyle }, [
|
|
140
|
+
h('div', { class: 'mpx-titlebar__inner', style: this.innerStyle }, [
|
|
141
|
+
h('div', { class: 'mpx-titlebar__left', on: { click: this.onLeftClick } }, leftChildren),
|
|
142
|
+
h('div', { class: 'mpx-titlebar__center' }, centerChildren),
|
|
143
|
+
h('div', { class: 'mpx-titlebar__right' }, [])
|
|
144
|
+
])
|
|
145
|
+
])
|
|
146
|
+
]),
|
|
147
|
+
h('page', { style: { position: 'relative'} }, [this.$slots.default])
|
|
148
|
+
])
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<style scoped>
|
|
154
|
+
.mpx-page-warp {
|
|
155
|
+
width: 100%;
|
|
156
|
+
height: 100%;
|
|
157
|
+
box-sizing: border-box;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
.mpx-titlebar--hidden {
|
|
162
|
+
display: none;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.mpx-titlebar__safe {
|
|
166
|
+
padding-top: var(--safe-area-inset-top);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.mpx-titlebar__inner {
|
|
170
|
+
display: flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: space-between;
|
|
173
|
+
padding: 0 12px;
|
|
174
|
+
box-sizing: border-box;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.mpx-titlebar__left,
|
|
178
|
+
.mpx-titlebar__right {
|
|
179
|
+
flex: 0 0 auto;
|
|
180
|
+
min-width: 44px;
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.mpx-titlebar__center {
|
|
186
|
+
flex: 1 1 auto;
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: center;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
padding: 0 8px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.mpx-titlebar__title {
|
|
195
|
+
font-size: 17px;
|
|
196
|
+
white-space: nowrap;
|
|
197
|
+
text-overflow: ellipsis;
|
|
198
|
+
overflow: hidden;
|
|
199
|
+
font-weight: 500;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.mpx-titlebar__back {
|
|
203
|
+
background: none;
|
|
204
|
+
border: none;
|
|
205
|
+
font-size: 20px;
|
|
206
|
+
color: inherit;
|
|
207
|
+
padding: 6px;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.mpx-titlebar {
|
|
212
|
+
/* flex-shrink: 0; */
|
|
213
|
+
height: calc(var(--safe-area-inset-top) + var(--titlebar-height));
|
|
214
|
+
width: 100%;
|
|
215
|
+
box-sizing: border-box;
|
|
216
|
+
-webkit-font-smoothing: antialiased;
|
|
217
|
+
position: fixed;
|
|
218
|
+
top: 0;
|
|
219
|
+
left: 0;
|
|
220
|
+
right: 0;
|
|
221
|
+
z-index: 99999;
|
|
222
|
+
/* 脱离上下文,单独渲染加速,修复安卓低端机型在页面滚动时titlebar闪烁问题 */
|
|
223
|
+
transform: translateZ(0);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.mpx-titlebar__content {
|
|
227
|
+
flex: 1;
|
|
228
|
+
overflow: hidden;
|
|
229
|
+
transform: translateZ(0);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.mpx-titlebar__scroll {
|
|
233
|
+
width: 100%;
|
|
234
|
+
height: 100%;
|
|
235
|
+
overflow: auto;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* SVG icon sizing and inherit color */
|
|
239
|
+
.mpx-titlebar__back svg {
|
|
240
|
+
display: block;
|
|
241
|
+
width: 20px;
|
|
242
|
+
height: 20px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.mpx-titlebar__back path {
|
|
246
|
+
stroke: currentColor;
|
|
247
|
+
}
|
|
248
|
+
</style>
|
|
@@ -91,7 +91,7 @@ function parse(cssString) {
|
|
|
91
91
|
return ast
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
function evaluateCondition(condition, defs) {
|
|
94
|
+
function evaluateCondition(condition, defs, filePath) {
|
|
95
95
|
try {
|
|
96
96
|
const keys = Object.keys(defs)
|
|
97
97
|
const values = keys.map(key => defs[key])
|
|
@@ -99,12 +99,12 @@ function evaluateCondition(condition, defs) {
|
|
|
99
99
|
const func = new Function(...keys, `return (${condition});`)
|
|
100
100
|
return func(...values)
|
|
101
101
|
} catch (e) {
|
|
102
|
-
console.error(`[Mpx style error]:Error evaluating condition: ${condition}`, e)
|
|
102
|
+
console.error(`[Mpx style error] File: ${filePath}, Error evaluating condition: ${condition}`, e)
|
|
103
103
|
return false
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function traverseAndEvaluate(ast, defs) {
|
|
107
|
+
function traverseAndEvaluate(ast, defs, filePath) {
|
|
108
108
|
let output = ''
|
|
109
109
|
let batchedIf = false
|
|
110
110
|
function traverse(nodes) {
|
|
@@ -114,12 +114,12 @@ function traverseAndEvaluate(ast, defs) {
|
|
|
114
114
|
} else if (node.type === 'If') {
|
|
115
115
|
// 直接判断 If 节点
|
|
116
116
|
batchedIf = false
|
|
117
|
-
if (evaluateCondition(node.condition, defs)) {
|
|
117
|
+
if (evaluateCondition(node.condition, defs, filePath)) {
|
|
118
118
|
traverse(node.children)
|
|
119
119
|
batchedIf = true
|
|
120
120
|
}
|
|
121
121
|
} else if (node.type === 'ElseIf' && !batchedIf) {
|
|
122
|
-
if (evaluateCondition(node.condition, defs)) {
|
|
122
|
+
if (evaluateCondition(node.condition, defs, filePath)) {
|
|
123
123
|
traverse(node.children)
|
|
124
124
|
batchedIf = true
|
|
125
125
|
}
|
|
@@ -136,11 +136,12 @@ function traverseAndEvaluate(ast, defs) {
|
|
|
136
136
|
*
|
|
137
137
|
* @param {string} content
|
|
138
138
|
* @param {Record<string, any>} defs
|
|
139
|
+
* @param {string} [filePath]
|
|
139
140
|
* @returns
|
|
140
141
|
*/
|
|
141
|
-
function stripCondition(content, defs) {
|
|
142
|
+
function stripCondition(content, defs, filePath) {
|
|
142
143
|
const ast = parse(content)
|
|
143
|
-
return traverseAndEvaluate(ast, defs)
|
|
144
|
+
return traverseAndEvaluate(ast, defs, filePath || 'unknown')
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
let proxyReadFileSync
|
|
@@ -185,10 +186,10 @@ function startFSStripForCss(defs) {
|
|
|
185
186
|
if (shouldStrip(path)) {
|
|
186
187
|
try {
|
|
187
188
|
if (typeof content === 'string') {
|
|
188
|
-
return stripCondition(content, defs)
|
|
189
|
+
return stripCondition(content, defs, path)
|
|
189
190
|
} else if (Buffer.isBuffer(content)) {
|
|
190
191
|
const str = content.toString('utf-8')
|
|
191
|
-
const result = stripCondition(str, defs)
|
|
192
|
+
const result = stripCondition(str, defs, path)
|
|
192
193
|
if (result !== str) {
|
|
193
194
|
return Buffer.from(result, 'utf-8')
|
|
194
195
|
}
|
|
@@ -215,11 +216,11 @@ function startFSStripForCss(defs) {
|
|
|
215
216
|
if (shouldStrip(path)) {
|
|
216
217
|
try {
|
|
217
218
|
if (typeof data === 'string') {
|
|
218
|
-
const result = stripCondition(data, defs)
|
|
219
|
+
const result = stripCondition(data, defs, path)
|
|
219
220
|
return callback(null, result)
|
|
220
221
|
} else if (Buffer.isBuffer(data)) {
|
|
221
222
|
const content = data.toString('utf-8')
|
|
222
|
-
const result = stripCondition(content, defs)
|
|
223
|
+
const result = stripCondition(content, defs, path)
|
|
223
224
|
if (result !== content) {
|
|
224
225
|
return callback(null, Buffer.from(result, 'utf-8'))
|
|
225
226
|
}
|
|
@@ -2652,7 +2652,14 @@ function getVirtualHostRoot (options, meta) {
|
|
|
2652
2652
|
}
|
|
2653
2653
|
}
|
|
2654
2654
|
if (isWeb(mode) && ctorType === 'page') {
|
|
2655
|
-
|
|
2655
|
+
const rootView = createASTElement('mpx-titlebar', [
|
|
2656
|
+
{
|
|
2657
|
+
name: 'pageConfig',
|
|
2658
|
+
value: '{{ this.$options.__mpxPageConfig }}'
|
|
2659
|
+
}
|
|
2660
|
+
])
|
|
2661
|
+
processElement(rootView, rootView, options, meta)
|
|
2662
|
+
return rootView
|
|
2656
2663
|
}
|
|
2657
2664
|
if (isReact(mode) && ctorType === 'page') {
|
|
2658
2665
|
const rootView = createASTElement('view', [
|
|
@@ -60,7 +60,9 @@ Vue.use(VueRouter)\n`
|
|
|
60
60
|
globalTabBar
|
|
61
61
|
})
|
|
62
62
|
|
|
63
|
-
output += `var App = require(${stringifyRequest(loaderContext, addQuery(loaderContext.resource, { isApp: true }))}).default
|
|
63
|
+
output += `var App = require(${stringifyRequest(loaderContext, addQuery(loaderContext.resource, { isApp: true }))}).default;\n`
|
|
64
|
+
|
|
65
|
+
output += `Vue.component('mpx-titlebar', require(${stringifyRequest(loaderContext, normalize.lib('runtime/components/web/mpx-titlebar.vue'))}).default);\n`
|
|
64
66
|
|
|
65
67
|
output += `
|
|
66
68
|
export default processAppOption({
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|