@ionic/core 7.3.2-dev.11693402720.1adb3bcf → 7.3.2-dev.11693507138.1f9ed625

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 (122) hide show
  1. package/components/checkbox.js +6 -3
  2. package/components/data.js +31 -7
  3. package/components/ion-datetime-button.js +3 -0
  4. package/components/ion-datetime.js +106 -81
  5. package/components/ion-range.js +2 -2
  6. package/components/ion-router-outlet.js +2 -2
  7. package/components/ion-textarea.js +3 -3
  8. package/components/ion-toggle.js +6 -3
  9. package/components/radio.js +6 -3
  10. package/dist/cjs/{data-967b9c15.js → data-db832785.js} +31 -7
  11. package/dist/cjs/ion-app_8.cjs.entry.js +2 -2
  12. package/dist/cjs/ion-checkbox.cjs.entry.js +5 -3
  13. package/dist/cjs/ion-datetime-button.cjs.entry.js +4 -1
  14. package/dist/cjs/ion-datetime_3.cjs.entry.js +106 -82
  15. package/dist/cjs/ion-radio_2.cjs.entry.js +5 -3
  16. package/dist/cjs/ion-range.cjs.entry.js +2 -2
  17. package/dist/cjs/ion-textarea.cjs.entry.js +2 -2
  18. package/dist/cjs/ion-toggle.cjs.entry.js +5 -3
  19. package/dist/cjs/ionic.cjs.js +1 -1
  20. package/dist/cjs/loader.cjs.js +1 -1
  21. package/dist/collection/collection-manifest.json +1 -1
  22. package/dist/collection/components/checkbox/checkbox.ios.css +35 -0
  23. package/dist/collection/components/checkbox/checkbox.js +24 -4
  24. package/dist/collection/components/checkbox/checkbox.md.css +35 -0
  25. package/dist/collection/components/checkbox/test/item/checkbox.e2e.js +13 -0
  26. package/dist/collection/components/checkbox/test/label/checkbox.e2e.js +16 -0
  27. package/dist/collection/components/datetime/datetime.js +107 -84
  28. package/dist/collection/components/datetime/test/prefer-wheel/datetime.e2e.js +15 -0
  29. package/dist/collection/components/datetime/test/set-value/datetime.e2e.js +9 -12
  30. package/dist/collection/components/datetime/utils/data.js +14 -6
  31. package/dist/collection/components/datetime/utils/parse.js +18 -1
  32. package/dist/collection/components/datetime-button/datetime-button.js +3 -0
  33. package/dist/collection/components/radio/radio.ios.css +35 -0
  34. package/dist/collection/components/radio/radio.js +24 -4
  35. package/dist/collection/components/radio/radio.md.css +35 -0
  36. package/dist/collection/components/radio/test/item/radio.e2e.js +15 -0
  37. package/dist/collection/components/radio/test/label-placement/radio.e2e.js +20 -0
  38. package/dist/collection/components/range/range.ios.css +41 -1
  39. package/dist/collection/components/range/range.js +3 -3
  40. package/dist/collection/components/range/range.md.css +41 -1
  41. package/dist/collection/components/range/test/label/range.e2e.js +48 -0
  42. package/dist/collection/components/router-outlet/{router-outlet.js → route-outlet.js} +2 -2
  43. package/dist/collection/components/textarea/textarea.ios.css +0 -16
  44. package/dist/collection/components/textarea/textarea.js +1 -1
  45. package/dist/collection/components/textarea/textarea.md.css +0 -16
  46. package/dist/collection/components/toggle/test/item/toggle.e2e.js +15 -0
  47. package/dist/collection/components/toggle/test/label/toggle.e2e.js +16 -0
  48. package/dist/collection/components/toggle/toggle.ios.css +35 -1
  49. package/dist/collection/components/toggle/toggle.js +24 -4
  50. package/dist/collection/components/toggle/toggle.md.css +35 -1
  51. package/dist/docs.json +117 -20
  52. package/dist/esm/{data-4b448e3a.js → data-009dbf15.js} +31 -7
  53. package/dist/esm/ion-app_8.entry.js +2 -2
  54. package/dist/esm/ion-checkbox.entry.js +5 -3
  55. package/dist/esm/ion-datetime-button.entry.js +4 -1
  56. package/dist/esm/ion-datetime_3.entry.js +106 -82
  57. package/dist/esm/ion-radio_2.entry.js +5 -3
  58. package/dist/esm/ion-range.entry.js +2 -2
  59. package/dist/esm/ion-textarea.entry.js +2 -2
  60. package/dist/esm/ion-toggle.entry.js +5 -3
  61. package/dist/esm/ionic.js +1 -1
  62. package/dist/esm/loader.js +1 -1
  63. package/dist/esm-es5/data-009dbf15.js +4 -0
  64. package/dist/esm-es5/ion-app_8.entry.js +1 -1
  65. package/dist/esm-es5/ion-checkbox.entry.js +1 -1
  66. package/dist/esm-es5/ion-datetime-button.entry.js +1 -1
  67. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  68. package/dist/esm-es5/ion-radio_2.entry.js +1 -1
  69. package/dist/esm-es5/ion-range.entry.js +1 -1
  70. package/dist/esm-es5/ion-textarea.entry.js +1 -1
  71. package/dist/esm-es5/ion-toggle.entry.js +1 -1
  72. package/dist/esm-es5/ionic.js +1 -1
  73. package/dist/esm-es5/loader.js +1 -1
  74. package/dist/html.html-data.json +53 -5
  75. package/dist/ionic/ionic.esm.js +1 -1
  76. package/dist/ionic/p-1f4adf06.system.entry.js +4 -0
  77. package/dist/ionic/p-2f8bea16.entry.js +4 -0
  78. package/dist/ionic/p-40138199.entry.js +4 -0
  79. package/dist/ionic/p-44ef7224.system.js +4 -0
  80. package/dist/ionic/p-4ca1ce6b.system.entry.js +4 -0
  81. package/dist/ionic/{p-d3b8aebf.entry.js → p-67b891af.entry.js} +1 -1
  82. package/dist/ionic/p-6da27ab8.system.entry.js +4 -0
  83. package/dist/ionic/p-721933ff.system.js +1 -1
  84. package/dist/ionic/p-7cfd253c.js +4 -0
  85. package/dist/ionic/p-7d93084b.entry.js +4 -0
  86. package/dist/ionic/p-86c5c320.entry.js +4 -0
  87. package/dist/ionic/p-90ec5785.system.entry.js +4 -0
  88. package/dist/ionic/p-af986299.system.entry.js +4 -0
  89. package/dist/ionic/p-b321c999.entry.js +4 -0
  90. package/dist/ionic/p-c328936a.system.entry.js +4 -0
  91. package/dist/ionic/p-d8a0267b.system.entry.js +4 -0
  92. package/dist/ionic/p-f5c19164.entry.js +4 -0
  93. package/dist/types/components/checkbox/checkbox.d.ts +8 -1
  94. package/dist/types/components/datetime/datetime.d.ts +17 -10
  95. package/dist/types/components/datetime/utils/data.d.ts +1 -1
  96. package/dist/types/components/datetime/utils/parse.d.ts +3 -3
  97. package/dist/types/components/radio/radio.d.ts +8 -1
  98. package/dist/types/components/range/range.d.ts +2 -1
  99. package/dist/types/components/toast/toast-interface.d.ts +3 -0
  100. package/dist/types/components/toggle/toggle.d.ts +8 -1
  101. package/dist/types/components.d.ts +40 -16
  102. package/hydrate/index.js +166 -105
  103. package/package.json +1 -1
  104. package/dist/collection/components/textarea/test/cols/textarea.e2e.js +0 -37
  105. package/dist/esm-es5/data-4b448e3a.js +0 -4
  106. package/dist/ionic/p-0053fe81.system.entry.js +0 -4
  107. package/dist/ionic/p-2cdcd6f2.entry.js +0 -4
  108. package/dist/ionic/p-48144921.entry.js +0 -4
  109. package/dist/ionic/p-564aaba3.entry.js +0 -4
  110. package/dist/ionic/p-5b88f780.entry.js +0 -4
  111. package/dist/ionic/p-601fd53c.js +0 -4
  112. package/dist/ionic/p-98b618ce.system.entry.js +0 -4
  113. package/dist/ionic/p-b1e8f045.entry.js +0 -4
  114. package/dist/ionic/p-b3cfc6e1.system.entry.js +0 -4
  115. package/dist/ionic/p-bbae0ac3.system.entry.js +0 -4
  116. package/dist/ionic/p-cc8910e7.system.js +0 -4
  117. package/dist/ionic/p-dd0b6889.system.entry.js +0 -4
  118. package/dist/ionic/p-e53b8af4.system.entry.js +0 -4
  119. package/dist/ionic/p-f78e60a5.entry.js +0 -4
  120. package/dist/ionic/p-fd42994b.system.entry.js +0 -4
  121. /package/dist/collection/components/router-outlet/{router-outlet.css → route-outlet.css} +0 -0
  122. /package/dist/types/components/router-outlet/{router-outlet.d.ts → route-outlet.d.ts} +0 -0
