mirror of
https://github.com/heroui-inc/heroui.git
synced 2026-09-18 18:55:01 +08:00
cb3a171955
* build(deps): patch js-yaml and svgo security advisories (#6844) Pin transitive js-yaml and svgo to patched versions via pnpm overrides: - js-yaml 3.15.1 -> 3.15.2 and 4.3.1 -> 4.3.2 (GHSA-2883-xcg3-v3hh): maxTotalMergeKeys does not count empty mappings, allowing CPU exhaustion via repeated merges of a large empty-mapping sequence. - svgo 2.8.3 -> 2.8.4 (GHSA-w27v-7q3p-w38r, GHSA-4vpr-x523-8j87): removeScripts let executable links through via namespace-prefixed anchors and control-character URL schemes, and did not sanitize HTML inside foreignObject. All three patches satisfy the ranges their parents already declare (gray-matter ^3.13.1, postcss-svgo ^2.7.0), so no package.json changes are needed. Overrides can be dropped once those parents update. * fix(styles): only ring list-box and menu items on real keyboard focus (#6849) * fix(styles): only ring list-box and menu items on real keyboard focus v3.2.5 dropped `:not(:focus)` from the focus rules repo-wide. That guard made the native selector inert, so the ring had only ever been driven by React Aria's data-focus-visible. Reinstating it as-is keeps dead code, so scope the native fallback to elements React Aria does not own instead. * fix(styles): extend keyboard-only focus ring guard to swept selectors The ring also appears when React Aria restores focus to a trigger on overlay close, not just when focus moves into a collection on open. Enumerating affected surfaces proved unreliable, so apply the guard wherever the v3.2.5 sweep landed: 37 selectors across 26 files. React Aria sets data-focused whenever it owns an element, so the attribute path covers it fully and the native path stays a non-RAC fallback. Calendar nav buttons keep their bare :focus-visible since they predate the sweep. Input and textarea focus-visible blocks are empty; their ring comes from :focus and is unchanged. * build(deps): bump react-aria to 3.52.1 and react-aria-components to 1.21.1 (#6848) * chore(styles): fix selective-import recipe and size measurement (#6846) Three packaging defects that made the documented à-la-carte path and the bundle-size report unreliable: - The README recipe imported only components + theme, but every component CSS file @applies shared utilities (status-focused, status-disabled, … — 176 uses across 83 files) and custom variants (motion-reduce:). Document the required utilities + variants prelude so the recipe actually compiles. - The CSS subpath exports (./components/*.css, ./utilities, ./variants, ./themes/default, ./base) only existed after clean-package rewrote the published package.json. Mirror them in the committed exports so monorepo consumers (docs, storybook) and the published surface match. - measureBundleSizes() read dist/components/<name>/index.js, which under preserveModules is a ~500-byte re-export shim. Button was reported as 0.21 KB gzip when its own modules total 0.72 KB, and the "total" summed overlapping scopes to ~24 KB against a real unique-file total of ~74 KB. Measure every .js under each component directory, report utils/ and hooks/ separately, and compute total from the unique set of all dist .js files. - Stop tracking the stale packages/styles/bundle-size.json (singular; 9 months out of date) and gitignore both filename variants. * ci: speed up the pipeline and stop running dead work (#6845) * ci: speed up the pipeline and stop running dead work - Drop ^build from test / test:browser / test:coverage. Both @heroui/react and @heroui/styles resolve to source via their exports maps, so the forced styles rebuild bought nothing for the test job. - Cache .turbo across QA jobs and cancel in-progress runs on the same ref. Merge the ESLint and TypeScript jobs into one install (Lint & Typecheck). continuous-release now needs: [build] so it only runs after a green build. - Declare real outputs so Turbo can cache: typecheck writes *.tsbuildinfo; build also produces bundle-sizes.json and apps/docs/public/skills/**. Narrow lint/test/typecheck inputs to exclude stories/mdx. Mark start as persistent/uncacheable like dev. - Delete update-stats.yml — it called update:search-meta and update:github-info, neither of which exists, and targeted base: main (not canary/v3*). - Remove the six dead docs-cn scripts. Chinese docs already live under apps/docs/content/docs/cn; there is no @heroui/docs-cn package. Slim postinstall to just typegen:docs (drop the unconditional styles rebuild and the no-op typegen:docs-cn). - Tighten pnpm-workspace globs so apps/docs/.contentlayer stops registering as a phantom "dot-contentlayer" package. Delete the redundant packages/testing/turbo.json. - Unify declaration generation with the shared React tsconfig so shipped .d.ts and pnpm typecheck share the same strictness. Gate release.yml on lint/typecheck/test before publish. Drop the hardcoded git identity from the install action (only the deleted stats workflow needed it). * ci: keep the styles build that browser tests need and fix type emit Two regressions from the previous commit: - Browser suites import packages/styles/dist/heroui.min.css by relative path, so dropping dependsOn: ^build from test / test:browser left that file ungenerated and every browser suite died behind a Vite error overlay. Restored on both; test:coverage stays without it since it is jsdom-only. - tsconfig.build.json set jsx: react-jsx, which made the JSX-only React imports unused locals and tripped the noUnusedLocals inherited from the shared config. Inheriting jsx instead keeps emit identical to typecheck. * fix(react): correct dependency declarations and production warning leak (#6847) * fix(react): correct dependency declarations and production warning leak - Promote @internationalized/date to peerDependencies. It is a real runtime import (utils/calendar.ts, calendar, range-calendar) and already marked external in rollup.config.mjs, but was only declared as a devDependency, so it resolved by accident through react-aria-components' own tree. - Drop tailwind-merge from dependencies. Zero imports in src; it is already transitive via tailwind-variants, and pinning it directly was what produced the 3.4.0/3.6.0 duplicate in the lockfile. - Drop @react-aria/i18n from peerDependencies and devDependencies. Zero references anywhere in the repo, but it forced an install on every consumer. - Put `types` first and add a `default` fallback in every generated exports condition, so resolution is correct under moduleResolution node16/nextnode. - Gate the ColorSlider channel/colorSpace warnings behind NODE_ENV. getValidColorSpace runs in the render body, so a misconfigured slider was warning on every render in production builds. - Move tag's md padding and font-size into the base .tag block so bare `class="tag"` markup is not rendered with zero padding, per the documented default-size pattern. * chore(docs): align tailwind-merge on 3.6.0 Dropping the unused direct tailwind-merge dependency from @heroui/react let tailwind-variants resolve its optional peer freely, which floated the library to 3.6.0 while apps/docs stayed pinned at 3.4.0 — two copies, and class merging running on a version nobody picked. Align docs on 3.6.0 so the workspace resolves a single copy. Verified: full docs build from scratch, 614 jsdom + 42 browser + 27 docs tests, typecheck, lint. * ci(lint): make the ESLint job actually able to fail (#6851) * ci(lint): make the ESLint job actually able to fail eslint-plugin-only-warn is registered in the shared base config, so it applies to every package and demotes every "error" severity to a warning. Combined with a bare `eslint .` in each package's lint script, the QA ESLint job always exited 0 and could never block a PR. Only the pre-commit hook was enforcing anything, because lint-staged already passes --max-warnings=0. Add --max-warnings=0 to all three lint scripts so warnings become the gate, keeping only-warn's editor ergonomics intact. Verified: a violation now reports "ESLint found too many warnings (maximum: 0)" and exits non-zero. The existing backlog was 2 auto-fixable import/order warnings in apps/docs, both fixed here. Also tighten two rules that were disabled more broadly than necessary: - react-hooks/rules-of-hooks was off for all of @heroui/react. All 159 violations are in .stories.tsx, where Storybook's lowercase render callbacks cannot be recognised as components; zero are in src/ or tests/. Scoped the disable to stories so real conditional-hook bugs are caught. - react-hooks/exhaustive-deps was off globally, now "warn". Of the 4 violations, the unnecessary itemIds.length dep in use-disclosure-group- navigation is simply removed, and calendar-year-picker's years and itemByYear are now memoized on the stable itemsKey instead of being rebuilt every render. That also fixes an effect that re-ran on every render because it depended on the unstable years array. * fix(docs): pin the ESLint import resolver so lint is cwd-independent Making lint blocking surfaced a pre-existing conflict: `pnpm lint` and the lint-staged pre-commit hook demanded contradictory import order for the same line, so no file content could satisfy both and each run reverted the other's autofix. Cause: the shared base sets `import/resolver: {typescript: true}`, which discovers tsconfig from process.cwd(). Running eslint from apps/docs resolved the `@/*` path aliases, but lint-staged runs it from the repo root where the root tsconfig has no such paths, so import/order classified aliased imports into a different group and produced the opposite ordering. Pin the resolver to apps/docs/tsconfig.json. Both invocations now report identical results, and the two import/order warnings are fixed. * fix(calendar): keep year labels in sync with locale and format changes The itemByYear memo added alongside the exhaustive-deps gate was keyed on itemsKey, which encodes only id:year. item.formatted also depends on locale, time zone and the format prop, so changing any of those left the grid rendering year labels in the previous format. Key the memo on the labels too, and wire @heroui/styles into turbo lint -- it had an ESLint config but no lint script, so its 175 files were never checked. * refactor(react): context memo (#6852) * refactor(react): memoize context values and stabilize refs 54 of ~67 context providers were passing a fresh object literal every render (`value={{slots}}`), so every consumer re-rendered whenever the root did — even when slots itself was correctly memoized. React Compiler is not enabled for the library build, so this was load-bearing and incomplete. - Memoize 43 stable context values with useMemo, or hoist module-level constants for LabelContext ({elementType: "span"}) and SurfaceContext ({variant: "default"}). Highest-consumer wins: table (12), pagination (7), accordion (6), tabs/card/slider (5). - Leave the 13 providers that feed live RAC render-prop state untouched; those need a slots/state context split, which is out of scope here. - Memoize mergeRefs(...) in date-picker, date-range-picker, autocomplete (x2), and scroll-shadow. Passing a fresh mergeRefs result as `ref` was detaching and reattaching the DOM node on every render. - Replace `= []` defaults that defeated their own useMemo deps (input-otp validationErrors, disclosure-group itemIds, combo-box siblings) with module-level EMPTY_* constants. - Hoist currentYear into the calendar-year-picker grid context so each cell no longer allocates a Date (default range is 1900–2099 ≈ 200 cells). * refactor(calendar): read currentYear per render instead of memoizing on [] Hoisting currentYear out of the cells is the win -- a grid was allocating one Date per year instead of one total. Memoizing it on [] also froze it for the component's lifetime, so a calendar mounted across midnight on Dec 31 would keep highlighting the old year. It's a number, so the context memo still compares it by value and the stability benefit is unchanged. * refactor: remove dead code, dead CSS, and outdated agent guidance (#6853) * chore: remove dead code, dead CSS, and outdated agent guidance - Drop unused createVariantBuilder / createVariants (~150 lines) from utils/variants.ts; only mapPropsVariants remains. Drop unused getValidChildren from utils/children.ts. - Delete the abandoned separator compound subsystem (7 classes never referenced by separator.styles.ts), plus .checkbox--disabled, .radio--disabled, and .switch__label (migration residue — Switch composes the shared Label). - Stop emitting phantom drawer__dialog--{left,right,bottom} classes; placement styling already routes through drawer__content--*, and only --top has a real CSS rule (handle padding). - Rename fieldset__field_group → fieldset__field-group (BEM). - Docs: remove the .radio--disabled listing that documented a class that never existed in the styles map. AGENTS.md: replace forwardRef examples with React 19 ref-as-prop, and show a memoized context value. * chore: keep drawer dialog hooks, sync fieldset docs and CLAUDE.md Three follow-ups to the dead-code pass: - Restore the drawer placement dialog mappings. drawer__dialog--bottom/left/right carry no CSS, but they are applied to the DOM and documented as public styling hooks in both locales, so dropping them would silently kill consumer overrides. Empty modifier hooks are an established convention here (see .avatar--md). - Rename fieldset__field_group to field-group in the en and cn docs. The class was renamed in CSS and styles.ts but the docs still showed the old name in a CSS override example and a class listing. - Apply the same React 19 ref-as-prop template to CLAUDE.md that AGENTS.md got. Both are always-applied agent rules, so updating only one left them contradicting each other -- CLAUDE.md still said to use forwardRef, which no component in src/components does. * fix(styles): remove stacked breadcrumbs padding so crumbs sit on-grid (#6854) * fix(styles): remove stacked breadcrumbs padding so crumbs sit on-grid * fix(styles): restore breadcrumbs spacing with gap instead of padding * fix: address v3.2.6 review follow-ups (#6855) * docs(agents): correct stale postinstall gotcha * fix(react): return undefined from pickChildren when no targets match * test(autocomplete): compile browser styles via @/styles.css * test(scroll-shadow): compile browser styles via @/styles.css * test(tag): compile browser styles via @/styles.css * refactor(styles): remove empty input focus-visible rules * refactor(styles): remove empty number-field hover and focus rules * refactor(styles): remove empty textarea focus-visible rules * docs(releases): add v3.2.6 release notes for en and cn * feat(component): avatar group (#6861) * feat(component): avatar group * docs(component): avatar group * fix(docs): remove stale avatar group demo export * chore(docs): update release notes * chore(docs): update release notes * build(deps): bump patch and minor dependencies across workspace (#6866) * build(deps): bump patch and minor dependencies across workspace Bump 89 dependencies across all six workspace manifests, restricted to patch and minor releases. All 27 available majors are skipped (eslint 10, vitest 5, typescript 7, vite 8, @babel/core 8, motion 13, shiki 4, lucide-react 1.x, @vercel/og 1.x and others). Also align intra-repo version drift: tailwindcss was 4.1.18 in packages/react but 4.3.0 elsewhere, typescript was 5.8.3 in packages/styles, @storybook/react was 10.1.10 in packages/react, @vitejs/plugin-react differed between two packages, and babel-plugin-react-compiler was on a stale 19.1.0-rc.3 at root while apps/docs already used the stable 1.0.0. Pin typescript in packages/react, packages/storybook and apps/docs, and jsdom in packages/react. These packages run tsc and vitest without declaring them, so pnpm auto-installed the optional peers at latest on lockfile refresh: typescript floated to 7.0.2 (breaking the build on baseUrl removal) and jsdom floated to 30.1.0, overriding the pin in @heroui/testing. Hold back three minors that ship breaking changes: - fumadocs-core/fumadocs-ui stay at 16.9.0. 16.10.0 removed the I18nLabel export and dropped `text` from LocaleContextType, both used by the docs app. - fumadocs-typescript stays at 5.0.0. 5.4.1 moves typescript from a peer to a hard ~7.0.2 dependency and adds shiki ^4. - eslint-plugin-react-hooks stays at 7.0.1. 7.1.1 changes which sites its rules fire on and flags setState-in-effect calls in the in-progress calendar-year-picker. jsdom is held at 30.0.1: 30.1.0 dispatches focusout with a non-Node relatedTarget, which throws inside react-aria's isBlurFromActiveElement and broke the Select suite. * style: reformat for prettier 3.9 and tailwind plugin 0.8 Formatting fallout from the prettier 3.7.4 -> 3.9.7 and prettier-plugin-tailwindcss 0.7.2 -> 0.8.1 bumps. Without it `pnpm lint` and `pnpm format` fail. Four files pick up prettier 3.9's union type collapsing, where a union that fits on one line no longer gets leading pipes. table.stories.tsx picks up a class reorder from the tailwind plugin, moving `scrollbar` ahead of `overflow-auto`; the two do not conflict, so rendering is unchanged. No behavioral changes. * chore(docs): update release note --------- Co-authored-by: WK <wingkwong.code@gmail.com> Co-authored-by: Tianen Pang <32772271+tianenpang@users.noreply.github.com>