@rdlabo/ionic-theme-ios27 1.0.4 → 1.1.0-beta.pr224.shac63de393e27b

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 (96) hide show
  1. package/dist/css/components/ion-toggle.css +1 -1
  2. package/dist/css/ionic-theme-ios27.css +1 -1
  3. package/dist/native/components/searchable-tabs.d.ts.map +1 -1
  4. package/dist/native/components/searchable-tabs.js +2 -0
  5. package/dist/native/runtime.d.ts.map +1 -1
  6. package/dist/native/runtime.js +73 -7
  7. package/dist/native/shared/crossfade.d.ts +2 -2
  8. package/dist/native/shared/crossfade.d.ts.map +1 -1
  9. package/dist/native/shared/crossfade.js +3 -3
  10. package/dist/segment/index.d.ts.map +1 -1
  11. package/dist/segment/index.js +7 -0
  12. package/docs/animation.md +2 -2
  13. package/docs/native-ui-shell.md +2 -0
  14. package/package.json +2 -5
  15. package/src/native/components/searchable-tabs.ts +3 -0
  16. package/src/native/runtime.ts +81 -6
  17. package/src/native/shared/crossfade.ts +4 -3
  18. package/src/segment/index.ts +11 -0
  19. package/src/styles/components/ion-toggle.scss +8 -2
  20. package/src/tab-bar-searchable/index.ts +1 -1
  21. package/node_modules/@rdlabo/ionic-theme-utils/LICENSE +0 -21
  22. package/node_modules/@rdlabo/ionic-theme-utils/README.md +0 -28
  23. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts +0 -3
  24. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts.map +0 -1
  25. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js +0 -13
  26. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js.map +0 -1
  27. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts +0 -6
  28. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts.map +0 -1
  29. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js +0 -37
  30. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js.map +0 -1
  31. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts +0 -13
  32. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts.map +0 -1
  33. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js +0 -11
  34. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js.map +0 -1
  35. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.d.ts +0 -3
  36. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.d.ts.map +0 -1
  37. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.js +0 -196
  38. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.js.map +0 -1
  39. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.d.ts +0 -3
  40. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.d.ts.map +0 -1
  41. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.js +0 -80
  42. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.js.map +0 -1
  43. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.d.ts +0 -2
  44. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.d.ts.map +0 -1
  45. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.js +0 -45
  46. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.js.map +0 -1
  47. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.d.ts +0 -38
  48. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.d.ts.map +0 -1
  49. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js +0 -2
  50. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js.map +0 -1
  51. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.d.ts +0 -38
  52. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.d.ts.map +0 -1
  53. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.js +0 -378
  54. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.js.map +0 -1
  55. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts +0 -16
  56. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts.map +0 -1
  57. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js +0 -2
  58. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js.map +0 -1
  59. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.d.ts +0 -41
  60. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.d.ts.map +0 -1
  61. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.js +0 -6
  62. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.js.map +0 -1
  63. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.d.ts +0 -12
  64. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.d.ts.map +0 -1
  65. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.js +0 -61
  66. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.js.map +0 -1
  67. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts +0 -15
  68. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts.map +0 -1
  69. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js +0 -49
  70. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js.map +0 -1
  71. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts +0 -18
  72. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts.map +0 -1
  73. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js +0 -412
  74. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js.map +0 -1
  75. package/node_modules/@rdlabo/ionic-theme-utils/package.json +0 -68
  76. package/node_modules/@rdlabo/ionic-theme-utils/scripts/copy-structured-list.mjs +0 -17
  77. package/node_modules/@rdlabo/ionic-theme-utils/src/dom.spec.ts +0 -19
  78. package/node_modules/@rdlabo/ionic-theme-utils/src/dom.ts +0 -16
  79. package/node_modules/@rdlabo/ionic-theme-utils/src/focus-controller.ts +0 -38
  80. package/node_modules/@rdlabo/ionic-theme-utils/src/index.ts +0 -59
  81. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/animations/ios.enter.ts +0 -259
  82. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/animations/ios.leave.ts +0 -99
  83. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/callout-surface.ts +0 -54
  84. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/popover-interface.ts +0 -44
  85. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/utils.spec.ts +0 -58
  86. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/utils.ts +0 -698
  87. package/node_modules/@rdlabo/ionic-theme-utils/src/sheets-of-glass.ts +0 -17
  88. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/index.scss +0 -1
  89. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/md-ion-list-inset.scss +0 -20
  90. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/structured-list.scss +0 -310
  91. package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/interfaces.ts +0 -28
  92. package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/utils.spec.ts +0 -56
  93. package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/utils.ts +0 -75
  94. package/node_modules/@rdlabo/ionic-theme-utils/src/theme.ts +0 -51
  95. package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.spec.ts +0 -52
  96. package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.ts +0 -542