@@ -51,4 +51,19 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
51
51
  expect(await list.screenshot()).toMatchSnapshot(screenshot(`radio-long-label-in-item`));
52
52
  });
53
53
  });
54
+ test.describe(title('radio: stacked label in item'), () => {
55
+ test('should render margins correctly when using stacked label in item', async ({ page }) => {
56
+ await page.setContent(`
57
+ <ion-list>
58
+ <ion-radio-group>
59
+ <ion-item>
60
+ <ion-radio label-placement="stacked">Enable Notifications</ion-radio>
61
+ </ion-item>
62
+ </ion-radio-group>
63
+ </ion-list>
64
+ `, config);
65
+ const list = page.locator('ion-list');
66
+ await expect(list).toHaveScreenshot(screenshot(`radio-stacked-label-in-item`));
67
+ });
68
+ });
54
69
  });
@@ -100,5 +100,25 @@ configs().forEach(({ title, screenshot, config }) => {
100
100
  expect(await radio.screenshot()).toMatchSnapshot(screenshot(`radio-label-fixed-justify-space-between`));
101
101
  });
102
102
  });
103
+ test.describe('radio: stacked placement', () => {
104
+ test('should align the label to the start of the container in the stacked position', async ({ page }) => {
105
+ await page.setContent(`
106
+ <ion-radio-group value="1">
107
+ <ion-radio label-placement="stacked" alignment="start" style="width: 200px" value="1">This is a long label</ion-radio>
108
+ </ion-radio-group>
109
+ `, config);
110
+ const radio = page.locator('ion-radio');
111
+ await expect(radio).toHaveScreenshot(screenshot(`radio-label-stacked-align-start`));
112
+ });
113
+ test('should align the label to the center of the container in the stacked position', async ({ page }) => {
114
+ await page.setContent(`
115
+ <ion-radio-group value="1">
116
+ <ion-radio label-placement="stacked" alignment="center" style="width: 200px" value="1">This is a long label</ion-radio>
117
+ </ion-radio-group>
118
+ `, config);
119
+ const radio = page.locator('ion-radio');
120
+ await expect(radio).toHaveScreenshot(screenshot(`radio-label-stacked-align-center`));
121
+ });
122
+ });
103
123
  });
