@allxsmith/bestax-bulma 5.15.2 → 5.15.4

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/dist/index.esm.js CHANGED
@@ -1779,6 +1779,75 @@ const Card = withSubComponents(CardComponent, {
1779
1779
  /** Internal test-only exports. Not part of the public API. */
1780
1780
  const __test_exports__ = { renderFooter };
1781
1781
 
1782
+ /**
1783
+ * The runtime mirror of `AnchorOnlyAttributes`, keyed so the two cannot drift.
1784
+ *
1785
+ * `Record<keyof …, true>` is what links them: the day React adds an anchor
1786
+ * attribute, every component deriving its props from `as` accepts it and this
1787
+ * object stops compiling until it is named here. A plain array beside a derived
1788
+ * type drifts in the one direction that matters — the type accepts the new
1789
+ * attribute and the filter lets it leak onto a `<div>`.
1790
+ */
1791
+ const ANCHOR_ONLY_ATTRS = {
1792
+ href: true,
1793
+ target: true,
1794
+ download: true,
1795
+ hrefLang: true,
1796
+ ping: true,
1797
+ referrerPolicy: true,
1798
+ media: true,
1799
+ type: true,
1800
+ };
1801
+ /**
1802
+ * `props` without the keys named in `strip`.
1803
+ *
1804
+ * The SET is per component and deliberately not shared, because the right
1805
+ * answer differs and the differences are principled: how open the component's
1806
+ * `as` is, and which of these attributes its own tags legitimately take. What
1807
+ * IS shared is the derived source of truth above and this function. Each caller
1808
+ * declares its delta next to the set it passes, which is the only place that
1809
+ * stays true when a component's `as` changes.
1810
+ */
1811
+ function omitAttrs(props, strip) {
1812
+ const out = {};
1813
+ // `Reflect.ownKeys`, not `Object.entries`: the latter drops symbol-keyed props.
1814
+ // React enumerates props with `for...in`, which never reports a symbol, so
1815
+ // nothing renders differently either way. It is not the same change for all
1816
+ // three callers: Menu filtered by rest-destructure and kept them already,
1817
+ // while Dropdown and Level filtered through `Object.entries` and did not, so
1818
+ // this restores one and widens two. Symbols are the only shape where those
1819
+ // two disagreed, and keeping them is the side a spread is on.
1820
+ for (const key of Reflect.ownKeys(props)) {
1821
+ // Enumerability, for the same reason in the other direction: `Reflect.ownKeys`
1822
+ // also reports NON-enumerable own props, which every filter this replaced left
1823
+ // behind. Copying one out would both add a prop the old code dropped and make
1824
+ // it enumerable on the way, so widening the key source must not widen this.
1825
+ if (!Object.prototype.propertyIsEnumerable.call(props, key))
1826
+ continue;
1827
+ const value = props[key];
1828
+ // `hasOwnProperty`, not `key in strip`: `in` walks the prototype chain, so a
1829
+ // prop named `toString`, `valueOf`, `constructor` or `hasOwnProperty` was
1830
+ // stripped although no caller named it — and anything written to
1831
+ // `Object.prototype` would start deleting props that share its keys.
1832
+ if (typeof key === 'symbol' ||
1833
+ !Object.prototype.hasOwnProperty.call(strip, key)) {
1834
+ // `defineProperty`, not `out[key] = value`: assignment consults the
1835
+ // prototype chain for a setter, and `__proto__` has one. A prop named
1836
+ // `__proto__` would be swallowed by it — dropped from the result AND
1837
+ // replacing the result's prototype with its value. A spread and a rest
1838
+ // destructure both DEFINE, which is what makes them immune, so this has
1839
+ // to define too.
1840
+ Object.defineProperty(out, key, {
1841
+ value,
1842
+ writable: true,
1843
+ enumerable: true,
1844
+ configurable: true,
1845
+ });
1846
+ }
1847
+ }
1848
+ return out;
1849
+ }
1850
+
1782
1851
  /**
1783
1852
  * Checks if code is running in a browser environment.
1784
1853
  * @param win - Window object.
@@ -2001,32 +2070,25 @@ const DropdownComponent = forwardRef(function DropdownComponent({ label, childre
2001
2070
  return (jsxs("div", { className: dropdownClasses, ref: setRefs, id: id, "data-testid": "dropdown-root", ...rest, children: [jsx("div", { className: usePrefixedClassNames('dropdown-trigger'), children: jsxs("button", { ref: triggerRef, className: buttonClass, "aria-haspopup": "true", "aria-controls": id ? `${id}-menu` : undefined, "aria-expanded": active, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, disabled: disabled, type: "button", children: [jsx("span", { children: label }), jsx("span", { className: usePrefixedClassNames('icon', 'is-small'), "aria-hidden": "true", children: jsx("i", { className: "fas fa-angle-down" }) })] }) }), jsx("div", { className: classNames(usePrefixedClassNames('dropdown-menu'), menuClassName), id: id ? `${id}-menu` : undefined, role: "menu", "data-testid": "dropdown-menu", onKeyDown: handleMenuKeyDown, children: jsx("div", { className: usePrefixedClassNames('dropdown-content'), onClick: handleMenuClick, tabIndex: -1, children: children }) })] }));
2002
2071
  });
2003
2072
  /**
2004
- * The anchor-only attributes, withheld from a `<div>` or a `<button>`.
2005
- *
2006
- * Mirrors `LINK_ATTRS` in `bestax-migrate/src/sources/_shared/polymorphic.ts`,
2007
- * which drops the same set when a migration lands one on a non-anchor. `rel` is
2008
- * absent from both on purpose: React declares it on `HTMLAttributes<T>` — every
2009
- * element — so withholding it would diverge from React's own typing, the same
2010
- * call #641 recorded for `Navbar.Link`.
2011
- */
2012
- const LINK_ONLY_ATTRS = [
2013
- 'href',
2014
- 'target',
2015
- 'download',
2016
- 'hrefLang',
2017
- 'ping',
2018
- 'referrerPolicy',
2019
- 'media',
2020
- ];
2021
- /** `props` without any attribute only an anchor can carry. */
2022
- function omitLinkAttrs(props) {
2023
- const out = {};
2024
- for (const [key, value] of Object.entries(props)) {
2025
- if (!LINK_ONLY_ATTRS.includes(key))
2026
- out[key] = value;
2027
- }
2028
- return out;
2029
- }
2073
+ * The anchor's attributes, minus the one a `<button>` legitimately takes.
2074
+ *
2075
+ * `type` stays: `as="button"` is a supported form and `type="submit"` is valid
2076
+ * there, so stripping it would remove a working attribute. The exclusion is per
2077
+ * COMPONENT where the reason is per TAG, so it also lets a `type` reach a
2078
+ * `<div>`; selecting the set from the rendered element would close that, and
2079
+ * moves output.
2080
+ *
2081
+ * This set differs from `Level.Item`'s in both directions, not just one. The
2082
+ * `type` above, which Level strips and this component keeps, and `rel`, which
2083
+ * Level adds and this component does not: React declares `rel` on
2084
+ * `HTMLAttributes` for every element, so withholding it would diverge from
2085
+ * React's own typing — the call #641 recorded for `Navbar.Link`. Level
2086
+ * withholds it anyway, because it always has.
2087
+ */
2088
+ const STRIP_FROM_NON_ANCHOR$1 = (() => {
2089
+ const { type: _type, ...rest } = ANCHOR_ONLY_ATTRS;
2090
+ return rest;
2091
+ })();
2030
2092
  /**
2031
2093
  * Bulma Dropdown item.
2032
2094
  *
@@ -2044,16 +2106,18 @@ const DropdownItem = ((itemProps) => {
2044
2106
  // plain-JS one, a loose `{...props}` spread, and the genericity a wrapping HOC
2045
2107
  // erases.
2046
2108
  //
2047
- // The whole link set, not `href` alone. Menu strips only `href` because its
2048
- // props never gained the rest; here they all arrive together under `as="a"`,
2049
- // and `bestax-migrate` already removes exactly this set (`LINK_ATTRS`) when it
2050
- // migrates onto a non-anchor — so stripping less would leave the codemod
2051
- // stricter than the component it migrates to.
2109
+ // The whole link set, not `href` alone: they all arrive together under
2110
+ // `as="a"`. Menu strips `href` only, because its `as` is open and a flat set
2111
+ // would delete attributes legal on the element a caller named.
2112
+ //
2113
+ // `bestax-migrate` tracks the same question from the other side, per attribute
2114
+ // per element (`LINK_ATTR_ELEMENTS`) rather than as one set — so the two lists
2115
+ // are not interchangeable and neither derives from the other.
2052
2116
  //
2053
2117
  // Menu's condition also admits a custom component and a custom element, which
2054
2118
  // own their prop contracts. `as` is closed to three intrinsic tags here, so
2055
2119
  // neither can arrive and the anchor test is the whole rule.
2056
- const forwarded = Component === 'a' ? rest : omitLinkAttrs(rest);
2120
+ const forwarded = Component === 'a' ? rest : omitAttrs(rest, STRIP_FROM_NON_ANCHOR$1);
2057
2121
  return (jsx(Component, { className: classNames(usePrefixedClassNames('dropdown-item', {
2058
2122
  'is-active': active,
2059
2123
  }), bulmaHelperClasses, className), tabIndex: 0, role: "menuitem", "data-testid": "dropdown-item", ...forwarded, ...(Component === 'button'
@@ -2118,6 +2182,11 @@ const MenuList = ({ className, children, ...props }) => {
2118
2182
  // Increment level for nested MenuLists
2119
2183
  return (jsx(MenuListLevelContext.Provider, { value: level + 1, children: jsx("ul", { className: ulClass, ...rest, children: children }) }));
2120
2184
  };
2185
+ /**
2186
+ * `href` alone — annotated so a typo is a compile error rather than a silent
2187
+ * no-op, the way both siblings' sets are.
2188
+ */
2189
+ const STRIP_FROM_NON_LINK = { href: true };
2121
2190
  /**
2122
2191
  * MenuItem supports `as` prop for custom link components, e.g., react-router-dom Link.
2123
2192
  *
@@ -2137,8 +2206,20 @@ const MenuItem = forwardRef(function MenuItem(itemProps, ref) {
2137
2206
  const isLinkLike = Component === 'a' ||
2138
2207
  typeof Component !== 'string' ||
2139
2208
  isCustomElement(Component);
2140
- const { href: _href, ...withoutHref } = forwarded;
2141
- const linkProps = isLinkLike ? forwarded : withoutHref;
2209
+ // `href` ALONE. `Dropdown.Item` strips every anchor-only attribute except
2210
+ // `type`; `Level.Item` strips all of them, plus `rel`.
2211
+ //
2212
+ // Not because nothing else can arrive: `as` here is an open `React.ElementType`,
2213
+ // and a non-fresh spread skips excess-property checking, so a typed caller
2214
+ // CAN deliver `target` or `download` and they are forwarded. The set stays at
2215
+ // `href` because widening it would be wrong for an open `as` — `referrerPolicy`
2216
+ // on `as="img"`, `target` on `as="form"` and `media` on `as="source"` are all
2217
+ // legal, and a flat strip would delete them. Getting that right needs a
2218
+ // per-attribute-per-element rule, which `bestax-migrate` already models in
2219
+ // `LINK_ATTR_ELEMENTS`.
2220
+ const linkProps = isLinkLike
2221
+ ? forwarded
2222
+ : omitAttrs(forwarded, STRIP_FROM_NON_LINK);
2142
2223
  const itemClass = classNames({ [usePrefixedClassNames('is-active')]: active }, bulmaHelperClasses);
2143
2224
  // Split children into label and nested MenuList(s)
2144
2225
  const labelChildren = [];
@@ -12987,6 +13068,18 @@ const LevelRight = ({ className, children, color, bgColor, textColor, ...props }
12987
13068
  const levelRightClasses = classNames(mainClass, bulmaHelperClasses, className);
12988
13069
  return (jsx("div", { className: levelRightClasses, ...rest, children: children }));
12989
13070
  };
13071
+ /**
13072
+ * The derived set plus `rel`.
13073
+ *
13074
+ * `rel` is not an anchor-only attribute — React declares it on every element, so
13075
+ * `AnchorOnlyAttributes` excludes it — but this component has always withheld it
13076
+ * from a non-anchor, so it is named separately rather than folded into the shared
13077
+ * set. That separation is what says which names come from where.
13078
+ *
13079
+ * The annotation is load-bearing: without it a typo adds a key nobody strips, and
13080
+ * widening the key type to `string` also erases `omitAttrs`'s return type.
13081
+ */
13082
+ const STRIP_FROM_NON_ANCHOR = { ...ANCHOR_ONLY_ATTRS, rel: true };
12990
13083
  /**
12991
13084
  * Individual item, can be rendered as `div`, `p`, or `a`.
12992
13085
  *
@@ -12994,7 +13087,7 @@ const LevelRight = ({ className, children, color, bgColor, textColor, ...props }
12994
13087
  * @param {LevelItemProps} props - Props for the LevelItem component.
12995
13088
  * @returns {JSX.Element} The rendered level item.
12996
13089
  */
12997
- const LevelItem = ({ as = 'div', hasTextCentered, className, children, href, target, rel, color, bgColor, textColor, ...props }) => {
13090
+ const LevelItem = ({ as = 'div', hasTextCentered, className, children, color, bgColor, textColor, ...props }) => {
12998
13091
  const { bulmaHelperClasses, rest } = useBulmaClasses({
12999
13092
  color: textColor ?? color,
13000
13093
  backgroundColor: bgColor,
@@ -13005,11 +13098,16 @@ const LevelItem = ({ as = 'div', hasTextCentered, className, children, href, tar
13005
13098
  'has-text-centered': hasTextCentered,
13006
13099
  });
13007
13100
  const levelItemClasses = classNames(mainClass, bulmaHelperClasses, className);
13008
- // If rendering as "a", only pass anchor-specific props
13101
+ // The anchor's own attributes reach an `<a>` and nothing else. The type
13102
+ // declares them at every `as` — narrowing that is source-breaking (#672) — so
13103
+ // the runtime is what keeps `<Level.Item as="p" download>` from rendering a
13104
+ // dead attribute.
13105
+ // `STRIP_FROM_NON_ANCHOR` above names the set and why `rel` is in it.
13009
13106
  if (Tag === 'a') {
13010
- return (jsx("a", { className: levelItemClasses, href: href, target: target, rel: rel, ...rest, children: children }));
13107
+ return (jsx("a", { className: levelItemClasses, ...rest, children: children }));
13011
13108
  }
13012
- return (jsx(Tag, { className: levelItemClasses, ...rest, children: children }));
13109
+ const withoutAnchorAttrs = omitAttrs(rest, STRIP_FROM_NON_ANCHOR);
13110
+ return (jsx(Tag, { className: levelItemClasses, ...withoutAnchorAttrs, children: children }));
13013
13111
  };
13014
13112
  const Level = withSubComponents(LevelComponent, {
13015
13113
  Left: LevelLeft,