@@ -1,20 +0,0 @@
1
- @use 'structured-list';
2
-
3
- @mixin theme($variant: classic, $disabled-selector: null) {
4
- ion-list.md {
5
- @include structured-list.support-text($disabled-selector: $disabled-selector);
6
-
7
- &.list-inset {
8
- @include structured-list.layout(8px, 16px, $variant: $variant, $disabled-selector: $disabled-selector);
9
- :is(#{structured-list.$groups}) ion-item > ion-input[labelplacement='floating'] {
10
- transition: transform 200ms ease;
11
- &:not(.label-floating) {
12
- transform: translateY(-7px);
13
- }
14
- &.label-floating {
15
- transform: translateY(-4px);
16
- }
17
- }
18
- }
19
- }
20
- }
@@ -1,310 +0,0 @@
1
- $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group;
2
-
3
- @function _disabled-suffix($disabled-selector) {
4
- @if $disabled-selector {
5
- @return ':not(#{$disabled-selector})';
6
- }
7
-
8
- @return '';
9
- }
10
-
11
- // Derive neutral list colors from the application's Ionic medium palette.
12
- @mixin colors($supporting-opacity, $detail-opacity) {
13
- ion-item {
14
- --detail-icon-color: rgb(var(--ion-color-medium-rgb));
15
- --detail-icon-opacity: #{$detail-opacity};
16
- }
17
-
18
- &.list-inset {
19
- ion-list-header ion-label:not(.ion-color) {
20
- color: rgba(var(--ion-color-medium-rgb), $supporting-opacity);
21
- }
22
-
23
- ion-list-header ion-note:not(.ion-color),
24
- > ion-note:not(.ion-color) {
25
- --color: rgba(var(--ion-color-medium-rgb), #{$supporting-opacity});
26
- }
27
- }
28
- }
29
-
30
- @mixin support-text($disabled-selector: null) {
31
- $d: _disabled-suffix($disabled-selector);
32
-
33
- ion-item#{$d} {
34
- &:has(ion-input.input-fill-outline),
35
- &:has(ion-textarea.textarea-fill-outline),
36
- &:has(ion-select:is([helpertext]:not([helpertext='']), [errortext]:not([errortext='']))),
37
- &:has(ion-input .input-bottom > :is(.helper-text:not(:empty), .error-text:not(:empty), .counter:not(:empty))),
38
- &:has(ion-textarea .textarea-bottom > :is(.helper-text:not(:empty), .error-text:not(:empty), .counter:not(:empty))) {
39
- --inner-border-width: 0;
40
- --inner-padding-bottom: 4px;
41
- }
42
-
43
- ion-input#{$d} .input-bottom:not(:has(> :is(.helper-text:not(:empty), .error-text:not(:empty), .counter:not(:empty)))),
44
- ion-textarea#{$d} .textarea-bottom:not(:has(> :is(.helper-text:not(:empty), .error-text:not(:empty), .counter:not(:empty)))) {
45
- display: none;
46
- }
47
- }
48
- }
49
-
50
- @mixin layout($outer-note-inset, $group-border-radius, $variant: classic, $disabled-selector: null) {
51
- $d: _disabled-suffix($disabled-selector);
52
- $is-ios27: $variant == ios27;
53
-
54
- background: transparent;
55
-
56
- ion-list-header#{$d} {
57
- display: flex;
58
- align-items: center;
59
-
60
- ion-label#{$d} {
61
- @if $is-ios27 {
62
- font-size: 1rem;
63
- line-height: 1.29rem;
64
- font-weight: 600;
65
- } @else {
66
- font-size: 0.9rem;
67
- }
68
- margin: 8px 4px 4px;
69
-
70
- &:not(.ion-color) {
71
- color: var(--ion-color-medium-tint);
72
- }
73
- }
74
-
75
- ion-button#{$d} {
76
- margin-top: 8px;
77
- }
78
-
79
- ion-note#{$d} {
80
- @if $is-ios27 {
81
- font-size: 0.76rem;
82
- line-height: 1.06rem;
83
- font-weight: 400;
84
- margin-block: 8px 4px;
85
- margin-inline: 0 16px;
86
-
87
- &:not(.ion-color) {
88
- --color: var(--ion-color-medium-tint);
89
- }
90
- } @else {
91
- font-size: 0.9rem;
92
- margin-block: 8px 4px;
93
- margin-inline: 0 16px;
94
- }
95
- }
96
- }
97
-
98
- @if $is-ios27 {
99
- ion-item-group.item-group-header#{$d} {
100
- border-radius: $group-border-radius;
101
-
102
- > ion-item#{$d} {
103
- --inner-padding-end: 0;
104
- --inner-border-width: 0;
105
-
106
- &::part(native) {
107
- padding-inline: 0;
108
- }
109
- }
110
-
111
- & > ion-item#{$d} > ion-label#{$d} {
112
- width: 100%;
113
- margin: 0;
114
- text-align: start;
115
- white-space: normal;
116
- padding: 16px 20px;
117
-
118
- /**
119
- * ion-header.header-collapse-condenseを使って、スクロールによってHeaderを変化させるトリック
120
- */
121
- ion-header.header-collapse-condense > ion-toolbar {
122
- --min-height: 0;
123
- visibility: hidden;
124
- }
125
-
126
- ion-icon#{$d} {
127
- font-size: 2.82rem;
128
- border-radius: 20%;
129
- padding: 6px;
130
- vertical-align: top;
131
-
132
- &:not(.ion-color) {
133
- color: #ffffff;
134
- }
135
- }
136
-
137
- h2 {
138
- font-size: 1.29rem;
139
- font-weight: 700;
140
- line-height: 1.53rem;
141
- margin: 20px 0 8px;
142
- }
143
-
144
- ion-text#{$d} {
145
- display: block;
146
- font-size: 1rem;
147
- line-height: 1.29rem;
148
-
149
- &:not(.ion-color) {
150
- color: var(--ion-color-step-500, var(--ion-text-color-step-500, #808080));
151
- }
152
- }
153
- }
154
- }
155
- } @else {
156
- ion-item-group.item-group-header#{$d} {
157
- & > ion-item#{$d} > ion-label#{$d} {
158
- width: 100%;
159
- text-align: center;
160
- padding: 32px 16px 16px;
161
- transform: translateX(5px);
162
-
163
- &:dir(rtl) {
164
- transform: translateX(-5px);
165
- }
166
-
167
- /**
168
- * ion-header.header-collapse-condenseを使って、スクロールによってHeaderを変化させるトリック
169
- */
170
- ion-header.header-collapse-condense > ion-toolbar {
171
- --min-height: 0;
172
- visibility: hidden;
173
- }
174
-
175
- ion-icon#{$d} {
176
- font-size: 2.8rem;
177
- border-radius: 20%;
178
- padding: 6px;
179
-
180
- &:not(.ion-color) {
181
- color: #ffffff;
182
- }
183
- }
184
-
185
- h2 {
186
- font-size: 1.2rem;
187
- font-weight: bold;
188
- margin-top: 8px;
189
- }
190
-
191
- ion-text#{$d} {
192
- font-size: 0.9rem;
193
- }
194
- }
195
- }
196
- }
197
-
198
- > :is(#{$groups})#{$d} {
199
- display: block;
200
- border-radius: $group-border-radius;
201
- overflow: hidden;
202
-
203
- &:has(> .radio-group-top) {
204
- display: flex;
205
- flex-direction: column;
206
- border-radius: 0;
207
- overflow: visible;
208
-
209
- > .radio-group-top {
210
- order: 1;
211
- font-size: 0.9rem;
212
- padding-block-start: 8px;
213
- }
214
-
215
- > ion-item#{$d}:first-of-type {
216
- --border-radius: #{$group-border-radius} #{$group-border-radius} 0 0;
217
- }
218
-
219
- > ion-item#{$d}:last-of-type {
220
- --border-radius: 0 0 #{$group-border-radius} #{$group-border-radius};
221
- }
222
- }
223
-
224
- &:not(:first-of-type) {
225
- margin-top: 16px;
226
- }
227
-
228
- ion-item#{$d} {
229
- &:has(> ion-label:not([slot])):has(> ion-note:not([slot])) {
230
- &::part(container) {
231
- flex-direction: column;
232
- justify-content: center;
233
- padding-top: 14px;
234
- padding-bottom: 14px;
235
- min-height: 64px;
236
- }
237
- & > ion-label#{$d},
238
- & > ion-note#{$d} {
239
- width: auto;
240
- align-self: flex-start;
241
- }
242
- & > ion-label#{$d} {
243
- margin: 0;
244
- font-weight: 500;
245
- @if $is-ios27 {
246
- font-size: 1rem;
247
- line-height: 1.29rem;
248
- } @else {
249
- font-size: 1.15rem;
250
- line-height: 0.9rem;
251
- }
252
- }
253
- & > ion-note#{$d} {
254
- @if $is-ios27 {
255
- font-size: 0.88rem;
256
- line-height: 1.18rem;
257
- } @else {
258
- font-size: 0.85rem;
259
- }
260
- }
261
- }
262
-
263
- ion-text[slot='end']#{$d} {
264
- padding-inline-start: 8px;
265
- }
266
-
267
- &.item-disabled {
268
- --detail-icon-opacity: 0.1;
269
- opacity: 1;
270
-
271
- & > * {
272
- opacity: 0.4;
273
- }
274
- }
275
-
276
- ion-button[slot='end']#{$d} {
277
- &.ion-align-self-end {
278
- transform: translateY(-7px);
279
- }
280
- ion-icon[slot='icon-only']#{$d} {
281
- font-size: 1.2rem;
282
- transform: translateY(4px);
283
- }
284
- }
285
- }
286
- }
287
-
288
- & > ion-note#{$d} {
289
- margin-block: 8px;
290
- margin-inline: calc(var(--ion-safe-area-left, 0) + $outer-note-inset) calc(var(--ion-safe-area-right, 0) + $outer-note-inset);
291
-
292
- &:dir(rtl) {
293
- margin-inline: calc(var(--ion-safe-area-right, 0) + $outer-note-inset) calc(var(--ion-safe-area-left, 0) + $outer-note-inset);
294
- }
295
-
296
- @if $is-ios27 {
297
- font-size: 0.76rem;
298
- line-height: 1.06rem;
299
- font-weight: 400;
300
- display: block;
301
- &:not(.ion-color) {
302
- --color: var(--ion-color-medium-tint);
303
- }
304
- } @else {
305
- --color: var(--ion-color-medium-tint);
306
- font-size: 0.9rem;
307
- display: block;
308
- }
309
- }
310
- }
@@ -1,28 +0,0 @@
1
- export enum TabBarSearchableType {
2
- Enter = 'enter',
3
- Leave = 'leave',
4
- }
5
-
6
- export type TabBarSearchableFunction = (event: Event, type: TabBarSearchableType) => Promise<void>;
7
-
8
- export interface SearchableEventCache {
9
- elementSizes: ElementSizes;
10
- colorSelected: string;
11
- }
12
-
13
- // DOM要素とサイズ情報の型定義
14
- export interface ElementSizes {
15
- tabBar: { width: number; height: number };
16
- closeButton: { width: number; height: number };
17
- fabButton: { width: number; height: number };
18
- searchContainer: { width: number; height: number };
19
- selectedTabButtonIcon: { width: number; height: number; top: number; left: number };
20
- }
21
-
22
- export interface ElementReferences {
23
- searchContainer: HTMLElement;
24
- closeButtons: HTMLElement;
25
- selectedTabButton: HTMLElement;
26
- selectedTabButtonIcon: HTMLElement;
27
- closeButtonIcon: HTMLElement;
28
- }
@@ -1,56 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
-
3
- import { getElement, getElementReferences, getElementSizes } from './utils';
4
-
5
- const rect = (width: number, height: number, top = 0, left = 0) => ({ width, height, top, left }) as DOMRect;
6
-
7
- describe('tab bar searchable utilities', () => {
8
- it('reports a missing required element with its selector', () => {
9
- expect(() => getElement(document.createElement('div'), '.missing')).toThrow('Expected click element to be inside `.missing`');
10
- });
11
-
12
- it('collects the elements used by the searchable animation', () => {
13
- const tabBar = document.createElement('ion-tab-bar');
14
- tabBar.innerHTML = '<ion-tab-button class="tab-selected"><ion-icon></ion-icon></ion-tab-button>';
15
- const footer = document.createElement('ion-footer');
16
- footer.innerHTML =
17
- '<ion-searchbar><div class="searchbar-input-container"></div></ion-searchbar><ion-buttons slot="start"><ion-button><ion-icon></ion-icon></ion-button></ion-buttons>';
18
-
19
- const references = getElementReferences(tabBar, footer);
20
-
21
- expect(references.selectedTabButton).toBe(tabBar.querySelector('.tab-selected'));
22
- expect(references.searchContainer).toBe(footer.querySelector('.searchbar-input-container'));
23
- expect(references.closeButtonIcon.tagName).toBe('ION-ICON');
24
- });
25
-
26
- it('normalizes bounding rectangles to the shared size model', () => {
27
- const tabBar = document.createElement('ion-tab-bar');
28
- const fab = document.createElement('ion-fab-button');
29
- const closeButtons = document.createElement('ion-buttons');
30
- const searchContainer = document.createElement('div');
31
- const selectedTabButtonIcon = document.createElement('ion-icon');
32
- const selectedTabButton = document.createElement('ion-tab-button');
33
- const closeButtonIcon = document.createElement('ion-icon');
34
- vi.spyOn(tabBar, 'getBoundingClientRect').mockReturnValue(rect(390, 80));
35
- vi.spyOn(fab, 'getBoundingClientRect').mockReturnValue(rect(56, 56));
36
- vi.spyOn(closeButtons, 'getBoundingClientRect').mockReturnValue(rect(44, 44));
37
- vi.spyOn(searchContainer, 'getBoundingClientRect').mockReturnValue(rect(270, 44));
38
- vi.spyOn(selectedTabButtonIcon, 'getBoundingClientRect').mockReturnValue(rect(24, 24, 12, 18));
39
-
40
- expect(
41
- getElementSizes(tabBar, fab, {
42
- closeButtons,
43
- searchContainer,
44
- selectedTabButton,
45
- selectedTabButtonIcon,
46
- closeButtonIcon,
47
- }),
48
- ).toEqual({
49
- tabBar: { width: 390, height: 80 },
50
- closeButton: { width: 44, height: 44 },
51
- fabButton: { width: 56, height: 56 },
52
- searchContainer: { width: 270, height: 44 },
53
- selectedTabButtonIcon: { width: 24, height: 24, top: 12, left: 18 },
54
- });
55
- });
56
- });
@@ -1,75 +0,0 @@
1
- import { ElementReferences, ElementSizes } from './interfaces';
2
-
3
- export const ANIMATION_DURATION = 400;
4
- export const ANIMATION_DELAY_BASE = 140;
5
- export const ANIMATION_DELAY_CLOSE_BUTTONS = 240;
6
- export const ANIMATION_EASING = 'cubic-bezier(0, 1, 0.22, 1)';
7
- export const OPACITY_TRANSITION = 'opacity 140ms ease';
8
-
9
- export const throwErrorByFailedClickElement = (selector: string): Error => {
10
- return new Error('Expected click element to be inside `' + selector + '`');
11
- };
12
-
13
- export const throwErrorByFailedExistElement = (selector: string): Error => {
14
- return new Error('Expected element `' + selector + '` to exist');
15
- };
16
-
17
- export const getElement = (docs: HTMLElement, selector: string): HTMLElement => {
18
- const el = docs.querySelector<HTMLElement>(selector);
19
- if (!el) {
20
- throw throwErrorByFailedClickElement(selector);
21
- }
22
- return el;
23
- };
24
-
25
- export const getElementReferences = (ionTabBar: HTMLElement, ionFooter: HTMLElement): ElementReferences => {
26
- const searchContainer = getElement(ionFooter, 'ion-searchbar .searchbar-input-container');
27
- const closeButtons = getElement(ionFooter, 'ion-buttons[slot=start]');
28
- const selectedTabButton = ionTabBar.querySelector<HTMLElement>('ion-tab-button.tab-selected');
29
- const selectedTabButtonIcon = selectedTabButton?.querySelector('ion-icon');
30
- const closeButtonIcon = closeButtons.querySelector('ion-icon');
31
-
32
- if (!selectedTabButton) {
33
- throw throwErrorByFailedExistElement('ion-tab-button.tab-selected');
34
- }
35
-
36
- if (!selectedTabButtonIcon) {
37
- throw throwErrorByFailedExistElement('ion-tab-button.tab-selected ion-icon');
38
- }
39
-
40
- if (!closeButtonIcon) {
41
- throw throwErrorByFailedExistElement('ion-buttons[slot=start] ion-button ion-icon');
42
- }
43
-
44
- return {
45
- searchContainer,
46
- closeButtons,
47
- selectedTabButton,
48
- selectedTabButtonIcon,
49
- closeButtonIcon,
50
- };
51
- };
52
-
53
- /**
54
- * 各要素のサイズ情報を取得
55
- */
56
- export const getElementSizes = (ionTabBar: HTMLElement, ionFabButton: HTMLElement, references: ElementReferences): ElementSizes => {
57
- const tabBarRect = ionTabBar.getBoundingClientRect();
58
- const fabButtonRect = ionFabButton.getBoundingClientRect();
59
- const closeButtonRect = references.closeButtons.getBoundingClientRect();
60
- const searchContainerRect = references.searchContainer.getBoundingClientRect();
61
- const selectedTabButtonIconRect = references.selectedTabButtonIcon!.getBoundingClientRect();
62
-
63
- return {
64
- tabBar: { width: tabBarRect.width, height: tabBarRect.height },
65
- closeButton: { width: closeButtonRect.width, height: closeButtonRect.height },
66
- fabButton: { width: fabButtonRect.width, height: fabButtonRect.height },
67
- searchContainer: { width: searchContainerRect.width, height: searchContainerRect.height },
68
- selectedTabButtonIcon: {
69
- width: selectedTabButtonIconRect.width,
70
- height: selectedTabButtonIconRect.height,
71
- top: selectedTabButtonIconRect.top,
72
- left: selectedTabButtonIconRect.left,
73
- },
74
- };
75
- };
@@ -1,51 +0,0 @@
1
- import type { IonicConfig } from '@ionic/core';
2
- import type { AnimationPosition } from './sheets-of-glass';
3
-
4
- export const cloneElement = (tagName: string, useCache: boolean = true): HTMLElement => {
5
- if (useCache) {
6
- const cachedElement = document.querySelector(`${tagName}.ion-cloned-element`);
7
- if (cachedElement !== null) return cachedElement as HTMLElement;
8
- }
9
- const clonedEl = document.createElement(tagName);
10
- clonedEl.classList.add('ion-cloned-element');
11
- clonedEl.style.setProperty('display', 'none');
12
- document.body.appendChild(clonedEl);
13
- return clonedEl;
14
- };
15
-
16
- export const getStep = (targetX: number, position: AnimationPosition) => {
17
- const currentX = targetX - position.width / 2;
18
- return Math.max(0, Math.min(1, (currentX - position.minPositionX) / (position.maxPositionX - position.minPositionX)));
19
- };
20
-
21
- export const changeSelectedElement = (target: HTMLElement, selected: HTMLElement, tagName: string, selectedClass: string): void => {
22
- target.querySelectorAll(tagName).forEach((element) => {
23
- element.classList.remove(selectedClass, 'ion-activated');
24
- });
25
- selected.classList.add('ion-activated');
26
- };
27
-
28
- export class Config {
29
- private values = new Map<keyof IonicConfig, unknown>();
30
- reset(config: IonicConfig) {
31
- this.values = new Map(Object.entries(config) as [keyof IonicConfig, unknown][]);
32
- }
33
- get<T = unknown>(key: keyof IonicConfig, fallback?: T): T {
34
- const value = this.values.get(key);
35
- return (value !== undefined ? value : fallback) as T;
36
- }
37
- getBoolean(key: keyof IonicConfig, fallback = false): boolean {
38
- const value = this.values.get(key);
39
- if (value === undefined) return fallback;
40
- return typeof value === 'string' ? value === 'true' : !!value;
41
- }
42
- getNumber(key: keyof IonicConfig, fallback?: number): number {
43
- const value = Number.parseFloat(String(this.values.get(key)));
44
- return Number.isNaN(value) ? (fallback ?? Number.NaN) : value;
45
- }
46
- set(key: keyof IonicConfig, value: unknown) {
47
- this.values.set(key, value);
48
- }
49
- }
50
-
51
- export const config = /* @__PURE__ */ new Config();
@@ -1,52 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { createIosTransitionAnimation } from './ios.transition';
4
-
5
- const createPage = (translucent = false) => {
6
- const page = document.createElement('div');
7
- const header = document.createElement('ion-header');
8
- if (translucent) {
9
- header.classList.add('header-translucent');
10
- }
11
- page.append(header, document.createElement('ion-content'));
12
- return page;
13
- };
14
-
15
- const createTransition = (radius?: number) => {
16
- const nav = document.createElement('ion-router-outlet');
17
- const enteringEl = createPage(true);
18
- const leavingEl = createPage();
19
- nav.append(leavingEl, enteringEl);
20
- document.body.append(nav);
21
-
22
- const animation = createIosTransitionAnimation({
23
- offLeftPercent: 30,
24
- radius,
25
- getIonPageElement: (element) => element,
26
- })(nav, { enteringEl, leavingEl });
27
-
28
- return { animation, enteringEl, nav };
29
- };
30
-
31
- describe('createIosTransitionAnimation radius', () => {
32
- it('does not apply a page clip when radius is omitted', () => {
33
- const { animation, enteringEl, nav } = createTransition();
34
-
35
- animation.progressStart(true);
36
-
37
- expect(enteringEl.style.clipPath).toBe('');
38
- animation.destroy();
39
- nav.remove();
40
- });
41
-
42
- it('applies the configured radius', () => {
43
- const { animation, enteringEl, nav } = createTransition(48);
44
- enteringEl.style.clipPath = 'inset(1px)';
45
-
46
- animation.progressStart(true);
47
-
48
- expect(enteringEl.style.clipPath).toBe('inset(0 round 48px)');
49
- animation.destroy();
50
- nav.remove();
51
- });
52
- });