104
124
  });
@@ -363,6 +363,25 @@
363
363
  max-width: 200px;
364
364
  }
365
365
 
366
+ /**
367
+ * Label is on top of the range.
368
+ */
369
+ :host(.range-label-placement-stacked) .range-wrapper {
370
+ flex-direction: column;
371
+ align-items: stretch;
372
+ }
373
+
374
+ :host(.range-label-placement-stacked) .label-text-wrapper {
375
+ /**
376
+ * The margin between the label and
377
+ * the range should be on the bottom
378
+ * when the label sits on top.
379
+ */
380
+ margin-left: 0;
381
+ margin-right: 0;
382
+ margin-bottom: 16px;
383
+ }
384
+
366
385
  :host {
367
386
  --knob-border-radius: 50%;
368
387
  --knob-background: #ffffff;
@@ -407,10 +426,31 @@
407
426
  margin-bottom: 0;
408
427
  }
409
428
 
410
- :host(.range-has-pin) {
429
+ :host(.range-has-pin:not(.range-label-placement-stacked)) {
430
+ /**
431
+ * The pin should not overlap any elements that are
432
+ * above the range. By adding padding to the top of the
433
+ * range, it provides a buffer for the pin to move into
434
+ * when it is pressed.
435
+ *
436
+ * The padding is not included when the label is stacked
437
+ * because the pin is below the label.
438
+ * It still requires a buffer to prevent the pin from
439
+ * overlapping the range. The buffer is added to the
440
+ * bottom of the range label instead of the host.
441
+ */
411
442
  padding-top: 20px;
412
443
  }
413
444
 
445
+ :host(.range-has-pin.range-label-placement-stacked) .label-text-wrapper {
446
+ /**
447
+ * The pin should not overlap the stacked label. By adding
448
+ * margin to the bottom of the label, it provides a buffer
449
+ * for the pin to move into when it is pressed.
450
+ */
451
+ margin-bottom: 20px;
452
+ }
453
+
414
454
  .range-bar-active {
415
455
  bottom: 0;
416
456
  width: auto;
@@ -808,15 +808,15 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
808
808
  "type": "string",
809
809
  "mutable": false,
810
810
  "complexType": {
811
- "original": "'start' | 'end' | 'fixed'",
812
- "resolved": "\"end\" | \"fixed\" | \"start\"",
811
+ "original": "'start' | 'end' | 'fixed' | 'stacked'",
812
+ "resolved": "\"end\" | \"fixed\" | \"stacked\" | \"start\"",
813
813
  "references": {}
814
814
  },
815
815
  "required": false,
816
816
  "optional": false,
817
817
  "docs": {
818
818
  "tags": [],
819
- "text": "Where to place the label relative to the range.\n`\"start\"`: The label will appear to the left of the range in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the range in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\")."
819
+ "text": "Where to place the label relative to the range.\n`\"start\"`: The label will appear to the left of the range in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the range in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\").\n`\"stacked\"`: The label will appear above the range regardless of the direction."
820
820
  },
821
821
  "attribute": "label-placement",
822
822
  "reflect": false,
@@ -363,6 +363,25 @@
363
363
  max-width: 200px;
364
364
  }
365
365
 
366
+ /**
367
+ * Label is on top of the range.
368
+ */
369
+ :host(.range-label-placement-stacked) .range-wrapper {
370
+ flex-direction: column;
371
+ align-items: stretch;
372
+ }
373
+
374
+ :host(.range-label-placement-stacked) .label-text-wrapper {
375
+ /**
376
+ * The margin between the label and
377
+ * the range should be on the bottom
378
+ * when the label sits on top.
379
+ */
380
+ margin-left: 0;
381
+ margin-right: 0;
382
+ margin-bottom: 16px;
383
+ }
384
+
366
385
  :host {
367
386
  --knob-border-radius: 50%;
368
387
  --knob-background: var(--bar-background-active);
@@ -423,10 +442,31 @@
423
442
  margin-bottom: 0;
424
443
  }
425
444
 
426
- :host(.range-has-pin) {
445
+ :host(.range-has-pin:not(.range-label-placement-stacked)) {
446
+ /**
447
+ * The pin should not overlap any elements that are
448
+ * above the range. By adding padding to the top of the
449
+ * range, it provides a buffer for the pin to move into
450
+ * when it is pressed.
451
+ *
452
+ * The padding is not included when the label is stacked
453
+ * because the pin is below the label.
454
+ * It still requires a buffer to prevent the pin from
455
+ * overlapping the range. The buffer is added to the
456
+ * bottom of the range label instead of the host.
457
+ */
427
458
  padding-top: 28px;
428
459
  }
429
460
 
461
+ :host(.range-has-pin.range-label-placement-stacked) .label-text-wrapper {
462
+ /**
463
+ * The pin should not overlap the stacked label. By adding
464
+ * margin to the bottom of the label, it provides a buffer
465
+ * for the pin to move into when it is pressed.
466
+ */
467
+ margin-bottom: 28px;
468
+ }
469
+
430
470
  .range-bar-active {
431
471
  bottom: 0;
432
472
  width: auto;
@@ -40,6 +40,17 @@ configs().forEach(({ title, screenshot, config }) => {
40
40
  const range = page.locator('ion-range');
41
41
  expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-no-items-fixed`));
42
42
  });
43
+ test('should render label above the range slider', async ({ page }) => {
44
+ await page.setContent(`
45
+ <div id="container" style="padding-inline-start: 20px;">
46
+ <ion-range label-placement="stacked">
47
+ <span slot="label">Volume</span>
48
+ </ion-range>
49
+ </div>
50
+ `, config);
51
+ const range = page.locator('#container');
52
+ await expect(range).toHaveScreenshot(screenshot(`range-no-items-stacked`));
53
+ });
43
54
  });
44
55
  test.describe('range: start and end items', () => {
45
56
  test('should render a range with no visible label', async ({ page }) => {
@@ -85,6 +96,17 @@ configs().forEach(({ title, screenshot, config }) => {
85
96
  const range = page.locator('ion-range');
86
97
  expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-items-fixed`));
87
98
  });
99
+ test('should render label above the range slider', async ({ page }) => {
100
+ await page.setContent(`
101
+ <ion-range label-placement="stacked">
102
+ <ion-icon name="volume-off" slot="start"></ion-icon>
103
+ <ion-icon name="volume-high" slot="end"></ion-icon>
104
+ <span slot="label">Volume</span>
105
+ </ion-range>
106
+ `, config);
107
+ const range = page.locator('ion-range');
108
+ await expect(range).toHaveScreenshot(screenshot(`range-items-stacked`));
109
+ });
88
110
  });
