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.
@@ -11,102 +11,74 @@ var _coreEvents = require("@storybook/core-events");
11
11
 
12
12
  var _constants = require("./constants");
13
13
 
14
- var _splitSelectors = require("./splitSelectors");
14
+ var _rewriteStyleSheet = require("./rewriteStyleSheet");
15
15
 
16
- 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; }
16
+ /* eslint-env browser */
17
+ const channel = _addons.addons.getChannel();
17
18
 
18
- 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; }
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
- var applyClasses = function applyClasses(element, classnames) {
50
- var _element$className$sp;
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
- var applyParameter = function applyParameter(element, parameter) {
58
- return applyClasses(element, Object.entries(parameter || {}).filter(function (_ref) {
59
- var _ref2 = _slicedToArray(_ref, 2),
60
- _ = _ref2[0],
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
- return value;
64
- }).map(function (_ref3) {
65
- var _ref4 = _slicedToArray(_ref3, 1),
66
- key = _ref4[0];
31
+ Object.entries(parameter || {}).forEach(_ref => {
32
+ let [state, value] = _ref;
67
33
 
68
- return "pseudo-".concat(_constants.PSEUDO_STATES[key]);
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
- var updateShadowHost = function updateShadowHost(shadowHost) {
75
- var classnames = new Set();
54
+ const updateShadowHost = shadowHost => {
55
+ const classnames = new Set();
76
56
 
77
- for (var element = shadowHost.parentElement; element; element = element.parentElement) {
57
+ for (let element = shadowHost.parentElement; element; element = element.parentElement) {
78
58
  if (!element.className) continue;
79
- element.className.split(" ").filter(function (classname) {
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
- }; // Keep track of attached shadow host elements for the current story
88
-
89
-
90
- var shadowHosts = new Set();
91
-
92
- _addons.addons.getChannel().on(_coreEvents.STORY_CHANGED, function () {
93
- return shadowHosts.clear();
94
- }); // Global decorator that rewrites stylesheets and applies classnames to render pseudo styles
95
-
96
-
97
- var withPseudoState = function withPseudoState(StoryFn, _ref5) {
98
- var viewMode = _ref5.viewMode,
99
- parameters = _ref5.parameters,
100
- id = _ref5.id,
101
- globalsArgs = _ref5.globals;
102
- var parameter = parameters.pseudo;
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)(function () {
121
- var timeout = setTimeout(function () {
122
- var element = document.getElementById(viewMode === "docs" ? "story--".concat(id) : "root");
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 function () {
127
- return clearTimeout(timeout);
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
- function rewriteStyleSheets(shadowRoot) {
104
+ exports.withPseudoState = withPseudoState;
105
+
106
+ const rewriteStyleSheets = shadowRoot => {
145
107
  var _shadowRoot$adoptedSt;
146
108
 
147
- var styleSheets = shadowRoot ? shadowRoot.styleSheets : document.styleSheets;
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
- _addons.addons.getChannel().on(_coreEvents.DOCS_RENDERED, function () {
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
- var shadowRoot = this._attachShadow(_objectSpread(_objectSpread({}, init), {}, {
127
+ const shadowRoot = this._attachShadow({ ...init,
257
128
  mode: "open"
258
- })); // Wait for it to render and apply its styles before rewriting them
129
+ }); // Wait for it to render and apply its styles before rewriting them
259
130
 
260
131
 
261
- requestAnimationFrame(function () {
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
- var LinkTitle = styled.span(function (_ref) {
25
- var active = _ref.active;
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
- var LinkIcon = styled(Icons)(function (_ref2) {
31
- var active = _ref2.active;
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
- var options = Object.keys(PSEUDO_STATES).sort();
40
- export var PseudoStateTool = function PseudoStateTool() {
41
- var _useGlobals = useGlobals(),
42
- _useGlobals2 = _slicedToArray(_useGlobals, 2),
43
- pseudo = _useGlobals2[0].pseudo,
44
- updateGlobals = _useGlobals2[1];
45
-
46
- var hasSelection = useMemo(function () {
47
- return !!pseudo && Object.values(pseudo).includes(true);
48
- }, [pseudo]);
49
- var getValue = useCallback(function (option) {
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: function tooltip() {
63
- return /*#__PURE__*/React.createElement(TooltipLinkList, {
64
- links: options.map(function (option) {
65
- return {
66
- id: option,
67
- title: /*#__PURE__*/React.createElement(LinkTitle, {
68
- active: getValue(option)
69
- }, ":", PSEUDO_STATES[option]),
70
- right: /*#__PURE__*/React.createElement(LinkIcon, {
71
- icon: "check",
72
- width: 12,
73
- height: 12,
74
- active: getValue(option)
75
- }),
76
- onClick: toggleOption(option),
77
- active: getValue(option)
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: hasSelection
58
+ active: options.some(isActive)
86
59
  }, /*#__PURE__*/React.createElement(Icons, {
87
60
  icon: "button"
88
61
  })));
@@ -1,8 +1,8 @@
1
- export var ADDON_ID = "storybook/pseudo-states";
2
- export var TOOL_ID = "".concat(ADDON_ID, "/tool"); // Dynamic pseudo-classes
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 var PSEUDO_STATES = {
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, function () {
4
+ addons.register(ADDON_ID, () => {
5
5
  addons.add(TOOL_ID, {
6
6
  type: types.TOOL,
7
7
  title: "CSS pseudo states",
8
- match: function match(_ref) {
9
- var viewMode = _ref.viewMode;
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 var decorators = [withPseudoState];
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
- var isAtRule = function isAtRule(selector) {
2
- return selector.indexOf("@") === 0;
3
- };
1
+ const isAtRule = selector => selector.indexOf("@") === 0;
4
2
 
5
- export var splitSelectors = function splitSelectors(selectors) {
3
+ export const splitSelectors = selectors => {
6
4
  if (isAtRule(selectors)) return [selectors];
7
- var result = [];
8
- var parentheses = 0;
9
- var brackets = 0;
10
- var selector = "";
5
+ let result = [];
6
+ let parentheses = 0;
7
+ let brackets = 0;
8
+ let selector = "";
11
9
 
12
- for (var i = 0, len = selectors.length; i < len; i++) {
13
- var char = selectors[i];
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;