storybook-addon-pseudo-states 1.14.0 → 1.15.1
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/CHANGELOG.md +39 -0
- package/README.md +24 -1
- package/dist/cjs/PseudoStateTool.js +39 -68
- package/dist/cjs/constants.js +3 -3
- package/dist/cjs/preset/manager.js +5 -3
- package/dist/cjs/preset/preview.js +1 -1
- package/dist/cjs/rewriteStyleSheet.js +87 -0
- package/dist/cjs/rewriteStyleSheet.test.js +70 -0
- package/dist/cjs/splitSelectors.js +8 -10
- package/dist/cjs/splitSelectors.test.js +4 -4
- package/dist/cjs/withPseudoState.js +67 -196
- package/dist/esm/PseudoStateTool.js +36 -63
- package/dist/esm/constants.js +3 -3
- package/dist/esm/preset/manager.js +5 -3
- package/dist/esm/preset/preview.js +1 -1
- package/dist/esm/rewriteStyleSheet.js +76 -0
- package/dist/esm/rewriteStyleSheet.test.js +68 -0
- package/dist/esm/splitSelectors.js +8 -10
- package/dist/esm/splitSelectors.test.js +4 -4
- package/dist/esm/withPseudoState.js +65 -192
- package/package.json +10 -10
|
@@ -11,102 +11,74 @@ var _coreEvents = require("@storybook/core-events");
|
|
|
11
11
|
|
|
12
12
|
var _constants = require("./constants");
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _rewriteStyleSheet = require("./rewriteStyleSheet");
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
/* eslint-env browser */
|
|
17
|
+
const channel = _addons.addons.getChannel();
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
21
|
-
|
|
22
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
23
|
-
|
|
24
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
25
|
-
|
|
26
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
27
|
-
|
|
28
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
29
|
-
|
|
30
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
31
|
-
|
|
32
|
-
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
33
|
-
|
|
34
|
-
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
35
|
-
|
|
36
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
37
|
-
|
|
38
|
-
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
39
|
-
|
|
40
|
-
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
41
|
-
|
|
42
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
43
|
-
|
|
44
|
-
var pseudoStates = Object.values(_constants.PSEUDO_STATES);
|
|
45
|
-
var matchOne = new RegExp(":(".concat(pseudoStates.join("|"), ")"));
|
|
46
|
-
var matchAll = new RegExp(":(".concat(pseudoStates.join("|"), ")"), "g"); // Drops any existing pseudo state classnames that carried over from a previously viewed story
|
|
19
|
+
const shadowHosts = new Set(); // Drops any existing pseudo state classnames that carried over from a previously viewed story
|
|
47
20
|
// before adding the new classnames. We do this the old-fashioned way, for IE compatibility.
|
|
48
21
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
element.className = (_element$className$sp = element.className.split(" ").filter(function (classname) {
|
|
53
|
-
return classname && classname.indexOf("pseudo-") !== 0;
|
|
54
|
-
})).concat.apply(_element$className$sp, _toConsumableArray(classnames)).join(" ");
|
|
22
|
+
const applyClasses = (element, classnames) => {
|
|
23
|
+
element.className = element.className.split(" ").filter(classname => classname && classname.indexOf("pseudo-") !== 0).concat(...classnames).join(" ");
|
|
55
24
|
};
|
|
56
25
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
value = _ref2[1];
|
|
26
|
+
const applyParameter = (rootElement, parameter) => {
|
|
27
|
+
const map = new Map([[rootElement, new Set()]]);
|
|
28
|
+
|
|
29
|
+
const add = (target, state) => map.set(target, new Set([...(map.get(target) || []), state]));
|
|
62
30
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
var _ref4 = _slicedToArray(_ref3, 1),
|
|
66
|
-
key = _ref4[0];
|
|
31
|
+
Object.entries(parameter || {}).forEach(_ref => {
|
|
32
|
+
let [state, value] = _ref;
|
|
67
33
|
|
|
68
|
-
|
|
69
|
-
|
|
34
|
+
if (typeof value === "boolean") {
|
|
35
|
+
// default API - applying pseudo class to root element.
|
|
36
|
+
add(rootElement, value && state);
|
|
37
|
+
} else if (typeof value === "string") {
|
|
38
|
+
// explicit selectors API - applying pseudo class to a specific element
|
|
39
|
+
rootElement.querySelectorAll(value).forEach(el => add(el, state));
|
|
40
|
+
} else if (Array.isArray(value)) {
|
|
41
|
+
// explicit selectors API - we have an array (of strings) recursively handle each one
|
|
42
|
+
value.forEach(sel => rootElement.querySelectorAll(sel).forEach(el => add(el, state)));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
map.forEach((states, target) => {
|
|
46
|
+
const classnames = [];
|
|
47
|
+
states.forEach(key => _constants.PSEUDO_STATES[key] && classnames.push("pseudo-".concat(_constants.PSEUDO_STATES[key])));
|
|
48
|
+
applyClasses(target, classnames);
|
|
49
|
+
});
|
|
70
50
|
}; // Traverses ancestry to collect relevant pseudo classnames, and applies them to the shadow host.
|
|
71
51
|
// Shadow DOM can only access classes on its host. Traversing is needed to mimic the CSS cascade.
|
|
72
52
|
|
|
73
53
|
|
|
74
|
-
|
|
75
|
-
|
|
54
|
+
const updateShadowHost = shadowHost => {
|
|
55
|
+
const classnames = new Set();
|
|
76
56
|
|
|
77
|
-
for (
|
|
57
|
+
for (let element = shadowHost.parentElement; element; element = element.parentElement) {
|
|
78
58
|
if (!element.className) continue;
|
|
79
|
-
element.className.split(" ").filter(
|
|
80
|
-
return classname.indexOf("pseudo-") === 0;
|
|
81
|
-
}).forEach(function (classname) {
|
|
82
|
-
return classnames.add(classname);
|
|
83
|
-
});
|
|
59
|
+
element.className.split(" ").filter(classname => classname.indexOf("pseudo-") === 0).forEach(classname => classnames.add(classname));
|
|
84
60
|
}
|
|
85
61
|
|
|
86
62
|
applyClasses(shadowHost, classnames);
|
|
87
|
-
}; //
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
var globals = globalsArgs.pseudo;
|
|
104
|
-
|
|
105
|
-
var channel = _addons.addons.getChannel(); // Sync parameter to globals, used by the toolbar (only in canvas as this
|
|
63
|
+
}; // Global decorator that rewrites stylesheets and applies classnames to render pseudo styles
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
const withPseudoState = (StoryFn, _ref2) => {
|
|
67
|
+
let {
|
|
68
|
+
viewMode,
|
|
69
|
+
parameters,
|
|
70
|
+
id,
|
|
71
|
+
globals: globalsArgs
|
|
72
|
+
} = _ref2;
|
|
73
|
+
const {
|
|
74
|
+
pseudo: parameter
|
|
75
|
+
} = parameters;
|
|
76
|
+
const {
|
|
77
|
+
pseudo: globals
|
|
78
|
+
} = globalsArgs; // Sync parameter to globals, used by the toolbar (only in canvas as this
|
|
106
79
|
// doesn't make sense for docs because many stories are displayed at once)
|
|
107
80
|
|
|
108
|
-
|
|
109
|
-
(0, _addons.useEffect)(function () {
|
|
81
|
+
(0, _addons.useEffect)(() => {
|
|
110
82
|
if (parameter !== globals && viewMode === "story") {
|
|
111
83
|
channel.emit(_coreEvents.UPDATE_GLOBALS, {
|
|
112
84
|
globals: {
|
|
@@ -117,135 +89,34 @@ var withPseudoState = function withPseudoState(StoryFn, _ref5) {
|
|
|
117
89
|
}, [parameter, viewMode]); // Convert selected states to classnames and apply them to the story root element.
|
|
118
90
|
// Then update each shadow host to redetermine its own pseudo classnames.
|
|
119
91
|
|
|
120
|
-
(0, _addons.useEffect)(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
applyParameter(element, parameter);
|
|
92
|
+
(0, _addons.useEffect)(() => {
|
|
93
|
+
const timeout = setTimeout(() => {
|
|
94
|
+
const element = document.getElementById(viewMode === "docs" ? "story--".concat(id) : "root");
|
|
95
|
+
applyParameter(element, globals || parameter);
|
|
124
96
|
shadowHosts.forEach(updateShadowHost);
|
|
125
97
|
}, 0);
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
};
|
|
129
|
-
}, [parameter, viewMode]);
|
|
98
|
+
return () => clearTimeout(timeout);
|
|
99
|
+
}, [globals, parameter, viewMode]);
|
|
130
100
|
return StoryFn();
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
exports.withPseudoState = withPseudoState;
|
|
134
|
-
var warnings = new Set();
|
|
135
|
-
|
|
136
|
-
var warnOnce = function warnOnce(message) {
|
|
137
|
-
if (warnings.has(message)) return; // eslint-disable-next-line no-console
|
|
138
|
-
|
|
139
|
-
console.warn(message);
|
|
140
|
-
warnings.add(message);
|
|
141
101
|
}; // Rewrite CSS rules for pseudo-states on all stylesheets to add an alternative selector
|
|
142
102
|
|
|
143
103
|
|
|
144
|
-
|
|
104
|
+
exports.withPseudoState = withPseudoState;
|
|
105
|
+
|
|
106
|
+
const rewriteStyleSheets = shadowRoot => {
|
|
145
107
|
var _shadowRoot$adoptedSt;
|
|
146
108
|
|
|
147
|
-
|
|
109
|
+
let styleSheets = shadowRoot ? shadowRoot.styleSheets : document.styleSheets;
|
|
148
110
|
if (shadowRoot !== null && shadowRoot !== void 0 && (_shadowRoot$adoptedSt = shadowRoot.adoptedStyleSheets) !== null && _shadowRoot$adoptedSt !== void 0 && _shadowRoot$adoptedSt.length) styleSheets = shadowRoot.adoptedStyleSheets;
|
|
111
|
+
Array.from(styleSheets).forEach(sheet => (0, _rewriteStyleSheet.rewriteStyleSheet)(sheet, shadowRoot, shadowHosts));
|
|
112
|
+
}; // Only track shadow hosts for the current story
|
|
149
113
|
|
|
150
|
-
var _iterator = _createForOfIteratorHelper(styleSheets),
|
|
151
|
-
_step;
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
155
|
-
var sheet = _step.value;
|
|
156
|
-
|
|
157
|
-
if (sheet._pseudoStatesRewritten) {
|
|
158
|
-
continue;
|
|
159
|
-
} else {
|
|
160
|
-
sheet._pseudoStatesRewritten = true;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
var index = 0;
|
|
165
|
-
|
|
166
|
-
var _iterator2 = _createForOfIteratorHelper(sheet.cssRules),
|
|
167
|
-
_step2;
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
171
|
-
var _step2$value = _step2.value,
|
|
172
|
-
cssText = _step2$value.cssText,
|
|
173
|
-
selectorText = _step2$value.selectorText;
|
|
174
|
-
|
|
175
|
-
if (matchOne.test(selectorText)) {
|
|
176
|
-
var selectors = (0, _splitSelectors.splitSelectors)(selectorText);
|
|
177
|
-
var newRule = cssText.replace(selectorText, selectors.flatMap(function (selector) {
|
|
178
|
-
if (selector.includes(".pseudo-")) return [];
|
|
179
|
-
var states = [];
|
|
180
|
-
var plainSelector = selector.replace(matchAll, function (_, state) {
|
|
181
|
-
states.push(state);
|
|
182
|
-
return "";
|
|
183
|
-
});
|
|
184
|
-
var stateSelector;
|
|
185
|
-
|
|
186
|
-
if (!matchOne.test(selector)) {
|
|
187
|
-
return [selector];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (selector.startsWith(":host(") || selector.startsWith("::slotted(")) {
|
|
191
|
-
stateSelector = states.reduce(function (acc, state) {
|
|
192
|
-
return acc.replaceAll(":".concat(state), ".pseudo-".concat(state));
|
|
193
|
-
}, selector);
|
|
194
|
-
} else if (shadowRoot) {
|
|
195
|
-
stateSelector = ":host(".concat(states.map(function (s) {
|
|
196
|
-
return ".pseudo-".concat(s);
|
|
197
|
-
}).join(""), ") ").concat(plainSelector);
|
|
198
|
-
} else {
|
|
199
|
-
stateSelector = "".concat(states.map(function (s) {
|
|
200
|
-
return ".pseudo-".concat(s);
|
|
201
|
-
}).join(""), " ").concat(plainSelector);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return [selector, stateSelector];
|
|
205
|
-
}).join(", "));
|
|
206
|
-
sheet.deleteRule(index);
|
|
207
|
-
sheet.insertRule(newRule, index);
|
|
208
|
-
if (shadowRoot) shadowHosts.add(shadowRoot.host);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
index++;
|
|
212
|
-
|
|
213
|
-
if (index > 1000) {
|
|
214
|
-
warnOnce("Reached maximum of 1000 pseudo selectors per sheet, skipping the rest.");
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
} catch (err) {
|
|
219
|
-
_iterator2.e(err);
|
|
220
|
-
} finally {
|
|
221
|
-
_iterator2.f();
|
|
222
|
-
}
|
|
223
|
-
} catch (e) {
|
|
224
|
-
if (e.toString().includes("cssRules")) {
|
|
225
|
-
warnOnce("Can't access cssRules, likely due to CORS restrictions: ".concat(sheet.href));
|
|
226
|
-
} else {
|
|
227
|
-
// eslint-disable-next-line no-console
|
|
228
|
-
console.error(e, sheet.href);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} catch (err) {
|
|
233
|
-
_iterator.e(err);
|
|
234
|
-
} finally {
|
|
235
|
-
_iterator.f();
|
|
236
|
-
}
|
|
237
|
-
} // Reinitialize CSS enhancements every time the story changes
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
_addons.addons.getChannel().on(_coreEvents.STORY_RENDERED, function () {
|
|
241
|
-
return rewriteStyleSheets();
|
|
242
|
-
}); // Reinitialize CSS enhancements every time a docs page is rendered
|
|
243
114
|
|
|
115
|
+
channel.on(_coreEvents.STORY_CHANGED, () => shadowHosts.clear()); // Reinitialize CSS enhancements every time the story changes
|
|
244
116
|
|
|
245
|
-
|
|
246
|
-
return rewriteStyleSheets();
|
|
247
|
-
}); // IE doesn't support shadow DOM
|
|
117
|
+
channel.on(_coreEvents.STORY_RENDERED, () => rewriteStyleSheets()); // Reinitialize CSS enhancements every time a docs page is rendered
|
|
248
118
|
|
|
119
|
+
channel.on(_coreEvents.DOCS_RENDERED, () => rewriteStyleSheets()); // IE doesn't support shadow DOM
|
|
249
120
|
|
|
250
121
|
if (Element.prototype.attachShadow) {
|
|
251
122
|
// Monkeypatch the attachShadow method so we can handle pseudo styles inside shadow DOM
|
|
@@ -253,12 +124,12 @@ if (Element.prototype.attachShadow) {
|
|
|
253
124
|
|
|
254
125
|
Element.prototype.attachShadow = function attachShadow(init) {
|
|
255
126
|
// Force "open" mode, so we can access the shadowRoot
|
|
256
|
-
|
|
127
|
+
const shadowRoot = this._attachShadow({ ...init,
|
|
257
128
|
mode: "open"
|
|
258
|
-
})
|
|
129
|
+
}); // Wait for it to render and apply its styles before rewriting them
|
|
259
130
|
|
|
260
131
|
|
|
261
|
-
requestAnimationFrame(
|
|
132
|
+
requestAnimationFrame(() => {
|
|
262
133
|
rewriteStyleSheets(shadowRoot);
|
|
263
134
|
updateShadowHost(shadowRoot.host);
|
|
264
135
|
});
|
|
@@ -1,34 +1,20 @@
|
|
|
1
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
-
|
|
3
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
-
|
|
5
|
-
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
-
|
|
7
|
-
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
8
|
-
|
|
9
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
10
|
-
|
|
11
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
12
|
-
|
|
13
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
14
|
-
|
|
15
|
-
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
16
|
-
|
|
17
|
-
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
18
|
-
|
|
19
1
|
import React, { useCallback, useMemo } from "react";
|
|
20
2
|
import { useGlobals } from "@storybook/api";
|
|
21
3
|
import { Icons, IconButton, WithTooltip, TooltipLinkList } from "@storybook/components";
|
|
22
4
|
import { styled, color } from "@storybook/theming";
|
|
23
5
|
import { PSEUDO_STATES } from "./constants";
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
const LinkTitle = styled.span(_ref => {
|
|
7
|
+
let {
|
|
8
|
+
active
|
|
9
|
+
} = _ref;
|
|
26
10
|
return {
|
|
27
11
|
color: active ? color.secondary : "inherit"
|
|
28
12
|
};
|
|
29
13
|
});
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
const LinkIcon = styled(Icons)(_ref2 => {
|
|
15
|
+
let {
|
|
16
|
+
active
|
|
17
|
+
} = _ref2;
|
|
32
18
|
return {
|
|
33
19
|
opacity: active ? 1 : 0,
|
|
34
20
|
path: {
|
|
@@ -36,53 +22,40 @@ var LinkIcon = styled(Icons)(function (_ref2) {
|
|
|
36
22
|
}
|
|
37
23
|
};
|
|
38
24
|
});
|
|
39
|
-
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return pseudo ? pseudo[option] : false;
|
|
51
|
-
}, [pseudo]);
|
|
52
|
-
var toggleOption = useCallback(function (option) {
|
|
53
|
-
return function () {
|
|
54
|
-
return updateGlobals({
|
|
55
|
-
pseudo: _objectSpread(_objectSpread({}, pseudo), {}, _defineProperty({}, option, !getValue(option)))
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
}, [pseudo, getValue]);
|
|
25
|
+
const options = Object.keys(PSEUDO_STATES).sort();
|
|
26
|
+
export const PseudoStateTool = () => {
|
|
27
|
+
const [{
|
|
28
|
+
pseudo
|
|
29
|
+
}, updateGlobals] = useGlobals();
|
|
30
|
+
const isActive = useCallback(option => (pseudo === null || pseudo === void 0 ? void 0 : pseudo[option]) === true, [pseudo]);
|
|
31
|
+
const toggleOption = useCallback(option => () => updateGlobals({
|
|
32
|
+
pseudo: { ...pseudo,
|
|
33
|
+
[option]: !isActive(option)
|
|
34
|
+
}
|
|
35
|
+
}), [pseudo]);
|
|
59
36
|
return /*#__PURE__*/React.createElement(WithTooltip, {
|
|
60
37
|
placement: "top",
|
|
61
38
|
trigger: "click",
|
|
62
|
-
tooltip:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
})
|
|
80
|
-
});
|
|
81
|
-
}
|
|
39
|
+
tooltip: () => /*#__PURE__*/React.createElement(TooltipLinkList, {
|
|
40
|
+
links: options.map(option => ({
|
|
41
|
+
id: option,
|
|
42
|
+
title: /*#__PURE__*/React.createElement(LinkTitle, {
|
|
43
|
+
active: isActive(option)
|
|
44
|
+
}, ":", PSEUDO_STATES[option]),
|
|
45
|
+
right: /*#__PURE__*/React.createElement(LinkIcon, {
|
|
46
|
+
icon: "check",
|
|
47
|
+
width: 12,
|
|
48
|
+
height: 12,
|
|
49
|
+
active: isActive(option)
|
|
50
|
+
}),
|
|
51
|
+
onClick: toggleOption(option),
|
|
52
|
+
active: isActive(option)
|
|
53
|
+
}))
|
|
54
|
+
})
|
|
82
55
|
}, /*#__PURE__*/React.createElement(IconButton, {
|
|
83
56
|
key: "pseudo-state",
|
|
84
57
|
title: "Select CSS pseudo states",
|
|
85
|
-
active:
|
|
58
|
+
active: options.some(isActive)
|
|
86
59
|
}, /*#__PURE__*/React.createElement(Icons, {
|
|
87
60
|
icon: "button"
|
|
88
61
|
})));
|
package/dist/esm/constants.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export const ADDON_ID = "storybook/pseudo-states";
|
|
2
|
+
export const TOOL_ID = "".concat(ADDON_ID, "/tool"); // Dynamic pseudo-classes
|
|
3
3
|
// @see https://www.w3.org/TR/2018/REC-selectors-3-20181106/#dynamic-pseudos
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export const PSEUDO_STATES = {
|
|
6
6
|
hover: "hover",
|
|
7
7
|
active: "active",
|
|
8
8
|
focusVisible: "focus-visible",
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { addons, types } from "@storybook/addons";
|
|
2
2
|
import { ADDON_ID, TOOL_ID } from "../constants";
|
|
3
3
|
import { PseudoStateTool } from "../PseudoStateTool";
|
|
4
|
-
addons.register(ADDON_ID,
|
|
4
|
+
addons.register(ADDON_ID, () => {
|
|
5
5
|
addons.add(TOOL_ID, {
|
|
6
6
|
type: types.TOOL,
|
|
7
7
|
title: "CSS pseudo states",
|
|
8
|
-
match:
|
|
9
|
-
|
|
8
|
+
match: _ref => {
|
|
9
|
+
let {
|
|
10
|
+
viewMode
|
|
11
|
+
} = _ref;
|
|
10
12
|
return viewMode === "story";
|
|
11
13
|
},
|
|
12
14
|
render: PseudoStateTool
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { withPseudoState } from "../withPseudoState";
|
|
2
|
-
export
|
|
2
|
+
export const decorators = [withPseudoState];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { PSEUDO_STATES } from "./constants";
|
|
2
|
+
import { splitSelectors } from "./splitSelectors";
|
|
3
|
+
const pseudoStates = Object.values(PSEUDO_STATES);
|
|
4
|
+
const matchOne = new RegExp(":(".concat(pseudoStates.join("|"), ")"));
|
|
5
|
+
const matchAll = new RegExp(":(".concat(pseudoStates.join("|"), ")"), "g");
|
|
6
|
+
const warnings = new Set();
|
|
7
|
+
|
|
8
|
+
const warnOnce = message => {
|
|
9
|
+
if (warnings.has(message)) return; // eslint-disable-next-line no-console
|
|
10
|
+
|
|
11
|
+
console.warn(message);
|
|
12
|
+
warnings.add(message);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const rewriteRule = (cssText, selectorText, shadowRoot) => {
|
|
16
|
+
return cssText.replace(selectorText, splitSelectors(selectorText).flatMap(selector => {
|
|
17
|
+
if (selector.includes(".pseudo-")) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!matchOne.test(selector)) {
|
|
22
|
+
return [selector];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const states = [];
|
|
26
|
+
const plainSelector = selector.replace(matchAll, (_, state) => {
|
|
27
|
+
states.push(state);
|
|
28
|
+
return "";
|
|
29
|
+
});
|
|
30
|
+
const classSelector = states.reduce((acc, state) => acc.replace(new RegExp(":".concat(state), "g"), ".pseudo-".concat(state)), selector);
|
|
31
|
+
|
|
32
|
+
if (selector.startsWith(":host(") || selector.startsWith("::slotted(")) {
|
|
33
|
+
return [selector, classSelector];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ancestorSelector = shadowRoot ? ":host(".concat(states.map(s => ".pseudo-".concat(s)).join(""), ") ").concat(plainSelector) : "".concat(states.map(s => ".pseudo-".concat(s)).join(""), " ").concat(plainSelector);
|
|
37
|
+
return [selector, classSelector, ancestorSelector].filter(selector => !selector.includes(":not()"));
|
|
38
|
+
}).join(", "));
|
|
39
|
+
}; // Rewrites the style sheet to add alternative selectors for any rule that targets a pseudo state.
|
|
40
|
+
// A sheet can only be rewritten once, and may carry over between stories.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
export const rewriteStyleSheet = (sheet, shadowRoot, shadowHosts) => {
|
|
44
|
+
if (sheet.__pseudoStatesRewritten) return;
|
|
45
|
+
sheet.__pseudoStatesRewritten = true;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
let index = 0;
|
|
49
|
+
|
|
50
|
+
for (const {
|
|
51
|
+
cssText,
|
|
52
|
+
selectorText
|
|
53
|
+
} of sheet.cssRules) {
|
|
54
|
+
if (matchOne.test(selectorText)) {
|
|
55
|
+
const newRule = rewriteRule(cssText, selectorText, shadowRoot);
|
|
56
|
+
sheet.deleteRule(index);
|
|
57
|
+
sheet.insertRule(newRule, index);
|
|
58
|
+
if (shadowRoot) shadowHosts.add(shadowRoot.host);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
index++;
|
|
62
|
+
|
|
63
|
+
if (index > 1000) {
|
|
64
|
+
warnOnce("Reached maximum of 1000 pseudo selectors per sheet, skipping the rest.");
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (e.toString().includes("cssRules")) {
|
|
70
|
+
warnOnce("Can't access cssRules, likely due to CORS restrictions: ".concat(sheet.href));
|
|
71
|
+
} else {
|
|
72
|
+
// eslint-disable-next-line no-console
|
|
73
|
+
console.error(e, sheet.href);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { rewriteStyleSheet } from "./rewriteStyleSheet";
|
|
2
|
+
|
|
3
|
+
class Sheet {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.__pseudoStatesRewritten = false;
|
|
6
|
+
|
|
7
|
+
for (var _len = arguments.length, rules = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
8
|
+
rules[_key] = arguments[_key];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.cssRules = rules.map(cssText => ({
|
|
12
|
+
cssText,
|
|
13
|
+
selectorText: cssText.slice(0, cssText.indexOf(" {"))
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
deleteRule(index) {
|
|
18
|
+
this.cssRules.splice(index, 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
insertRule(cssText, index) {
|
|
22
|
+
this.cssRules.splice(index, 0, cssText);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("rewriteStyleSheet", () => {
|
|
28
|
+
it("adds alternative selector targeting the element directly", () => {
|
|
29
|
+
const sheet = new Sheet("a:hover { color: red }");
|
|
30
|
+
rewriteStyleSheet(sheet);
|
|
31
|
+
expect(sheet.cssRules[0]).toContain("a.pseudo-hover");
|
|
32
|
+
});
|
|
33
|
+
it("adds alternative selector targeting an ancestor", () => {
|
|
34
|
+
const sheet = new Sheet("a:hover { color: red }");
|
|
35
|
+
rewriteStyleSheet(sheet);
|
|
36
|
+
expect(sheet.cssRules[0]).toContain(".pseudo-hover a");
|
|
37
|
+
});
|
|
38
|
+
it("adds alternative selector for each pseudo selector", () => {
|
|
39
|
+
const sheet = new Sheet("a:hover, a:focus { color: red }");
|
|
40
|
+
rewriteStyleSheet(sheet);
|
|
41
|
+
expect(sheet.cssRules[0]).toContain("a.pseudo-hover");
|
|
42
|
+
expect(sheet.cssRules[0]).toContain("a.pseudo-focus");
|
|
43
|
+
expect(sheet.cssRules[0]).toContain(".pseudo-hover a");
|
|
44
|
+
expect(sheet.cssRules[0]).toContain(".pseudo-focus a");
|
|
45
|
+
});
|
|
46
|
+
it("keeps non-pseudo selectors as-is", () => {
|
|
47
|
+
const sheet = new Sheet("a.class, a:hover, a:focus, a#id { color: red }");
|
|
48
|
+
rewriteStyleSheet(sheet);
|
|
49
|
+
expect(sheet.cssRules[0]).toContain("a.class");
|
|
50
|
+
expect(sheet.cssRules[0]).toContain("a#id");
|
|
51
|
+
});
|
|
52
|
+
it("supports combined pseudo selectors", () => {
|
|
53
|
+
const sheet = new Sheet("a:hover:focus { color: red }");
|
|
54
|
+
rewriteStyleSheet(sheet);
|
|
55
|
+
expect(sheet.cssRules[0]).toContain("a.pseudo-hover.pseudo-focus");
|
|
56
|
+
expect(sheet.cssRules[0]).toContain(".pseudo-hover.pseudo-focus a");
|
|
57
|
+
});
|
|
58
|
+
it('supports ":host"', () => {
|
|
59
|
+
const sheet = new Sheet(":host(:hover) { color: red }");
|
|
60
|
+
rewriteStyleSheet(sheet);
|
|
61
|
+
expect(sheet.cssRules[0]).toEqual(":host(:hover), :host(.pseudo-hover) { color: red }");
|
|
62
|
+
});
|
|
63
|
+
it('supports ":not"', () => {
|
|
64
|
+
const sheet = new Sheet(":not(:hover) { color: red }");
|
|
65
|
+
rewriteStyleSheet(sheet);
|
|
66
|
+
expect(sheet.cssRules[0]).toEqual(":not(:hover), :not(.pseudo-hover) { color: red }");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
return selector.indexOf("@") === 0;
|
|
3
|
-
};
|
|
1
|
+
const isAtRule = selector => selector.indexOf("@") === 0;
|
|
4
2
|
|
|
5
|
-
export
|
|
3
|
+
export const splitSelectors = selectors => {
|
|
6
4
|
if (isAtRule(selectors)) return [selectors];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
let result = [];
|
|
6
|
+
let parentheses = 0;
|
|
7
|
+
let brackets = 0;
|
|
8
|
+
let selector = "";
|
|
11
9
|
|
|
12
|
-
for (
|
|
13
|
-
|
|
10
|
+
for (let i = 0, len = selectors.length; i < len; i++) {
|
|
11
|
+
const char = selectors[i];
|
|
14
12
|
|
|
15
13
|
if (char === "(") {
|
|
16
14
|
parentheses += 1;
|