89
111
  test.describe('range: label prop', () => {
90
112
  test('should render label in the start placement', async ({ page }) => {
@@ -102,6 +124,15 @@ configs().forEach(({ title, screenshot, config }) => {
102
124
  const range = page.locator('ion-range');
103
125
  expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-label-prop-fixed`));
104
126
  });
127
+ test('should render label above the range slider', async ({ page }) => {
128
+ await page.setContent(`
129
+ <div id="container" style="padding-inline-start: 20px;">
130
+ <ion-range label-placement="stacked" label="Volume"></ion-range>
131
+ </div>
132
+ `, config);
133
+ const range = page.locator('#container');
134
+ await expect(range).toHaveScreenshot(screenshot(`range-label-prop-stacked`));
135
+ });
105
136
  });
106
137
  });
107
138
  });
@@ -117,4 +148,21 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
117
148
  expect(await range.screenshot()).toMatchSnapshot(screenshot(`range-label-truncate`));
118
149
  });
119
150
  });
151
+ test.describe(title('range: with pin'), () => {
152
+ test('should render pin below a stacked label', async ({ page }) => {
153
+ await page.setContent(`
154
+ <div id="container" style="padding-inline-start: 20px;">
155
+ <ion-range label-placement="stacked" pin="true">
156
+ <span slot="label">Volume</span>
157
+ </ion-range>
158
+ </div>
159
+ `, config);
160
+ const container = page.locator('#container');
161
+ const range = page.locator('ion-range');
162
+ const knob = range.locator('.range-knob-handle');
163
+ // Force the pin to show
164
+ await knob.evaluate((el) => el.classList.add('ion-focused'));
165
+ await expect(container).toHaveScreenshot(screenshot(`range-stacked-pin`));
166
+ });
167
+ });
120
168
  });
@@ -190,12 +190,12 @@ export class RouterOutlet {
190
190
  static get encapsulation() { return "shadow"; }
191
191
  static get originalStyleUrls() {
192
192
  return {
193
- "$": ["router-outlet.scss"]
193
+ "$": ["route-outlet.scss"]
194
194
  };
195
195
  }
196
196
  static get styleUrls() {
197
197
  return {
198
- "$": ["router-outlet.css"]
198
+ "$": ["route-outlet.css"]
199
199
  };
200
200
  }
201
201
  static get properties() {
@@ -55,22 +55,6 @@
55
55
  box-sizing: border-box;
56
56
  }
57
57
 
58
- /**
59
- * When the cols property is set we should
60
- * respect that width instead of defaulting
61
- * to taking up the entire line.
62
- * Requires both the cols and autoGrow
63
- * properties to be reflected as attributes
64
- * on the host.
65
- *
66
- * cols does not work with autoGrow because
67
- * autoGrow would prevent line breaks from naturally
68
- * occurring until the textarea takes up the entire line width.
69
- */
70
- :host([cols]:not([auto-grow])) {
71
- width: fit-content;
72
- }
73
-
74
58
  :host(.legacy-textarea) {
75
59
  flex: 1;
76
60
  background: var(--background);
@@ -689,7 +689,7 @@ Developers can use the "legacy" property to continue using the legacy form marku
689
689
  "text": "The visible width of the text control, in average character widths. If it is specified, it must be a positive integer."
690
690
  },
691
691
  "attribute": "cols",
692
- "reflect": true
692
+ "reflect": false
693
693
  },
694
694
  "rows": {
695
695
  "type": "number",
@@ -55,22 +55,6 @@
55
55
  box-sizing: border-box;
56
56
  }
57
57
 
58
- /**
59
- * When the cols property is set we should
60
- * respect that width instead of defaulting
61
- * to taking up the entire line.
62
- * Requires both the cols and autoGrow
63
- * properties to be reflected as attributes
64
- * on the host.
65
- *
66
- * cols does not work with autoGrow because
67
- * autoGrow would prevent line breaks from naturally
68
- * occurring until the textarea takes up the entire line width.
69
- */
70
- :host([cols]:not([auto-grow])) {
71
- width: fit-content;
72
- }
73
-
74
58
  :host(.legacy-textarea) {
75
59
  flex: 1;
76
60
  background: var(--background);
@@ -58,4 +58,19 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
58
58
  expect(await list.screenshot()).toMatchSnapshot(screenshot(`toggle-long-label-in-item`));
59
59
  });
60
60
  });
61
+ test.describe(title('toggle: stacked label in item'), () => {
62
+ test('should render margins correctly when using stacked label in item', async ({ page }) => {
63
+ await page.setContent(`
64
+ <ion-list>
65
+ <ion-radio-group>
66
+ <ion-item>
67
+ <ion-toggle label-placement="stacked">Enable Notifications</ion-toggle>
68
+ </ion-item>
69
+ </ion-radio-group>
70
+ </ion-list>
71
+ `, config);
72
+ const list = page.locator('ion-list');
73
+ await expect(list).toHaveScreenshot(screenshot(`toggle-stacked-label-in-item`));
74
+ });
75
+ });
61
76
  });
@@ -90,4 +90,20 @@ configs().forEach(({ title, screenshot, config }) => {
90
90
  expect(await toggle.screenshot()).toMatchSnapshot(screenshot(`toggle-label-fixed-justify-space-between`));
91
91
  });
92
92
  });
93
+ test.describe(title('toggle: stacked placement'), () => {
94
+ test('should align the label to the start of the container in the stacked position', async ({ page }) => {
95
+ await page.setContent(`
96
+ <ion-toggle label-placement="stacked" alignment="start" style="width: 200px">This is a long label</ion-toggle>
97
+ `, config);
98
+ const toggle = page.locator('ion-toggle');
99
+ await expect(toggle).toHaveScreenshot(screenshot(`toggle-label-stacked-align-start`));
100
+ });
101
+ test('should align the label to the center of the container in the stacked position', async ({ page }) => {
102
+ await page.setContent(`
103
+ <ion-toggle label-placement="stacked" alignment="center" style="width: 200px">This is a long label</ion-toggle>
104
+ `, config);
105
+ const toggle = page.locator('ion-toggle');
106
+ await expect(toggle).toHaveScreenshot(screenshot(`toggle-label-stacked-align-center`));
107
+ });
108
+ });
93
109
  });
@@ -123,7 +123,6 @@ input {
123
123
  display: flex;
124
124
  position: relative;
125
125
  flex-grow: 1;
126
- align-items: center;
127
126
  height: inherit;
128
127
  transition: background-color 15ms linear;
129
128
  cursor: inherit;
@@ -146,6 +145,15 @@ input {
146
145
  margin-bottom: 10px;
147
146
  }
148
147
 
148
+ :host(.in-item.toggle-label-placement-stacked) .label-text-wrapper {
149
+ margin-top: 10px;
150
+ margin-bottom: 16px;
151
+ }
152
+
153
+ :host(.in-item.toggle-label-placement-stacked) .native-wrapper {
154
+ margin-bottom: 10px;
155
+ }
156
+
149
157
  /**
150
158
  * If no label text is placed into the slot
151
159
  * then the element should be hidden otherwise
@@ -172,6 +180,14 @@ input {
172
180
  justify-content: end;
173
181
  }
174
182
 
183
+ :host(.toggle-alignment-start) .toggle-wrapper {
184
+ align-items: start;
185
+ }
186
+
187
+ :host(.toggle-alignment-center) .toggle-wrapper {
188
+ align-items: center;
189
+ }
190
+
175
191
  /**
176
192
  * Label is on the left of the input in LTR and
177
193
  * on the right in RTL.
@@ -235,6 +251,24 @@ input {
235
251
  max-width: 200px;
236
252
  }
237
253
 
254
+ /**
255
+ * Label is on top of the toggle.
256
+ */
257
+ :host(.toggle-label-placement-stacked) .toggle-wrapper {
258
+ flex-direction: column;
259
+ }
260
+
261
+ :host(.toggle-label-placement-stacked) .label-text-wrapper {
262
+ /**
263
+ * The margin between the label and
264
+ * the toggle should be on the bottom
265
+ * when the label sits on top.
266
+ */
267
+ margin-left: 0;
268
+ margin-right: 0;
269
+ margin-bottom: 16px;
270
+ }
271
+
238
272
  .toggle-icon-wrapper {
239
273
  display: flex;
240
274
  position: relative;
@@ -71,6 +71,7 @@ export class Toggle {
71
71
  this.labelPlacement = 'start';
72
72
  this.legacy = undefined;
73
73
  this.justify = 'space-between';
74
+ this.alignment = 'center';
74
75
  }
75
76
  disabledChanged() {
76
77
  this.emitStyle();
@@ -168,7 +169,7 @@ export class Toggle {
168
169
  return legacyFormController.hasLegacyControl() ? this.renderLegacyToggle() : this.renderToggle();
169
170
  }
170
171
  renderToggle() {
171
- const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name } = this;
172
+ const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment } = this;
172
173
  const mode = getIonMode(this);
173
174
  const value = this.getValue();
174
175
  const rtl = isRTL(el) ? 'rtl' : 'ltr';
@@ -180,6 +181,7 @@ export class Toggle {
180
181
  'toggle-checked': checked,
181
182
  'toggle-disabled': disabled,
182
183
  [`toggle-justify-${justify}`]: true,
184
+ [`toggle-alignment-${alignment}`]: true,
183
185
  [`toggle-label-placement-${labelPlacement}`]: true,
184
186
  [`toggle-${rtl}`]: true,
185
187
  }) }, h("label", { class: "toggle-wrapper" }, h("input", Object.assign({ type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, onFocus: () => this.onFocus(), onBlur: () => this.onBlur(), ref: (focusEl) => (this.focusEl = focusEl) }, this.inheritedAttributes)), h("div", { class: {
@@ -352,15 +354,15 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
352
354
  "type": "string",
353
355
  "mutable": false,
354
356
  "complexType": {
355
- "original": "'start' | 'end' | 'fixed'",
356
- "resolved": "\"end\" | \"fixed\" | \"start\"",
357
+ "original": "'start' | 'end' | 'fixed' | 'stacked'",
358
+ "resolved": "\"end\" | \"fixed\" | \"stacked\" | \"start\"",
357
359
  "references": {}
358
360
  },
359
361
  "required": false,
360
362
  "optional": false,
361
363
  "docs": {
362
364
  "tags": [],
363
- "text": "Where to place the label relative to the input.\n`\"start\"`: The label will appear to the left of the toggle in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the toggle in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\")."
365
+ "text": "Where to place the label relative to the input.\n`\"start\"`: The label will appear to the left of the toggle in LTR and to the right in RTL.\n`\"end\"`: The label will appear to the right of the toggle in LTR and to the left in RTL.\n`\"fixed\"`: The label has the same behavior as `\"start\"` except it also has a fixed width. Long text will be truncated with ellipses (\"...\").\n`\"stacked\"`: The label will appear above the toggle regardless of the direction. The alignment of the label can be controlled with the `alignment` property."
364
366
  },
365
367
  "attribute": "label-placement",
366
368
  "reflect": false,
@@ -400,6 +402,24 @@ Developers can dismiss this warning by removing their usage of the "legacy" prop
400
402
  "attribute": "justify",
401
403
  "reflect": false,
402
404
  "defaultValue": "'space-between'"
405
+ },
406
+ "alignment": {
407
+ "type": "string",
408
+ "mutable": false,
409
+ "complexType": {
410
+ "original": "'start' | 'center'",
411
+ "resolved": "\"center\" | \"start\"",
412
+ "references": {}
413
+ },
414
+ "required": false,
415
+ "optional": false,
416
+ "docs": {
417
+ "tags": [],
418
+ "text": "How to control the alignment of the toggle and label on the cross axis.\n``\"start\"`: The label and control will appear on the left of the cross axis in LTR, and on the right side in RTL.\n`\"center\"`: The label and control will appear at the center of the cross axis in both LTR and RTL."
419
+ },
420
+ "attribute": "alignment",
421
+ "reflect": false,
422
+ "defaultValue": "'center'"
403
423
  }
404
424
  };
405
425
  }
@@ -123,7 +123,6 @@ input {
123
123
  display: flex;
124
124
  position: relative;
125
125
  flex-grow: 1;
126
- align-items: center;
127
126
  height: inherit;
128
127
  transition: background-color 15ms linear;
129
128
  cursor: inherit;
@@ -146,6 +145,15 @@ input {
146
145
  margin-bottom: 10px;
147
146
  }
148
147
 
148
+ :host(.in-item.toggle-label-placement-stacked) .label-text-wrapper {
149
+ margin-top: 10px;
150
+ margin-bottom: 16px;
151
+ }
152
+
153
+ :host(.in-item.toggle-label-placement-stacked) .native-wrapper {
154
+ margin-bottom: 10px;
155
+ }
156
+
149
157
  /**
150
158
  * If no label text is placed into the slot
151
159
  * then the element should be hidden otherwise
@@ -172,6 +180,14 @@ input {
172
180
  justify-content: end;
173
181
  }
174
182
 
183
+ :host(.toggle-alignment-start) .toggle-wrapper {
184
+ align-items: start;
185
+ }
186
+
187
+ :host(.toggle-alignment-center) .toggle-wrapper {
188
+ align-items: center;
189
+ }
190
+
175
191
  /**
176
192
  * Label is on the left of the input in LTR and
177
193
  * on the right in RTL.
@@ -235,6 +251,24 @@ input {
235
251
  max-width: 200px;
236
252
  }
237
253
 
254
+ /**
255
+ * Label is on top of the toggle.
256
+ */
257
+ :host(.toggle-label-placement-stacked) .toggle-wrapper {
258
+ flex-direction: column;
259
+ }
260
+
261
+ :host(.toggle-label-placement-stacked) .label-text-wrapper {
262
+ /**
263
+ * The margin between the label and
264
+ * the toggle should be on the bottom
265
+ * when the label sits on top.
266
+ */
267
+ margin-left: 0;
268
+ margin-right: 0;
269
+ margin-bottom: 16px;
270
+ }
271
+
238
272
  .toggle-icon-wrapper {
239
273
  display: flex;
240
274
  position: relative;