@rdlabo/ionic-theme-ios27 1.2.0-1 → 1.2.0-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/css/components/ion-tabs.css +1 -1
- package/dist/css/ionic-theme-ios27.css +1 -1
- package/dist/css/vertical-bars.css +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -12
- package/dist/native/components/index.d.ts.map +1 -1
- package/dist/native/components/index.js +4 -1
- package/dist/native/components/ion-button.d.ts.map +1 -1
- package/dist/native/components/ion-button.js +3 -3
- package/dist/native/components/ion-buttons.d.ts.map +1 -1
- package/dist/native/components/ion-buttons.js +3 -2
- package/dist/native/components/searchable-tabs.d.ts.map +1 -1
- package/dist/native/components/searchable-tabs.js +6 -7
- package/dist/native/definitions.d.ts +8 -15
- package/dist/native/definitions.d.ts.map +1 -1
- package/dist/native/definitions.js +1 -6
- package/dist/native/index.d.ts +1 -2
- package/dist/native/index.d.ts.map +1 -1
- package/dist/native/index.js +64 -28
- package/dist/native/prehide.d.ts.map +1 -1
- package/dist/native/prehide.js +6 -4
- package/dist/native/runtime.d.ts.map +1 -1
- package/dist/native/runtime.js +40 -19
- package/dist/native/shared/candidate.d.ts.map +1 -1
- package/dist/native/shared/candidate.js +8 -0
- package/dist/native/shared/dom.d.ts.map +1 -1
- package/dist/native/shared/dom.js +11 -10
- package/dist/native/shared/modal.d.ts +8 -0
- package/dist/native/shared/modal.d.ts.map +1 -0
- package/dist/native/shared/modal.js +143 -0
- package/dist/native/vertical-bars-web.d.ts.map +1 -1
- package/dist/native/vertical-bars-web.js +11 -3
- package/dist/sheets-of-glass/animations.d.ts +13 -4
- package/dist/sheets-of-glass/animations.d.ts.map +1 -1
- package/dist/sheets-of-glass/animations.js +56 -40
- package/dist/sheets-of-glass/index.d.ts +1 -1
- package/dist/sheets-of-glass/index.d.ts.map +1 -1
- package/dist/sheets-of-glass/index.js +64 -20
- package/dist/tab-bar-searchable/index.d.ts.map +1 -1
- package/dist/tab-bar-searchable/index.js +39 -11
- package/dist/vertical-bars.d.ts +1 -2
- package/dist/vertical-bars.d.ts.map +1 -1
- package/dist/vertical-bars.js +0 -1
- package/docs/iphone-duo-with-original-theme.md +37 -15
- package/docs/iphone-duo.md +90 -47
- package/docs/migration.md +3 -3
- package/docs/native-ui-shell.md +12 -8
- package/docs/special-markup.md +0 -13
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +1 -1
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +50 -6
- package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalSearch.swift +118 -0
- package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +32 -179
- package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +5 -0
- package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +67 -0
- package/package.json +1 -1
- package/src/index.ts +40 -9
- package/src/native/components/index.ts +6 -1
- package/src/native/components/ion-button.ts +4 -3
- package/src/native/components/ion-buttons.ts +4 -2
- package/src/native/components/searchable-tabs.ts +8 -11
- package/src/native/definitions.ts +11 -19
- package/src/native/index.ts +67 -30
- package/src/native/prehide.ts +7 -4
- package/src/native/runtime.ts +40 -18
- package/src/native/shared/candidate.ts +10 -0
- package/src/native/shared/dom.ts +11 -10
- package/src/native/shared/modal.ts +157 -0
- package/src/native/vertical-bars-web.ts +12 -3
- package/src/sheets-of-glass/animations.ts +66 -41
- package/src/sheets-of-glass/index.ts +82 -16
- package/src/styles/components/ion-tabs.scss +7 -4
- package/src/styles/vertical-bars.scss +110 -35
- package/src/tab-bar-searchable/index.ts +40 -9
- package/src/vertical-bars.ts +1 -2
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { createAnimation } from '@ionic/core';
|
|
2
2
|
import { getStep } from '../utils';
|
|
3
|
+
export const animationRect = (element, vertical = false) => {
|
|
4
|
+
const rect = element.getBoundingClientRect();
|
|
5
|
+
return vertical
|
|
6
|
+
? { left: rect.top, top: rect.left, right: rect.bottom, width: rect.height, height: rect.width, x: rect.y, y: rect.x }
|
|
7
|
+
: rect;
|
|
8
|
+
};
|
|
9
|
+
const translate = (x, y, vertical) => `translate3d(${vertical ? y : x}px, ${vertical ? x : y}px, 0)`;
|
|
10
|
+
const stretchTransform = (x, y, vertical) => `scale(${vertical ? y : x}, ${vertical ? x : y})`;
|
|
11
|
+
const mainSize = (element, vertical) => (vertical ? element.offsetHeight : element.offsetWidth);
|
|
12
|
+
const axisScale = (transform, vertical) => {
|
|
13
|
+
const matrix = new DOMMatrixReadOnly(transform);
|
|
14
|
+
return vertical ? { a: matrix.d, d: matrix.a } : matrix;
|
|
15
|
+
};
|
|
3
16
|
export const getScaleAnimation = (effectElement) => {
|
|
4
17
|
return createAnimation().addElement(effectElement.shadowRoot.querySelector('[part="native"]')).easing('ease-out');
|
|
5
18
|
};
|
|
@@ -37,66 +50,67 @@ const tabBarTapFrames = [
|
|
|
37
50
|
[0.7, 1.003, -0.74, 0.59],
|
|
38
51
|
[0.8, 1, 0, 0],
|
|
39
52
|
];
|
|
40
|
-
export const createTabBarPressAnimation = (effect, from, to, bar) => {
|
|
41
|
-
const box = bar
|
|
53
|
+
export const createTabBarPressAnimation = (effect, from, to, bar, vertical = false) => {
|
|
54
|
+
const box = animationRect(bar, vertical);
|
|
42
55
|
const center = box.left + box.width / 2;
|
|
43
|
-
const left = center - bar
|
|
44
|
-
const fromBox = from
|
|
45
|
-
const toBox = to
|
|
46
|
-
const parentScale = box.width / bar
|
|
47
|
-
const buttonScale =
|
|
56
|
+
const left = center - mainSize(bar, vertical) / 2;
|
|
57
|
+
const fromBox = animationRect(from, vertical);
|
|
58
|
+
const toBox = animationRect(to, vertical);
|
|
59
|
+
const parentScale = box.width / mainSize(bar, vertical);
|
|
60
|
+
const buttonScale = axisScale(getComputedStyle(from).transform, vertical);
|
|
48
61
|
const width = fromBox.width / parentScale / buttonScale.a;
|
|
49
62
|
const height = fromBox.height / parentScale / buttonScale.d;
|
|
50
63
|
const start = (fromBox.left + fromBox.width / 2 - box.left) / parentScale;
|
|
51
64
|
const end = (toBox.left + toBox.width / 2 - box.left) / parentScale;
|
|
52
65
|
const selected = from === to;
|
|
66
|
+
const growth = vertical ? 20 / 16 : 1;
|
|
53
67
|
const frames = tabBarPressFrames.map(([time, position, extraWidth, extraHeight, expansion]) => {
|
|
54
|
-
const scale = 1 + (14.14 / bar
|
|
68
|
+
const scale = vertical ? 1 : 1 + (14.14 / mainSize(bar, vertical)) * expansion;
|
|
55
69
|
const press = 1 - Math.exp(-18 * time) * (1 + 18 * time);
|
|
56
70
|
return {
|
|
57
71
|
offset: time / 0.9,
|
|
58
72
|
x: center + (left + start + (end - start) * position - center) * scale - width / 2,
|
|
59
|
-
sx: ((width + (selected ? 16 * press : extraWidth)) * scale) / width,
|
|
60
|
-
sy: ((height + (selected ? 16 * press : extraHeight)) * scale) / height,
|
|
73
|
+
sx: ((width + (selected ? 16 * press : extraWidth) * growth) * scale) / width,
|
|
74
|
+
sy: ((height + (selected ? 16 * press : extraHeight) * growth) * scale) / height,
|
|
61
75
|
};
|
|
62
76
|
});
|
|
63
77
|
const move = createAnimation()
|
|
64
78
|
.addElement(effect)
|
|
65
79
|
.beforeStyles({
|
|
66
|
-
width: `${width}px`,
|
|
67
|
-
height: `${height}px`,
|
|
80
|
+
width: `${vertical ? height : width}px`,
|
|
81
|
+
height: `${vertical ? width : height}px`,
|
|
68
82
|
display: 'block',
|
|
69
83
|
opacity: '1',
|
|
70
84
|
})
|
|
71
|
-
.keyframes(frames.map(({ offset, x }) => ({ offset, transform:
|
|
85
|
+
.keyframes(frames.map(({ offset, x }) => ({ offset, transform: translate(x, box.top + box.height / 2 - height / 2, vertical) })));
|
|
72
86
|
const stretch = getScaleAnimation(effect)
|
|
73
87
|
.easing('linear')
|
|
74
|
-
.keyframes(frames.map(({ offset, sx, sy }) => ({ offset, transform:
|
|
88
|
+
.keyframes(frames.map(({ offset, sx, sy }) => ({ offset, transform: stretchTransform(sx, sy, vertical) })));
|
|
75
89
|
return createAnimation().duration(900).easing('linear').addAnimation([move, stretch]);
|
|
76
90
|
};
|
|
77
|
-
export const createTabBarDragAnimation = (effect, bar, velocity) => {
|
|
91
|
+
export const createTabBarDragAnimation = (effect, bar, velocity, vertical = false) => {
|
|
78
92
|
const native = effect.shadowRoot.querySelector('[part="native"]');
|
|
79
|
-
const current =
|
|
80
|
-
const { width, height } = effect
|
|
81
|
-
const scale = 1 + 14.14 / bar
|
|
82
|
-
const restX = ((width + 16) * scale) / width;
|
|
83
|
-
const restY = ((height + 16) * scale) / height;
|
|
93
|
+
const current = axisScale(native.style.transform || getComputedStyle(native).transform, vertical);
|
|
94
|
+
const { width, height } = animationRect(effect, vertical);
|
|
95
|
+
const scale = vertical ? 1 : 1 + 14.14 / mainSize(bar, vertical);
|
|
96
|
+
const restX = ((width + (vertical ? 20 : 16)) * scale) / width;
|
|
97
|
+
const restY = ((height + (vertical ? 20 : 16)) * scale) / height;
|
|
84
98
|
const stretch = Math.min(16, 32 * velocity * velocity) * scale;
|
|
85
99
|
const rebound = Math.max(stretch, (current.a - restX) * width) * 0.8;
|
|
86
100
|
return getScaleAnimation(effect)
|
|
87
101
|
.duration(500)
|
|
88
102
|
.easing('linear')
|
|
89
103
|
.keyframes([
|
|
90
|
-
{ offset: 0, transform:
|
|
91
|
-
{ offset: 0.2, transform:
|
|
92
|
-
{ offset: 0.44, transform:
|
|
93
|
-
{ offset: 1, transform:
|
|
104
|
+
{ offset: 0, transform: stretchTransform(current.a, current.d, vertical) },
|
|
105
|
+
{ offset: 0.2, transform: stretchTransform(restX + stretch / width, restY - stretch / height, vertical) },
|
|
106
|
+
{ offset: 0.44, transform: stretchTransform(restX - rebound / width, restY + rebound / height, vertical) },
|
|
107
|
+
{ offset: 1, transform: stretchTransform(restX, restY, vertical) },
|
|
94
108
|
]);
|
|
95
109
|
};
|
|
96
|
-
export const createTabBarReleaseAnimation = (effectElement, target, tap) => {
|
|
110
|
+
export const createTabBarReleaseAnimation = (effectElement, target, tap, vertical = false) => {
|
|
97
111
|
const tapElapsed = tap?.elapsed;
|
|
98
112
|
const native = effectElement.shadowRoot.querySelector('[part="native"]');
|
|
99
|
-
const scale =
|
|
113
|
+
const scale = axisScale(native.style.transform || getComputedStyle(native).transform, vertical);
|
|
100
114
|
const samples = [
|
|
101
115
|
[0, 1],
|
|
102
116
|
[0.033, 0.85],
|
|
@@ -110,22 +124,24 @@ export const createTabBarReleaseAnimation = (effectElement, target, tap) => {
|
|
|
110
124
|
[0.45, 0],
|
|
111
125
|
];
|
|
112
126
|
const bar = target.parentElement;
|
|
113
|
-
const box = bar
|
|
114
|
-
const current = effectElement
|
|
115
|
-
const targetBox = target
|
|
127
|
+
const box = animationRect(bar, vertical);
|
|
128
|
+
const current = animationRect(effectElement, vertical);
|
|
129
|
+
const targetBox = animationRect(target, vertical);
|
|
116
130
|
const center = box.left + box.width / 2;
|
|
117
|
-
const x = center + (targetBox.left + targetBox.width / 2 - center) / (box.width / bar
|
|
131
|
+
const x = center + (targetBox.left + targetBox.width / 2 - center) / (box.width / mainSize(bar, vertical)) - current.width / 2;
|
|
118
132
|
const y = box.top + box.height / 2 - current.height / 2;
|
|
133
|
+
const endScaleX = vertical ? target.offsetHeight / current.width : 1;
|
|
134
|
+
const endScaleY = vertical ? target.offsetWidth / current.height : 1;
|
|
119
135
|
const elapsed = (tapElapsed ?? 0) / 1000;
|
|
120
136
|
const duration = tapElapsed === undefined ? 450 : 800 - tapElapsed;
|
|
121
137
|
let frames = samples.map(([time, remaining]) => ({
|
|
122
138
|
offset: time / 0.45,
|
|
123
139
|
x: x + (current.x - x) * remaining,
|
|
124
140
|
y: y + (current.y - y) * remaining,
|
|
125
|
-
sx:
|
|
126
|
-
sy:
|
|
141
|
+
sx: endScaleX + (scale.a - endScaleX) * remaining,
|
|
142
|
+
sy: endScaleY + (scale.d - endScaleY) * remaining,
|
|
127
143
|
}));
|
|
128
|
-
if (tapElapsed !== undefined) {
|
|
144
|
+
if (tapElapsed !== undefined && !vertical) {
|
|
129
145
|
const nextIndex = tabBarTapFrames.findIndex(([time]) => time > elapsed);
|
|
130
146
|
const previous = tabBarTapFrames[Math.max(0, nextIndex - 1)];
|
|
131
147
|
const next = tabBarTapFrames[nextIndex];
|
|
@@ -136,7 +152,7 @@ export const createTabBarReleaseAnimation = (effectElement, target, tap) => {
|
|
|
136
152
|
...tabBarTapFrames.slice(nextIndex).map(([time, position, width, height]) => ({
|
|
137
153
|
offset: (time - elapsed) / (0.8 - elapsed),
|
|
138
154
|
x: x + ((current.x - x) * (1 - position)) / Math.max(0.1, 1 - progress),
|
|
139
|
-
y,
|
|
155
|
+
y: y + ((current.y - y) * (1 - position)) / Math.max(0.1, 1 - progress),
|
|
140
156
|
sx: 1 + (width * (time >= 0.367 ? 1 + 0.75 * extraDistance : 1)) / current.width,
|
|
141
157
|
sy: 1 + (height * (time >= 0.367 ? 1 + 0.5 * extraDistance : 1)) / current.height,
|
|
142
158
|
})),
|
|
@@ -146,14 +162,14 @@ export const createTabBarReleaseAnimation = (effectElement, target, tap) => {
|
|
|
146
162
|
.addElement(native)
|
|
147
163
|
.duration(duration)
|
|
148
164
|
.easing('linear')
|
|
149
|
-
.keyframes(frames.map(({ offset, sx, sy }) => ({ offset, transform:
|
|
165
|
+
.keyframes(frames.map(({ offset, sx, sy }) => ({ offset, transform: stretchTransform(sx, sy, vertical) })));
|
|
150
166
|
const move = createAnimation()
|
|
151
167
|
.addElement(effectElement)
|
|
152
168
|
.duration(duration)
|
|
153
169
|
.easing('linear')
|
|
154
170
|
.keyframes(frames.map(({ offset, x, y }) => ({
|
|
155
171
|
offset,
|
|
156
|
-
transform:
|
|
172
|
+
transform: translate(x, y, vertical),
|
|
157
173
|
})));
|
|
158
174
|
const offset = 1 - 200 / duration;
|
|
159
175
|
const selectedBackground = 'var(--ios27-tab-selected-background, rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb, var(--ios27-button-color-selected-default-rgb))), 0.095))';
|
|
@@ -174,8 +190,8 @@ export const createTabBarReleaseAnimation = (effectElement, target, tap) => {
|
|
|
174
190
|
{ offset: 1, backgroundColor: selectedBackground },
|
|
175
191
|
]);
|
|
176
192
|
const release = createAnimation().duration(duration).addAnimation([stretch, move, glass, selected]);
|
|
177
|
-
if (tapElapsed !== undefined) {
|
|
178
|
-
const width = bar
|
|
193
|
+
if (tapElapsed !== undefined && !vertical) {
|
|
194
|
+
const width = mainSize(bar, vertical);
|
|
179
195
|
const expansion = box.width - width;
|
|
180
196
|
const peak = Math.max(8.65, expansion);
|
|
181
197
|
const platter = createAnimation()
|
|
@@ -236,7 +252,7 @@ export const createPreMoveAnimation = (effectElement, tabSelectedElement, curren
|
|
|
236
252
|
},
|
|
237
253
|
]);
|
|
238
254
|
};
|
|
239
|
-
export const createMoveAnimation = (effectElement, detail, tabSelectedElement, animationPosition) => {
|
|
255
|
+
export const createMoveAnimation = (effectElement, detail, tabSelectedElement, animationPosition, vertical = false) => {
|
|
240
256
|
return createAnimation()
|
|
241
257
|
.duration(500)
|
|
242
258
|
.addElement(effectElement)
|
|
@@ -247,7 +263,7 @@ export const createMoveAnimation = (effectElement, detail, tabSelectedElement, a
|
|
|
247
263
|
opacity: '1',
|
|
248
264
|
transform: 'none',
|
|
249
265
|
})
|
|
250
|
-
.fromTo('transform',
|
|
266
|
+
.fromTo('transform', translate(animationPosition.minPositionX, animationPosition.positionY, vertical), translate(animationPosition.maxPositionX, animationPosition.positionY, vertical))
|
|
251
267
|
.progressStep(getStep(detail.currentX, animationPosition));
|
|
252
268
|
};
|
|
253
269
|
export const getMoveAnimationKeyframe = (type, scales) => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { EffectScales, registeredEffect } from './interfaces';
|
|
2
|
-
export declare const registerEffect: (targetElement: HTMLElement, effectTagName: string, selectedClassName: string, scales: EffectScales) => registeredEffect | undefined;
|
|
2
|
+
export declare const registerEffect: (targetElement: HTMLElement, effectTagName: string, selectedClassName: string, scales: EffectScales, vertical?: boolean) => registeredEffect | undefined;
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sheets-of-glass/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sheets-of-glass/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAmBjF,eAAO,MAAM,cAAc,GACzB,eAAe,WAAW,EAC1B,eAAe,MAAM,EACrB,mBAAmB,MAAM,EACzB,QAAQ,YAAY,EACpB,kBAAgB,KACf,gBAAgB,GAAG,SAqZrB,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createGesture } from '@ionic/core';
|
|
2
2
|
import { changeSelectedElement, cloneElement, getStep } from '../utils';
|
|
3
3
|
import { isNativeUIShell } from '../native-integration';
|
|
4
|
-
import { createMoveAnimation, createPreMoveAnimation, createTabBarDragAnimation, createTabBarPressAnimation, createTabBarReleaseAnimation, getMoveAnimationKeyframe, getScaleAnimation, } from './animations';
|
|
4
|
+
import { animationRect, createMoveAnimation, createPreMoveAnimation, createTabBarDragAnimation, createTabBarPressAnimation, createTabBarReleaseAnimation, getMoveAnimationKeyframe, getScaleAnimation, } from './animations';
|
|
5
5
|
const GESTURE_NAME = 'ios27-enable-gesture';
|
|
6
6
|
const ANIMATED_NAME = 'ios27-animated';
|
|
7
|
-
export const registerEffect = (targetElement, effectTagName, selectedClassName, scales) => {
|
|
7
|
+
export const registerEffect = (targetElement, effectTagName, selectedClassName, scales, vertical = false) => {
|
|
8
8
|
if (!targetElement.classList.contains('ios')) {
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
@@ -24,6 +24,8 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
24
24
|
let destroyed = false;
|
|
25
25
|
const originalTabBarScale = targetElement.style.getPropertyValue('--ios27-tab-bar-scale');
|
|
26
26
|
const effectElement = cloneElement(effectTagName, false);
|
|
27
|
+
if (vertical)
|
|
28
|
+
effectElement.classList.add('ios27-vertical-tab-effect');
|
|
27
29
|
const stopTabPress = () => {
|
|
28
30
|
if (!tabPressAnimation)
|
|
29
31
|
return;
|
|
@@ -98,17 +100,36 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
98
100
|
document.addEventListener('pointercancel', onPointerCancel);
|
|
99
101
|
};
|
|
100
102
|
targetElement.addEventListener('pointerdown', onPointerDown);
|
|
103
|
+
const onClick = (event) => {
|
|
104
|
+
if (vertical && tabDragging && event.isTrusted && event.detail > 0) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.stopImmediatePropagation();
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
targetElement.addEventListener('click', onClick, true);
|
|
110
|
+
const axisDetail = (detail) => vertical
|
|
111
|
+
? {
|
|
112
|
+
...detail,
|
|
113
|
+
currentX: detail.currentY,
|
|
114
|
+
currentY: detail.currentX,
|
|
115
|
+
startX: detail.startY,
|
|
116
|
+
startY: detail.startX,
|
|
117
|
+
velocityX: detail.velocityY,
|
|
118
|
+
velocityY: detail.velocityX,
|
|
119
|
+
}
|
|
120
|
+
: detail;
|
|
101
121
|
const createAnimationGesture = () => {
|
|
102
122
|
targetElement.classList.add(GESTURE_NAME);
|
|
103
123
|
gesture = createGesture({
|
|
104
124
|
el: targetElement,
|
|
105
125
|
threshold: 0,
|
|
126
|
+
direction: vertical ? 'y' : 'x',
|
|
106
127
|
gestureName: `${GESTURE_NAME}_${effectTagName}_${crypto.randomUUID()}`,
|
|
107
|
-
onStart: (event) => onStartGesture(event),
|
|
108
|
-
onMove: (event) => onMoveGesture(event),
|
|
128
|
+
onStart: (event) => onStartGesture(axisDetail(event)),
|
|
129
|
+
onMove: (event) => onMoveGesture(axisDetail(event)),
|
|
109
130
|
onEnd: (detail) => {
|
|
110
131
|
if (effectTagName === 'ion-tab-button' && tabDragging)
|
|
111
|
-
onMoveGesture(detail);
|
|
132
|
+
onMoveGesture(axisDetail(detail));
|
|
112
133
|
onEndGesture().then();
|
|
113
134
|
},
|
|
114
135
|
});
|
|
@@ -135,25 +156,31 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
135
156
|
return false;
|
|
136
157
|
currentTouchedElement = detail.event.target.closest(effectTagName) || undefined;
|
|
137
158
|
const tabSelectedElement = targetElement.querySelector(`${effectTagName}.${selectedClassName}`);
|
|
138
|
-
if (currentTouchedElement === undefined ||
|
|
159
|
+
if (currentTouchedElement === undefined ||
|
|
160
|
+
currentTouchedElement.parentElement !== targetElement ||
|
|
161
|
+
currentTouchedElement.disabled ||
|
|
162
|
+
tabSelectedElement === null) {
|
|
163
|
+
currentTouchedElement = undefined;
|
|
139
164
|
return false;
|
|
140
165
|
}
|
|
141
166
|
selectedElementBeforeGesture = tabSelectedElement;
|
|
142
167
|
if (effectTagName === 'ion-tab-button') {
|
|
143
|
-
targetElement.style.setProperty('--ios27-tab-bar-scale', String(1 + 14.14 / targetElement.offsetWidth));
|
|
168
|
+
targetElement.style.setProperty('--ios27-tab-bar-scale', String(1 + 14.14 / (vertical ? targetElement.offsetHeight : targetElement.offsetWidth)));
|
|
144
169
|
}
|
|
170
|
+
if (vertical)
|
|
171
|
+
targetElement.classList.add(ANIMATED_NAME);
|
|
145
172
|
animationPosition = {
|
|
146
|
-
minPositionX: targetElement
|
|
147
|
-
maxPositionX: targetElement
|
|
148
|
-
width: tabSelectedElement.clientWidth,
|
|
149
|
-
positionY: tabSelectedElement
|
|
173
|
+
minPositionX: animationRect(targetElement, vertical).left,
|
|
174
|
+
maxPositionX: animationRect(targetElement, vertical).right - (vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth),
|
|
175
|
+
width: vertical ? tabSelectedElement.clientHeight : tabSelectedElement.clientWidth,
|
|
176
|
+
positionY: animationRect(tabSelectedElement, vertical).top,
|
|
150
177
|
};
|
|
151
178
|
targetElement.classList.add(ANIMATED_NAME);
|
|
152
179
|
changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
|
|
153
180
|
if (effectTagName === 'ion-tab-button') {
|
|
154
|
-
moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition);
|
|
155
|
-
moveAnimation.progressStart(true, getStep(tabSelectedElement
|
|
156
|
-
tabPressAnimation = createTabBarPressAnimation(effectElement, tabSelectedElement, currentTouchedElement, targetElement);
|
|
181
|
+
moveAnimation = createMoveAnimation(effectElement, detail, tabSelectedElement, animationPosition, vertical);
|
|
182
|
+
moveAnimation.progressStart(true, getStep(animationRect(tabSelectedElement, vertical).left + animationRect(tabSelectedElement, vertical).width / 2, animationPosition));
|
|
183
|
+
tabPressAnimation = createTabBarPressAnimation(effectElement, tabSelectedElement, currentTouchedElement, targetElement, vertical);
|
|
157
184
|
tabPressStartedAt = performance.now();
|
|
158
185
|
void tabPressAnimation.play();
|
|
159
186
|
return true;
|
|
@@ -187,7 +214,7 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
187
214
|
return;
|
|
188
215
|
tabDragging = true;
|
|
189
216
|
stopTabPress();
|
|
190
|
-
tabPressAnimation = createTabBarDragAnimation(effectElement, targetElement, detail.velocityX);
|
|
217
|
+
tabPressAnimation = createTabBarDragAnimation(effectElement, targetElement, detail.velocityX, vertical);
|
|
191
218
|
void tabPressAnimation.play();
|
|
192
219
|
}
|
|
193
220
|
else if (scaleAnimationPromise === undefined) {
|
|
@@ -211,10 +238,22 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
211
238
|
}
|
|
212
239
|
}
|
|
213
240
|
const currentX = detail.currentX;
|
|
214
|
-
const previousY = targetElement
|
|
215
|
-
const nextEl = targetElement.getRootNode().elementFromPoint(currentX, previousY);
|
|
216
|
-
|
|
217
|
-
if (
|
|
241
|
+
const previousY = animationRect(targetElement, vertical).top + animationRect(targetElement, vertical).height / 2;
|
|
242
|
+
const nextEl = targetElement.getRootNode().elementFromPoint(vertical ? previousY : currentX, vertical ? currentX : previousY);
|
|
243
|
+
let latestTouchedElement = nextEl?.closest(effectTagName) || undefined;
|
|
244
|
+
if (vertical && effectTagName === 'ion-tab-button') {
|
|
245
|
+
const bar = targetElement.getBoundingClientRect();
|
|
246
|
+
const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
|
|
247
|
+
const distance = (tab) => Math.abs(currentX - (bar.top + tab.offsetTop + tab.offsetHeight / 2));
|
|
248
|
+
latestTouchedElement =
|
|
249
|
+
currentX >= bar.top && currentX <= bar.bottom
|
|
250
|
+
? tabs.reduce((closest, tab) => (!closest || distance(tab) < distance(closest) ? tab : closest), undefined)
|
|
251
|
+
: undefined;
|
|
252
|
+
}
|
|
253
|
+
if (latestTouchedElement &&
|
|
254
|
+
latestTouchedElement.parentElement === targetElement &&
|
|
255
|
+
!latestTouchedElement.disabled &&
|
|
256
|
+
currentTouchedElement !== latestTouchedElement) {
|
|
218
257
|
currentTouchedElement = latestTouchedElement;
|
|
219
258
|
changeSelectedElement(targetElement, currentTouchedElement, effectTagName, selectedClassName);
|
|
220
259
|
}
|
|
@@ -263,12 +302,16 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
263
302
|
moveAnimation.destroy();
|
|
264
303
|
moveAnimation = undefined;
|
|
265
304
|
const tabs = Array.from(targetElement.querySelectorAll(effectTagName)).filter((tab) => tab.offsetWidth > 0);
|
|
305
|
+
if (vertical) {
|
|
306
|
+
currentTouchedElement.classList.remove('ion-activated');
|
|
307
|
+
targetElement.classList.remove(ANIMATED_NAME);
|
|
308
|
+
}
|
|
266
309
|
const releasing = createTabBarReleaseAnimation(effectElement, currentTouchedElement, tapElapsed !== undefined && tapElapsed < 200
|
|
267
310
|
? {
|
|
268
311
|
elapsed: tapElapsed,
|
|
269
312
|
distance: Math.abs(tabs.indexOf(currentTouchedElement) - tabs.indexOf(selectedElementBeforeGesture)),
|
|
270
313
|
}
|
|
271
|
-
: undefined);
|
|
314
|
+
: undefined, vertical);
|
|
272
315
|
currentTouchedElement.classList.remove('ion-activated');
|
|
273
316
|
releaseAnimation = releasing;
|
|
274
317
|
await releasing.play();
|
|
@@ -297,6 +340,7 @@ export const registerEffect = (targetElement, effectTagName, selectedClassName,
|
|
|
297
340
|
destroyed = true;
|
|
298
341
|
cancelActiveGesture();
|
|
299
342
|
targetElement.removeEventListener('pointerdown', onPointerDown);
|
|
343
|
+
targetElement.removeEventListener('click', onClick, true);
|
|
300
344
|
targetElement.removeEventListener('nativeUIShellChange', nativeChanged);
|
|
301
345
|
if (gesture) {
|
|
302
346
|
gesture.destroy();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tab-bar-searchable/index.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAC;AAiBnC,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAC;AAqBnC,eAAO,MAAM,sBAAsB,GACjC,WAAW,WAAW,EACtB,cAAc,WAAW,EACzB,WAAW,WAAW,KACrB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tab-bar-searchable/index.ts"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,wBAAwB,EAE9B,MAAM,2BAA2B,CAAC;AAiBnC,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,2BAA2B,CAAC;AAqBnC,eAAO,MAAM,sBAAsB,GACjC,WAAW,WAAW,EACtB,cAAc,WAAW,EACzB,WAAW,WAAW,KACrB,wBA+EF,CAAC"}
|
|
@@ -10,11 +10,14 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
10
10
|
}
|
|
11
11
|
ionFooter.style.pointerEvents = 'none';
|
|
12
12
|
ionFooter.style.opacity = '0';
|
|
13
|
+
ionFooter.querySelector('ion-buttons[slot=start]')?.classList.add('ios-theme-horizontal-only');
|
|
13
14
|
const nativeSearch = registerNativeSearch(ionTabBar, ionFabButton, ionFooter);
|
|
14
15
|
let searchableEventCache;
|
|
15
16
|
let resumeNative;
|
|
16
17
|
let nativeEntry = false;
|
|
17
18
|
let nativeRequest = 0;
|
|
19
|
+
let verticalSearch = false;
|
|
20
|
+
let tabVisibilityObserver;
|
|
18
21
|
return async (event, type) => {
|
|
19
22
|
const selector = type === TabBarSearchableType.Enter ? 'ion-fab-button' : 'ion-buttons[slot=start] ion-button';
|
|
20
23
|
if (!event.target?.closest(selector))
|
|
@@ -37,11 +40,28 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
if (type === TabBarSearchableType.Enter) {
|
|
40
|
-
|
|
43
|
+
verticalSearch = !!ionTabBar.closest('ion-app.ios-theme-vertical-bars');
|
|
44
|
+
if (verticalSearch) {
|
|
45
|
+
const keepTabsVisible = () => {
|
|
46
|
+
if (ionTabBar.classList.contains('tab-bar-hidden') && ionTabBar.closest('ion-app.ios-theme-vertical-bars')) {
|
|
47
|
+
ionTabBar.classList.remove('tab-bar-hidden');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
tabVisibilityObserver?.disconnect();
|
|
51
|
+
tabVisibilityObserver = new MutationObserver(keepTabsVisible);
|
|
52
|
+
tabVisibilityObserver.observe(ionTabBar, { attributes: true, attributeFilter: ['class'] });
|
|
53
|
+
keepTabsVisible();
|
|
54
|
+
}
|
|
55
|
+
resumeNative ?? (resumeNative = await suspendNativeUIShell([
|
|
56
|
+
...(verticalSearch ? [] : [ionTabBar]),
|
|
57
|
+
ionFooter,
|
|
58
|
+
ionFabButton.closest('ion-fab') ?? ionFabButton,
|
|
59
|
+
]));
|
|
41
60
|
try {
|
|
42
|
-
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter);
|
|
61
|
+
searchableEventCache = await enterEvent(event, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
43
62
|
}
|
|
44
63
|
catch (error) {
|
|
64
|
+
tabVisibilityObserver?.disconnect();
|
|
45
65
|
resumeNative();
|
|
46
66
|
resumeNative = undefined;
|
|
47
67
|
throw error;
|
|
@@ -49,10 +69,11 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
49
69
|
}
|
|
50
70
|
else if (searchableEventCache !== undefined) {
|
|
51
71
|
try {
|
|
52
|
-
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter);
|
|
72
|
+
await leaveEvent(event, searchableEventCache, ionTabBar, ionFabButton, ionFooter, verticalSearch);
|
|
53
73
|
searchableEventCache = undefined;
|
|
54
74
|
}
|
|
55
75
|
finally {
|
|
76
|
+
tabVisibilityObserver?.disconnect();
|
|
56
77
|
resumeNative?.();
|
|
57
78
|
resumeNative = undefined;
|
|
58
79
|
}
|
|
@@ -62,7 +83,7 @@ export const attachTabBarSearchable = (ionTabBar, ionFabButton, ionFooter) => {
|
|
|
62
83
|
}
|
|
63
84
|
};
|
|
64
85
|
};
|
|
65
|
-
const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
86
|
+
const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter, verticalSearch) => {
|
|
66
87
|
if (!event.target?.closest('ion-fab-button')) {
|
|
67
88
|
throw throwErrorByFailedClickElement('ion-fab-button');
|
|
68
89
|
}
|
|
@@ -71,10 +92,13 @@ const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
|
71
92
|
const colorSelected = references.selectedTabButton
|
|
72
93
|
? getComputedStyle(references.selectedTabButton).getPropertyValue('--color-selected').trim()
|
|
73
94
|
: '';
|
|
74
|
-
|
|
95
|
+
if (verticalSearch)
|
|
96
|
+
references.closeButtonIcon.setAttribute('name', 'close');
|
|
97
|
+
const tabAnimations = verticalSearch
|
|
98
|
+
? []
|
|
99
|
+
: [createTabBarAnimation(ionTabBar, references, sizes), createEffectAnimation(references, sizes)];
|
|
75
100
|
const searchContainerAnimation = createSearchContainerAnimation(references, sizes);
|
|
76
101
|
const closeButtonsAnimation = createCloseButtonsAnimation(references);
|
|
77
|
-
const tabBarAnimation = createTabBarAnimation(ionTabBar, references, sizes);
|
|
78
102
|
const fabButtonAnimation = createFabButtonAnimation(ionFabButton);
|
|
79
103
|
await createAnimation()
|
|
80
104
|
.delay(ANIMATION_DELAY_BASE)
|
|
@@ -83,22 +107,26 @@ const enterEvent = async (event, ionTabBar, ionFabButton, ionFooter) => {
|
|
|
83
107
|
.addElement(ionFooter)
|
|
84
108
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'auto'))
|
|
85
109
|
.fromTo('opacity', '0.8', '1')
|
|
86
|
-
.addAnimation([
|
|
110
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
87
111
|
.play();
|
|
88
112
|
return {
|
|
89
113
|
elementSizes: sizes,
|
|
90
114
|
colorSelected,
|
|
91
115
|
};
|
|
92
116
|
};
|
|
93
|
-
const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton, ionFooter) => {
|
|
117
|
+
const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton, ionFooter, verticalSearch) => {
|
|
94
118
|
if (!event.target?.closest('ion-buttons[slot=start] ion-button')) {
|
|
95
119
|
throw throwErrorByFailedClickElement('ion-buttons[slot=start] ion-button');
|
|
96
120
|
}
|
|
97
121
|
const references = getElementReferences(ionTabBar, ionFooter);
|
|
98
|
-
const
|
|
122
|
+
const tabAnimations = verticalSearch
|
|
123
|
+
? []
|
|
124
|
+
: [
|
|
125
|
+
createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes),
|
|
126
|
+
createReverseEffectAnimation(references, searchableEventCache.elementSizes, searchableEventCache.colorSelected),
|
|
127
|
+
];
|
|
99
128
|
const searchContainerAnimation = createReverseSearchContainerAnimation(references, searchableEventCache.elementSizes);
|
|
100
129
|
const closeButtonsAnimation = createReverseCloseButtonsAnimation(references);
|
|
101
|
-
const tabBarAnimation = createReverseTabBarAnimation(ionTabBar, references, searchableEventCache.elementSizes);
|
|
102
130
|
const fabButtonAnimation = createReverseFabButtonAnimation(ionFabButton, searchableEventCache.elementSizes);
|
|
103
131
|
await createAnimation()
|
|
104
132
|
.delay(ANIMATION_DELAY_BASE)
|
|
@@ -107,6 +135,6 @@ const leaveEvent = async (event, searchableEventCache, ionTabBar, ionFabButton,
|
|
|
107
135
|
.addElement(ionFooter)
|
|
108
136
|
.afterAddWrite(() => (ionFooter.style.pointerEvents = 'none'))
|
|
109
137
|
.fromTo('opacity', '1', '0')
|
|
110
|
-
.addAnimation([
|
|
138
|
+
.addAnimation([...tabAnimations, fabButtonAnimation, searchContainerAnimation, closeButtonsAnimation])
|
|
111
139
|
.play();
|
|
112
140
|
};
|
package/dist/vertical-bars.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { enableVerticalControlArea, setVerticalControlAreaPlacement, IonicNativeUIShell } from './native';
|
|
2
|
-
export { HingeStatus } from './native';
|
|
3
2
|
export { withNativeUIShellTransition } from './native-integration/transition';
|
|
4
|
-
export type {
|
|
3
|
+
export type { NativeUIShellStatus, NativeUIShellSuspension, VerticalBarEdge, VerticalControlAreaHandle } from './native';
|
|
5
4
|
//# sourceMappingURL=vertical-bars.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertical-bars.d.ts","sourceRoot":"","sources":["../src/vertical-bars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"vertical-bars.d.ts","sourceRoot":"","sources":["../src/vertical-bars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/vertical-bars.js
CHANGED