@ionic/core 8.8.9-dev.11779403760.13ea2a08 → 8.8.9-dev.11779411201.1a483e09
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/components/index.js +1 -1
- package/components/ion-alert.js +1 -1
- package/components/ion-infinite-scroll-content.js +1 -1
- package/components/ion-loading.js +1 -1
- package/components/ion-refresher-content.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/ion-toast.js +1 -1
- package/components/p-DdgejEM3.js +4 -0
- package/components/{p-NVAi5HQQ.js → p-DohYi97K.js} +1 -1
- package/dist/cjs/{config-BiqQSDrb.js → config-OJmFhoxl.js} +56 -8
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/ion-alert.cjs.entry.js +1 -1
- package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-loading.cjs.entry.js +1 -1
- package/dist/cjs/ion-refresher_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-select_3.cjs.entry.js +20 -7
- package/dist/cjs/ion-toast.cjs.entry.js +1 -1
- package/dist/collection/components/select/select.js +20 -7
- package/dist/collection/utils/sanitization/index.js +55 -8
- package/dist/docs.json +1 -1
- package/dist/esm/{config-DLfuRiEz.js → config-C5F1aoVz.js} +56 -9
- package/dist/esm/index.js +1 -1
- package/dist/esm/ion-alert.entry.js +1 -1
- package/dist/esm/ion-infinite-scroll_2.entry.js +1 -1
- package/dist/esm/ion-loading.entry.js +1 -1
- package/dist/esm/ion-refresher_2.entry.js +1 -1
- package/dist/esm/ion-select_3.entry.js +20 -7
- package/dist/esm/ion-toast.entry.js +1 -1
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-3f1dbb47.entry.js → p-3e186bd8.entry.js} +1 -1
- package/dist/ionic/{p-b4ba0050.entry.js → p-4a5ebd2b.entry.js} +1 -1
- package/dist/ionic/{p-7e6112fc.entry.js → p-729a5b04.entry.js} +1 -1
- package/dist/ionic/{p-c21a780d.entry.js → p-7d25ad9a.entry.js} +1 -1
- package/dist/ionic/p-c6516c13.entry.js +4 -0
- package/dist/ionic/{p-fd519e2b.entry.js → p-e7505567.entry.js} +1 -1
- package/dist/ionic/p-pI5qh5HW.js +4 -0
- package/dist/types/utils/sanitization/index.d.ts +18 -4
- package/hydrate/index.js +74 -14
- package/hydrate/index.mjs +74 -14
- package/package.json +1 -1
- package/components/p-VVrXfwLZ.js +0 -4
- package/dist/ionic/p-c5118189.entry.js +0 -4
- package/dist/ionic/p-u6HLvq0g.js +0 -4
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
var index = require('./index-CzcLEdQ5.js');
|
|
7
7
|
var index$1 = require('./index-BJrpF9T3.js');
|
|
8
8
|
var ionicGlobal = require('./ionic-global-BW5tRzrz.js');
|
|
9
|
-
var config = require('./config-
|
|
9
|
+
var config = require('./config-OJmFhoxl.js');
|
|
10
10
|
require('./helpers-DJYxKN5U.js');
|
|
11
11
|
require('./focus-visible-BIj-I3-C.js');
|
|
12
12
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-CzcLEdQ5.js');
|
|
7
|
-
var config = require('./config-
|
|
7
|
+
var config = require('./config-OJmFhoxl.js');
|
|
8
8
|
var helpers = require('./helpers-DJYxKN5U.js');
|
|
9
9
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
10
10
|
var overlays = require('./overlays-BuMIwR8B.js');
|
|
@@ -10,7 +10,7 @@ var helpers = require('./helpers-DJYxKN5U.js');
|
|
|
10
10
|
var haptic = require('./haptic-CQJGW58i.js');
|
|
11
11
|
var ionicGlobal = require('./ionic-global-BW5tRzrz.js');
|
|
12
12
|
var animation = require('./animation-BZJ2wKuM.js');
|
|
13
|
-
var config = require('./config-
|
|
13
|
+
var config = require('./config-OJmFhoxl.js');
|
|
14
14
|
var index$2 = require('./index-CgAbCW6L.js');
|
|
15
15
|
var spinnerConfigs = require('./spinner-configs-DxHKnd3-.js');
|
|
16
16
|
require('./focus-visible-BIj-I3-C.js');
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
var index = require('./index-CzcLEdQ5.js');
|
|
7
7
|
var caretDown = require('./caret-down-vtVgfXIs.js');
|
|
8
|
-
var config = require('./config-
|
|
8
|
+
var config = require('./config-OJmFhoxl.js');
|
|
9
9
|
var notchController = require('./notch-controller-BTZCPOsd.js');
|
|
10
10
|
var compareWithUtils = require('./compare-with-utils-DSicavqM.js');
|
|
11
11
|
var validity = require('./validity-QmuwEptc.js');
|
|
@@ -947,7 +947,7 @@ const Select = class {
|
|
|
947
947
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
948
948
|
*/
|
|
949
949
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
950
|
-
return (index.h(index.Host, { key: '
|
|
950
|
+
return (index.h(index.Host, { key: 'ce995037cea6ce309ead79f881772c628086ab45', onClick: this.onClick, class: theme.createColorClasses(this.color, {
|
|
951
951
|
[theme$1]: true,
|
|
952
952
|
'in-item': inItem,
|
|
953
953
|
'in-item-color': theme.hostContext('ion-item.ion-color', el),
|
|
@@ -966,7 +966,7 @@ const Select = class {
|
|
|
966
966
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
967
967
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
968
968
|
[`select-size-${size}`]: size !== undefined,
|
|
969
|
-
}) }, index.h("label", { key: '
|
|
969
|
+
}) }, index.h("label", { key: '862fa9d03a7feeeac05463842898849001724cef', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick, part: "wrapper" }, this.renderLabelContainer(), index.h("div", { key: '1e91cc271790756a97cfc0c8caaca767ba793467', class: "select-wrapper-inner", part: "inner" },
|
|
970
970
|
/**
|
|
971
971
|
* For the ionic theme, we render the outline container here
|
|
972
972
|
* instead of higher up, so it can be positioned relative to
|
|
@@ -976,7 +976,7 @@ const Select = class {
|
|
|
976
976
|
* <label> element, ensuring that clicking the label text
|
|
977
977
|
* focuses the select.
|
|
978
978
|
*/
|
|
979
|
-
theme$1 === 'ionic' && fill === 'outline' && index.h("div", { key: '
|
|
979
|
+
theme$1 === 'ionic' && fill === 'outline' && index.h("div", { key: '14a2a14daa10625fa59f8f148e124a1c0c147319', class: "select-outline" }), index.h("slot", { key: 'a5a4372feffbeb3652e6045e64cbfacb66c58da9', name: "start" }), index.h("div", { key: 'b6ac054eaca720a21e0617cf413e576ef23a60e8', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), index.h("slot", { key: '140d299760a7c7f186b31e9e38f968534b1e116a', name: "end" }), shouldRenderInnerIcon && this.renderSelectIcon()), shouldRenderOuterIcon && this.renderSelectIcon(), shouldRenderHighlight && index.h("div", { key: 'b1a1e98302b0e87c3cd850e9abd8faf946a2b492', class: "select-highlight" })), this.renderBottomContent()));
|
|
980
980
|
}
|
|
981
981
|
get el() { return index.getElement(this); }
|
|
982
982
|
static get watchers() { return {
|
|
@@ -1129,6 +1129,17 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1129
1129
|
if (!slotName && nodes.every((n) => n.nodeType === Node.TEXT_NODE)) {
|
|
1130
1130
|
return nodes.map((n) => { var _a; return (_a = n.textContent) === null || _a === void 0 ? void 0 : _a.trim(); }).join(' ') || null;
|
|
1131
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Mirror known custom-element properties (e.g. ion-icon's `icon`)
|
|
1134
|
+
* onto attributes before cloning. Frameworks like Vue set these as
|
|
1135
|
+
* DOM properties, which `cloneNode` doesn't copy, so without this
|
|
1136
|
+
* step the cloned overlay copy renders without the prop's value.
|
|
1137
|
+
*/
|
|
1138
|
+
nodes.forEach((n) => {
|
|
1139
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1140
|
+
config.reflectPropertiesToAttributes(n);
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1132
1143
|
// Clone each node into a temporary container
|
|
1133
1144
|
const container = document.createElement('div');
|
|
1134
1145
|
nodes.forEach((n) => {
|
|
@@ -1142,9 +1153,11 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1142
1153
|
}
|
|
1143
1154
|
container.appendChild(clone);
|
|
1144
1155
|
});
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1156
|
+
/**
|
|
1157
|
+
* Sanitize the cloned DOM in place. Trusted attributes (size, color,
|
|
1158
|
+
* shape, etc.) are preserved; event handlers, javascript: URLs, and
|
|
1159
|
+
* blocked tags are stripped.
|
|
1160
|
+
*/
|
|
1148
1161
|
config.sanitizeDOMTree(container);
|
|
1149
1162
|
if (useHTML) {
|
|
1150
1163
|
return container.innerHTML.trim() || null;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-CzcLEdQ5.js');
|
|
7
|
-
var config = require('./config-
|
|
7
|
+
var config = require('./config-OJmFhoxl.js');
|
|
8
8
|
var helpers = require('./helpers-DJYxKN5U.js');
|
|
9
9
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
10
10
|
var overlays = require('./overlays-BuMIwR8B.js');
|
|
@@ -9,7 +9,7 @@ import { focusVisibleElement, renderHiddenInput, inheritAttributes } from "../..
|
|
|
9
9
|
import { printIonWarning } from "../../utils/logging/index";
|
|
10
10
|
import { actionSheetController, alertController, popoverController, modalController } from "../../utils/overlays";
|
|
11
11
|
import { isRTL } from "../../utils/rtl/index";
|
|
12
|
-
import { sanitizeDOMTree } from "../../utils/sanitization/index";
|
|
12
|
+
import { reflectPropertiesToAttributes, sanitizeDOMTree } from "../../utils/sanitization/index";
|
|
13
13
|
import { createColorClasses, hostContext } from "../../utils/theme";
|
|
14
14
|
import { watchForOptions } from "../../utils/watch-options";
|
|
15
15
|
import { caretDownSharp, chevronExpand } from "ionicons/icons";
|
|
@@ -994,7 +994,7 @@ export class Select {
|
|
|
994
994
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
995
995
|
*/
|
|
996
996
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
997
|
-
return (h(Host, { key: '
|
|
997
|
+
return (h(Host, { key: 'ce995037cea6ce309ead79f881772c628086ab45', onClick: this.onClick, class: createColorClasses(this.color, {
|
|
998
998
|
[theme]: true,
|
|
999
999
|
'in-item': inItem,
|
|
1000
1000
|
'in-item-color': hostContext('ion-item.ion-color', el),
|
|
@@ -1013,7 +1013,7 @@ export class Select {
|
|
|
1013
1013
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
1014
1014
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
1015
1015
|
[`select-size-${size}`]: size !== undefined,
|
|
1016
|
-
}) }, h("label", { key: '
|
|
1016
|
+
}) }, h("label", { key: '862fa9d03a7feeeac05463842898849001724cef', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick, part: "wrapper" }, this.renderLabelContainer(), h("div", { key: '1e91cc271790756a97cfc0c8caaca767ba793467', class: "select-wrapper-inner", part: "inner" },
|
|
1017
1017
|
/**
|
|
1018
1018
|
* For the ionic theme, we render the outline container here
|
|
1019
1019
|
* instead of higher up, so it can be positioned relative to
|
|
@@ -1023,7 +1023,7 @@ export class Select {
|
|
|
1023
1023
|
* <label> element, ensuring that clicking the label text
|
|
1024
1024
|
* focuses the select.
|
|
1025
1025
|
*/
|
|
1026
|
-
theme === 'ionic' && fill === 'outline' && h("div", { key: '
|
|
1026
|
+
theme === 'ionic' && fill === 'outline' && h("div", { key: '14a2a14daa10625fa59f8f148e124a1c0c147319', class: "select-outline" }), h("slot", { key: 'a5a4372feffbeb3652e6045e64cbfacb66c58da9', name: "start" }), h("div", { key: 'b6ac054eaca720a21e0617cf413e576ef23a60e8', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: '140d299760a7c7f186b31e9e38f968534b1e116a', name: "end" }), shouldRenderInnerIcon && this.renderSelectIcon()), shouldRenderOuterIcon && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'b1a1e98302b0e87c3cd850e9abd8faf946a2b492', class: "select-highlight" })), this.renderBottomContent()));
|
|
1027
1027
|
}
|
|
1028
1028
|
static get is() { return "ion-select"; }
|
|
1029
1029
|
static get encapsulation() { return "shadow"; }
|
|
@@ -1846,6 +1846,17 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1846
1846
|
if (!slotName && nodes.every((n) => n.nodeType === Node.TEXT_NODE)) {
|
|
1847
1847
|
return nodes.map((n) => { var _a; return (_a = n.textContent) === null || _a === void 0 ? void 0 : _a.trim(); }).join(' ') || null;
|
|
1848
1848
|
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Mirror known custom-element properties (e.g. ion-icon's `icon`)
|
|
1851
|
+
* onto attributes before cloning. Frameworks like Vue set these as
|
|
1852
|
+
* DOM properties, which `cloneNode` doesn't copy, so without this
|
|
1853
|
+
* step the cloned overlay copy renders without the prop's value.
|
|
1854
|
+
*/
|
|
1855
|
+
nodes.forEach((n) => {
|
|
1856
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1857
|
+
reflectPropertiesToAttributes(n);
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1849
1860
|
// Clone each node into a temporary container
|
|
1850
1861
|
const container = document.createElement('div');
|
|
1851
1862
|
nodes.forEach((n) => {
|
|
@@ -1859,9 +1870,11 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1859
1870
|
}
|
|
1860
1871
|
container.appendChild(clone);
|
|
1861
1872
|
});
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1873
|
+
/**
|
|
1874
|
+
* Sanitize the cloned DOM in place. Trusted attributes (size, color,
|
|
1875
|
+
* shape, etc.) are preserved; event handlers, javascript: URLs, and
|
|
1876
|
+
* blocked tags are stripped.
|
|
1877
|
+
*/
|
|
1865
1878
|
sanitizeDOMTree(container);
|
|
1866
1879
|
if (useHTML) {
|
|
1867
1880
|
return container.innerHTML.trim() || null;
|
|
@@ -11,7 +11,7 @@ import { printIonError } from "../logging/index";
|
|
|
11
11
|
*
|
|
12
12
|
* Use this when you have an HTML string from an unknown source and need
|
|
13
13
|
* to render it via `innerHTML`. Prefer `sanitizeDOMTree` when the source
|
|
14
|
-
* is
|
|
14
|
+
* is a trusted DOM tree that must keep its component attributes
|
|
15
15
|
* (`size`, `color`, `shape`, etc.).
|
|
16
16
|
*
|
|
17
17
|
* @param untrustedString - The HTML string to sanitize. Pass an
|
|
@@ -95,14 +95,14 @@ export const sanitizeDOMString = (untrustedString) => {
|
|
|
95
95
|
}
|
|
96
96
|
};
|
|
97
97
|
/**
|
|
98
|
-
* Sanitize an entire
|
|
98
|
+
* Sanitize an entire trusted DOM tree in place.
|
|
99
99
|
*
|
|
100
100
|
* Removes blocked tags (`script`, `iframe`, etc.) from the subtree and
|
|
101
|
-
* then sanitizes attributes on every remaining element.
|
|
101
|
+
* then sanitizes attributes on every remaining element. Component
|
|
102
102
|
* attributes like `size`, `color`, and `shape` are preserved; event
|
|
103
103
|
* handlers (`on*`) and `javascript:` URLs are stripped.
|
|
104
104
|
*
|
|
105
|
-
* Use this when you have a DOM tree
|
|
105
|
+
* Use this when you have a DOM tree the developer controls (e.g.
|
|
106
106
|
* cloned slot content from a component) and you need to render it
|
|
107
107
|
* elsewhere safely.
|
|
108
108
|
*
|
|
@@ -127,7 +127,7 @@ export const sanitizeDOMTree = (root) => {
|
|
|
127
127
|
* clean those up as well
|
|
128
128
|
*/
|
|
129
129
|
// TODO(FW-2832): type (using Element triggers other type errors as well)
|
|
130
|
-
const sanitizeElement = (element,
|
|
130
|
+
const sanitizeElement = (element, allowSafeAttributes = false) => {
|
|
131
131
|
// IE uses childNodes, so ignore nodes that are not elements
|
|
132
132
|
if (element.nodeType && element.nodeType !== 1) {
|
|
133
133
|
return;
|
|
@@ -147,12 +147,12 @@ const sanitizeElement = (element, allowSafeAuthorAttributes = false) => {
|
|
|
147
147
|
const attributeName = attribute.name;
|
|
148
148
|
const lowerName = attributeName.toLowerCase();
|
|
149
149
|
// remove non-allowed attribs
|
|
150
|
-
if (!
|
|
150
|
+
if (!allowSafeAttributes && !allowedAttributes.includes(lowerName)) {
|
|
151
151
|
element.removeAttribute(attributeName);
|
|
152
152
|
continue;
|
|
153
153
|
}
|
|
154
154
|
// strip event-handler attributes (already removed by the allowlist
|
|
155
|
-
// when !
|
|
155
|
+
// when !allowSafeAttributes; this guards the permissive path)
|
|
156
156
|
if (lowerName.startsWith('on')) {
|
|
157
157
|
element.removeAttribute(attributeName);
|
|
158
158
|
continue;
|
|
@@ -183,7 +183,7 @@ const sanitizeElement = (element, allowSafeAuthorAttributes = false) => {
|
|
|
183
183
|
const childElements = getElementChildren(element);
|
|
184
184
|
/* eslint-disable-next-line */
|
|
185
185
|
for (let i = 0; i < childElements.length; i++) {
|
|
186
|
-
sanitizeElement(childElements[i],
|
|
186
|
+
sanitizeElement(childElements[i], allowSafeAttributes);
|
|
187
187
|
}
|
|
188
188
|
};
|
|
189
189
|
/**
|
|
@@ -208,8 +208,55 @@ const isSanitizerEnabled = () => {
|
|
|
208
208
|
}
|
|
209
209
|
return true;
|
|
210
210
|
};
|
|
211
|
+
/**
|
|
212
|
+
* Mirror known custom-element DOM properties onto attributes so they
|
|
213
|
+
* survive `cloneNode`. Call this on a DOM subtree before cloning it for
|
|
214
|
+
* rendering elsewhere (e.g. cloning slotted option content into an
|
|
215
|
+
* overlay).
|
|
216
|
+
*
|
|
217
|
+
* Only sets the attribute when the property holds a non-empty string
|
|
218
|
+
* and the attribute isn't already present, so existing attributes
|
|
219
|
+
* take precedence.
|
|
220
|
+
*
|
|
221
|
+
* @param root - The root element whose subtree (and itself) will be
|
|
222
|
+
* inspected.
|
|
223
|
+
*/
|
|
224
|
+
export const reflectPropertiesToAttributes = (root) => {
|
|
225
|
+
const candidates = [];
|
|
226
|
+
if (root.tagName in elementPropsToReflect) {
|
|
227
|
+
candidates.push(root);
|
|
228
|
+
}
|
|
229
|
+
for (const tagName of Object.keys(elementPropsToReflect)) {
|
|
230
|
+
candidates.push(...Array.from(root.querySelectorAll(tagName.toLowerCase())));
|
|
231
|
+
}
|
|
232
|
+
for (const el of candidates) {
|
|
233
|
+
if (!(el.tagName in elementPropsToReflect)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const props = elementPropsToReflect[el.tagName];
|
|
237
|
+
for (const prop of props) {
|
|
238
|
+
const value = el[prop];
|
|
239
|
+
if (typeof value === 'string' && value.length > 0 && !el.hasAttribute(prop)) {
|
|
240
|
+
el.setAttribute(prop, value);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
211
245
|
const allowedAttributes = ['class', 'id', 'href', 'src', 'name', 'slot'];
|
|
212
246
|
const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed'];
|
|
247
|
+
/**
|
|
248
|
+
* Properties on custom elements that frameworks (Vue, Angular) often
|
|
249
|
+
* set as DOM properties rather than attributes. `cloneNode` only copies
|
|
250
|
+
* attributes, so these values are lost when slotted content is cloned
|
|
251
|
+
* into an overlay. For each known custom element, we mirror the listed
|
|
252
|
+
* properties onto attributes so the cloned copy still has the data it
|
|
253
|
+
* needs to render.
|
|
254
|
+
*
|
|
255
|
+
* Keyed by uppercased tagName so the lookup matches `Element.tagName`.
|
|
256
|
+
*/
|
|
257
|
+
const elementPropsToReflect = {
|
|
258
|
+
'ION-ICON': ['icon', 'name', 'src', 'ios', 'md'],
|
|
259
|
+
};
|
|
213
260
|
export class IonicSafeString {
|
|
214
261
|
constructor(value) {
|
|
215
262
|
this.value = value;
|
package/dist/docs.json
CHANGED
|
@@ -12,7 +12,7 @@ import { j as printIonError } from './index-Omi_TcwW.js';
|
|
|
12
12
|
*
|
|
13
13
|
* Use this when you have an HTML string from an unknown source and need
|
|
14
14
|
* to render it via `innerHTML`. Prefer `sanitizeDOMTree` when the source
|
|
15
|
-
* is
|
|
15
|
+
* is a trusted DOM tree that must keep its component attributes
|
|
16
16
|
* (`size`, `color`, `shape`, etc.).
|
|
17
17
|
*
|
|
18
18
|
* @param untrustedString - The HTML string to sanitize. Pass an
|
|
@@ -96,14 +96,14 @@ const sanitizeDOMString = (untrustedString) => {
|
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
98
|
/**
|
|
99
|
-
* Sanitize an entire
|
|
99
|
+
* Sanitize an entire trusted DOM tree in place.
|
|
100
100
|
*
|
|
101
101
|
* Removes blocked tags (`script`, `iframe`, etc.) from the subtree and
|
|
102
|
-
* then sanitizes attributes on every remaining element.
|
|
102
|
+
* then sanitizes attributes on every remaining element. Component
|
|
103
103
|
* attributes like `size`, `color`, and `shape` are preserved; event
|
|
104
104
|
* handlers (`on*`) and `javascript:` URLs are stripped.
|
|
105
105
|
*
|
|
106
|
-
* Use this when you have a DOM tree
|
|
106
|
+
* Use this when you have a DOM tree the developer controls (e.g.
|
|
107
107
|
* cloned slot content from a component) and you need to render it
|
|
108
108
|
* elsewhere safely.
|
|
109
109
|
*
|
|
@@ -128,7 +128,7 @@ const sanitizeDOMTree = (root) => {
|
|
|
128
128
|
* clean those up as well
|
|
129
129
|
*/
|
|
130
130
|
// TODO(FW-2832): type (using Element triggers other type errors as well)
|
|
131
|
-
const sanitizeElement = (element,
|
|
131
|
+
const sanitizeElement = (element, allowSafeAttributes = false) => {
|
|
132
132
|
// IE uses childNodes, so ignore nodes that are not elements
|
|
133
133
|
if (element.nodeType && element.nodeType !== 1) {
|
|
134
134
|
return;
|
|
@@ -148,12 +148,12 @@ const sanitizeElement = (element, allowSafeAuthorAttributes = false) => {
|
|
|
148
148
|
const attributeName = attribute.name;
|
|
149
149
|
const lowerName = attributeName.toLowerCase();
|
|
150
150
|
// remove non-allowed attribs
|
|
151
|
-
if (!
|
|
151
|
+
if (!allowSafeAttributes && !allowedAttributes.includes(lowerName)) {
|
|
152
152
|
element.removeAttribute(attributeName);
|
|
153
153
|
continue;
|
|
154
154
|
}
|
|
155
155
|
// strip event-handler attributes (already removed by the allowlist
|
|
156
|
-
// when !
|
|
156
|
+
// when !allowSafeAttributes; this guards the permissive path)
|
|
157
157
|
if (lowerName.startsWith('on')) {
|
|
158
158
|
element.removeAttribute(attributeName);
|
|
159
159
|
continue;
|
|
@@ -184,7 +184,7 @@ const sanitizeElement = (element, allowSafeAuthorAttributes = false) => {
|
|
|
184
184
|
const childElements = getElementChildren(element);
|
|
185
185
|
/* eslint-disable-next-line */
|
|
186
186
|
for (let i = 0; i < childElements.length; i++) {
|
|
187
|
-
sanitizeElement(childElements[i],
|
|
187
|
+
sanitizeElement(childElements[i], allowSafeAttributes);
|
|
188
188
|
}
|
|
189
189
|
};
|
|
190
190
|
/**
|
|
@@ -209,8 +209,55 @@ const isSanitizerEnabled = () => {
|
|
|
209
209
|
}
|
|
210
210
|
return true;
|
|
211
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* Mirror known custom-element DOM properties onto attributes so they
|
|
214
|
+
* survive `cloneNode`. Call this on a DOM subtree before cloning it for
|
|
215
|
+
* rendering elsewhere (e.g. cloning slotted option content into an
|
|
216
|
+
* overlay).
|
|
217
|
+
*
|
|
218
|
+
* Only sets the attribute when the property holds a non-empty string
|
|
219
|
+
* and the attribute isn't already present, so existing attributes
|
|
220
|
+
* take precedence.
|
|
221
|
+
*
|
|
222
|
+
* @param root - The root element whose subtree (and itself) will be
|
|
223
|
+
* inspected.
|
|
224
|
+
*/
|
|
225
|
+
const reflectPropertiesToAttributes = (root) => {
|
|
226
|
+
const candidates = [];
|
|
227
|
+
if (root.tagName in elementPropsToReflect) {
|
|
228
|
+
candidates.push(root);
|
|
229
|
+
}
|
|
230
|
+
for (const tagName of Object.keys(elementPropsToReflect)) {
|
|
231
|
+
candidates.push(...Array.from(root.querySelectorAll(tagName.toLowerCase())));
|
|
232
|
+
}
|
|
233
|
+
for (const el of candidates) {
|
|
234
|
+
if (!(el.tagName in elementPropsToReflect)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const props = elementPropsToReflect[el.tagName];
|
|
238
|
+
for (const prop of props) {
|
|
239
|
+
const value = el[prop];
|
|
240
|
+
if (typeof value === 'string' && value.length > 0 && !el.hasAttribute(prop)) {
|
|
241
|
+
el.setAttribute(prop, value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
212
246
|
const allowedAttributes = ['class', 'id', 'href', 'src', 'name', 'slot'];
|
|
213
247
|
const blockedTags = ['script', 'style', 'iframe', 'meta', 'link', 'object', 'embed'];
|
|
248
|
+
/**
|
|
249
|
+
* Properties on custom elements that frameworks (Vue, Angular) often
|
|
250
|
+
* set as DOM properties rather than attributes. `cloneNode` only copies
|
|
251
|
+
* attributes, so these values are lost when slotted content is cloned
|
|
252
|
+
* into an overlay. For each known custom element, we mirror the listed
|
|
253
|
+
* properties onto attributes so the cloned copy still has the data it
|
|
254
|
+
* needs to render.
|
|
255
|
+
*
|
|
256
|
+
* Keyed by uppercased tagName so the lookup matches `Element.tagName`.
|
|
257
|
+
*/
|
|
258
|
+
const elementPropsToReflect = {
|
|
259
|
+
'ION-ICON': ['icon', 'name', 'src', 'ios', 'md'],
|
|
260
|
+
};
|
|
214
261
|
class IonicSafeString {
|
|
215
262
|
constructor(value) {
|
|
216
263
|
this.value = value;
|
|
@@ -244,4 +291,4 @@ const getMode = () => {
|
|
|
244
291
|
};
|
|
245
292
|
const ENABLE_HTML_CONTENT_DEFAULT = false;
|
|
246
293
|
|
|
247
|
-
export { ENABLE_HTML_CONTENT_DEFAULT as E, IonicSafeString as I, sanitizeDOMString as a, sanitizeDOMTree as b, getMode as g, setupConfig as s };
|
|
294
|
+
export { ENABLE_HTML_CONTENT_DEFAULT as E, IonicSafeString as I, sanitizeDOMString as a, sanitizeDOMTree as b, getMode as g, reflectPropertiesToAttributes as r, setupConfig as s };
|
package/dist/esm/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { createGesture } from './index-CfgBF1SE.js';
|
|
|
10
10
|
export { g as getPlatforms, i as initialize, a as isPlatform } from './ionic-global--9mOmThr.js';
|
|
11
11
|
export { c as componentOnReady } from './helpers-Do7zwvM1.js';
|
|
12
12
|
export { L as LogLevel } from './index-Omi_TcwW.js';
|
|
13
|
-
export { I as IonicSafeString, g as getMode, s as setupConfig } from './config-
|
|
13
|
+
export { I as IonicSafeString, g as getMode, s as setupConfig } from './config-C5F1aoVz.js';
|
|
14
14
|
export { o as openURL } from './theme-DaJxRxSQ.js';
|
|
15
15
|
export { m as menuController } from './index-BWMvrRiE.js';
|
|
16
16
|
export { b as actionSheetController, a as alertController, l as loadingController, m as modalController, p as pickerController, c as popoverController, t as toastController } from './overlays-CpQ6Df2g.js';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, e as config, f as printIonWarning, i as forceUpdate, h, d as Host, g as getElement } from './index-Omi_TcwW.js';
|
|
5
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-
|
|
5
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-C5F1aoVz.js';
|
|
6
6
|
import { c as createButtonActiveGesture } from './button-active-B016u5N-.js';
|
|
7
7
|
import { r as raf } from './helpers-Do7zwvM1.js';
|
|
8
8
|
import { c as createLockController } from './lock-controller-B-hirT0v.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { r as registerInstance, c as createEvent, w as writeTask, a as readTask, h, d as Host, g as getElement, e as config } from './index-Omi_TcwW.js';
|
|
5
5
|
import { f as findClosestIonContent, p as printIonContentErrorMsg, g as getScrollElement } from './index-BmkLokUL.js';
|
|
6
6
|
import { c as getIonTheme } from './ionic-global--9mOmThr.js';
|
|
7
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-
|
|
7
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-C5F1aoVz.js';
|
|
8
8
|
import './helpers-Do7zwvM1.js';
|
|
9
9
|
import './focus-visible-vXpMhGrs.js';
|
|
10
10
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, e as config, h, d as Host, g as getElement } from './index-Omi_TcwW.js';
|
|
5
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-
|
|
5
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-C5F1aoVz.js';
|
|
6
6
|
import { r as raf } from './helpers-Do7zwvM1.js';
|
|
7
7
|
import { c as createLockController } from './lock-controller-B-hirT0v.js';
|
|
8
8
|
import { d as createDelegateController, e as createTriggerController, B as BACKDROP, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod } from './overlays-CpQ6Df2g.js';
|
|
@@ -8,7 +8,7 @@ import { c as componentOnReady, t as transitionEndAsync, e as clamp, g as getEle
|
|
|
8
8
|
import { d as hapticImpact, I as ImpactStyle } from './haptic-CbnKC3go.js';
|
|
9
9
|
import { b as getIonMode, c as getIonTheme } from './ionic-global--9mOmThr.js';
|
|
10
10
|
import { c as createAnimation } from './animation-Cd1EA2ar.js';
|
|
11
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-
|
|
11
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-C5F1aoVz.js';
|
|
12
12
|
import { h as arrowDown, i as caretBackSharp } from './index-D2tu5BUg.js';
|
|
13
13
|
import { S as SPINNERS } from './spinner-configs-D4RIp70E.js';
|
|
14
14
|
import './focus-visible-vXpMhGrs.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, e as config, f as printIonWarning, h, d as Host, g as getElement, i as forceUpdate } from './index-Omi_TcwW.js';
|
|
5
5
|
import { c as caretDownSvg } from './caret-down-D1t981Ih.js';
|
|
6
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, b as sanitizeDOMTree } from './config-
|
|
6
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, r as reflectPropertiesToAttributes, b as sanitizeDOMTree } from './config-C5F1aoVz.js';
|
|
7
7
|
import { c as createNotchController } from './notch-controller-klgNWpjJ.js';
|
|
8
8
|
import { i as isOptionSelected, c as compareOptions } from './compare-with-utils-sObYyvOy.js';
|
|
9
9
|
import { c as checkInvalidState } from './validity-BjW8SOqw.js';
|
|
@@ -945,7 +945,7 @@ const Select = class {
|
|
|
945
945
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
946
946
|
*/
|
|
947
947
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
948
|
-
return (h(Host, { key: '
|
|
948
|
+
return (h(Host, { key: 'ce995037cea6ce309ead79f881772c628086ab45', onClick: this.onClick, class: createColorClasses(this.color, {
|
|
949
949
|
[theme]: true,
|
|
950
950
|
'in-item': inItem,
|
|
951
951
|
'in-item-color': hostContext('ion-item.ion-color', el),
|
|
@@ -964,7 +964,7 @@ const Select = class {
|
|
|
964
964
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
965
965
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
966
966
|
[`select-size-${size}`]: size !== undefined,
|
|
967
|
-
}) }, h("label", { key: '
|
|
967
|
+
}) }, h("label", { key: '862fa9d03a7feeeac05463842898849001724cef', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick, part: "wrapper" }, this.renderLabelContainer(), h("div", { key: '1e91cc271790756a97cfc0c8caaca767ba793467', class: "select-wrapper-inner", part: "inner" },
|
|
968
968
|
/**
|
|
969
969
|
* For the ionic theme, we render the outline container here
|
|
970
970
|
* instead of higher up, so it can be positioned relative to
|
|
@@ -974,7 +974,7 @@ const Select = class {
|
|
|
974
974
|
* <label> element, ensuring that clicking the label text
|
|
975
975
|
* focuses the select.
|
|
976
976
|
*/
|
|
977
|
-
theme === 'ionic' && fill === 'outline' && h("div", { key: '
|
|
977
|
+
theme === 'ionic' && fill === 'outline' && h("div", { key: '14a2a14daa10625fa59f8f148e124a1c0c147319', class: "select-outline" }), h("slot", { key: 'a5a4372feffbeb3652e6045e64cbfacb66c58da9', name: "start" }), h("div", { key: 'b6ac054eaca720a21e0617cf413e576ef23a60e8', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: '140d299760a7c7f186b31e9e38f968534b1e116a', name: "end" }), shouldRenderInnerIcon && this.renderSelectIcon()), shouldRenderOuterIcon && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'b1a1e98302b0e87c3cd850e9abd8faf946a2b492', class: "select-highlight" })), this.renderBottomContent()));
|
|
978
978
|
}
|
|
979
979
|
get el() { return getElement(this); }
|
|
980
980
|
static get watchers() { return {
|
|
@@ -1127,6 +1127,17 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1127
1127
|
if (!slotName && nodes.every((n) => n.nodeType === Node.TEXT_NODE)) {
|
|
1128
1128
|
return nodes.map((n) => { var _a; return (_a = n.textContent) === null || _a === void 0 ? void 0 : _a.trim(); }).join(' ') || null;
|
|
1129
1129
|
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Mirror known custom-element properties (e.g. ion-icon's `icon`)
|
|
1132
|
+
* onto attributes before cloning. Frameworks like Vue set these as
|
|
1133
|
+
* DOM properties, which `cloneNode` doesn't copy, so without this
|
|
1134
|
+
* step the cloned overlay copy renders without the prop's value.
|
|
1135
|
+
*/
|
|
1136
|
+
nodes.forEach((n) => {
|
|
1137
|
+
if (n.nodeType === Node.ELEMENT_NODE) {
|
|
1138
|
+
reflectPropertiesToAttributes(n);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1130
1141
|
// Clone each node into a temporary container
|
|
1131
1142
|
const container = document.createElement('div');
|
|
1132
1143
|
nodes.forEach((n) => {
|
|
@@ -1140,9 +1151,11 @@ const getOptionContent = (option, slotName, useHTML = false) => {
|
|
|
1140
1151
|
}
|
|
1141
1152
|
container.appendChild(clone);
|
|
1142
1153
|
});
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1154
|
+
/**
|
|
1155
|
+
* Sanitize the cloned DOM in place. Trusted attributes (size, color,
|
|
1156
|
+
* shape, etc.) are preserved; event handlers, javascript: URLs, and
|
|
1157
|
+
* blocked tags are stripped.
|
|
1158
|
+
*/
|
|
1146
1159
|
sanitizeDOMTree(container);
|
|
1147
1160
|
if (useHTML) {
|
|
1148
1161
|
return container.innerHTML.trim() || null;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { f as printIonWarning, r as registerInstance, c as createEvent, e as config, j as printIonError, h, d as Host, g as getElement } from './index-Omi_TcwW.js';
|
|
5
|
-
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-
|
|
5
|
+
import { E as ENABLE_HTML_CONTENT_DEFAULT, a as sanitizeDOMString } from './config-C5F1aoVz.js';
|
|
6
6
|
import { g as getElementRoot, r as raf } from './helpers-Do7zwvM1.js';
|
|
7
7
|
import { c as createLockController } from './lock-controller-B-hirT0v.js';
|
|
8
8
|
import { O as OVERLAY_GESTURE_PRIORITY, d as createDelegateController, e as createTriggerController, i as isCancel, j as prepareOverlay, k as setOverlayId, f as present, g as dismiss, h as eventMethod, s as safeCall, G as GESTURE } from './overlays-CpQ6Df2g.js';
|
package/dist/ionic/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
export{c as createAnimation}from"./p-DDFhx1YX.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./p-E1wtzfO0.js";export{iosTransitionAnimation}from"./p-BYAxJDlE.js";export{mdTransitionAnimation}from"./p-BQgCYKKG.js";export{g as getTimeGivenProgression}from"./p-hHmYLOfE.js";export{createGesture}from"./p-Cl0B-RWe.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./p-DaknVxZR.js";export{c as componentOnReady}from"./p-CHE1xWbg.js";export{L as LogLevel}from"./p-Omi_TcwW.js";export{I as IonicSafeString,g as getMode,s as setupConfig}from"./p-
|
|
4
|
+
export{c as createAnimation}from"./p-DDFhx1YX.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./p-E1wtzfO0.js";export{iosTransitionAnimation}from"./p-BYAxJDlE.js";export{mdTransitionAnimation}from"./p-BQgCYKKG.js";export{g as getTimeGivenProgression}from"./p-hHmYLOfE.js";export{createGesture}from"./p-Cl0B-RWe.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./p-DaknVxZR.js";export{c as componentOnReady}from"./p-CHE1xWbg.js";export{L as LogLevel}from"./p-Omi_TcwW.js";export{I as IonicSafeString,g as getMode,s as setupConfig}from"./p-pI5qh5HW.js";export{o as openURL}from"./p-DaJxRxSQ.js";export{m as menuController}from"./p-CK179dBb.js";export{b as actionSheetController,a as alertController,l as loadingController,m as modalController,p as pickerController,c as popoverController,t as toastController}from"./p-BFBCtvFI.js";import"./p-BTEOs1at.js";import"./p-vXpMhGrs.js";import"./p-FvDKM4Ax.js";const e=e=>{const{swiper:o,extendParams:t}=e,s={effect:void 0,direction:"horizontal",initialSlide:0,loop:!1,parallax:!1,slidesPerView:1,spaceBetween:0,speed:300,slidesPerColumn:1,slidesPerColumnFill:"column",slidesPerGroup:1,centeredSlides:!1,slidesOffsetBefore:0,slidesOffsetAfter:0,touchEventsTarget:"container",freeMode:!1,freeModeMomentum:!0,freeModeMomentumRatio:1,freeModeMomentumBounce:!0,freeModeMomentumBounceRatio:1,freeModeMomentumVelocityRatio:1,freeModeSticky:!1,freeModeMinimumVelocity:.02,autoHeight:!1,setWrapperSize:!1,zoom:{maxRatio:3,minRatio:1,toggle:!1},touchRatio:1,touchAngle:45,simulateTouch:!0,touchStartPreventDefault:!1,shortSwipes:!0,longSwipes:!0,longSwipesRatio:.5,longSwipesMs:300,followFinger:!0,threshold:0,touchMoveStopPropagation:!0,touchReleaseOnEdges:!1,iOSEdgeSwipeDetection:!1,iOSEdgeSwipeThreshold:20,resistance:!0,resistanceRatio:.85,watchSlidesProgress:!1,watchSlidesVisibility:!1,preventClicks:!0,preventClicksPropagation:!0,slideToClickedSlide:!1,loopAdditionalSlides:0,noSwiping:!0,runCallbacksOnInit:!0,coverflowEffect:{rotate:50,stretch:0,depth:100,modifier:1,slideShadows:!0},flipEffect:{slideShadows:!0,limitRotation:!0},cubeEffect:{slideShadows:!0,shadow:!0,shadowOffset:20,shadowScale:.94},fadeEffect:{crossFade:!1},a11y:{prevSlideMessage:"Previous slide",nextSlideMessage:"Next slide",firstSlideMessage:"This is the first slide",lastSlideMessage:"This is the last slide"}};o.pagination&&(s.pagination={type:"bullets",clickable:!1,hideOnClick:!1}),o.scrollbar&&(s.scrollbar={hide:!0}),t(s)};export{e as IonicSlides}
|