@haruhimemoe/ui 0.3.0 → 0.5.0

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.
Files changed (130) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +336 -27
  3. package/dist/components/actions/AsyncButton.d.ts +30 -0
  4. package/dist/components/actions/AsyncButton.js +48 -0
  5. package/dist/components/actions/CopyButton.d.ts +3 -3
  6. package/dist/components/actions/CopyButton.js +9 -12
  7. package/dist/components/actions/InlineConfirm.d.ts +45 -0
  8. package/dist/components/actions/InlineConfirm.js +80 -0
  9. package/dist/components/actions/Pagination.d.ts +40 -17
  10. package/dist/components/actions/Pagination.js +25 -18
  11. package/dist/components/actions/PaginationButton.d.ts +25 -0
  12. package/dist/components/actions/PaginationButton.js +23 -0
  13. package/dist/components/actions/PaginationStatus.d.ts +7 -4
  14. package/dist/components/actions/PaginationStatus.js +7 -5
  15. package/dist/components/actions/StatusOutput.d.ts +20 -0
  16. package/dist/components/actions/StatusOutput.js +9 -0
  17. package/dist/components/actions/pages.d.ts +21 -0
  18. package/dist/components/actions/pages.js +21 -0
  19. package/dist/components/actions/useLatestStatus.d.ts +26 -0
  20. package/dist/components/actions/useLatestStatus.js +32 -0
  21. package/dist/components/basics/AutoLink.d.ts +21 -0
  22. package/dist/components/basics/AutoLink.js +26 -0
  23. package/dist/components/basics/Badge.d.ts +22 -0
  24. package/dist/components/basics/Badge.js +18 -0
  25. package/dist/components/basics/ButtonLink.d.ts +5 -6
  26. package/dist/components/basics/ButtonLink.js +5 -11
  27. package/dist/components/basics/Card.d.ts +4 -3
  28. package/dist/components/basics/Card.js +3 -2
  29. package/dist/components/basics/Disclosure.d.ts +34 -0
  30. package/dist/components/basics/Disclosure.js +30 -0
  31. package/dist/components/basics/Notice.d.ts +2 -1
  32. package/dist/components/basics/Tabs.d.ts +37 -0
  33. package/dist/components/basics/Tabs.js +47 -0
  34. package/dist/components/basics/TextLink.d.ts +21 -0
  35. package/dist/components/basics/TextLink.js +19 -0
  36. package/dist/components/basics/buttonStyles.d.ts +3 -1
  37. package/dist/components/basics/buttonStyles.js +1 -1
  38. package/dist/components/basics/cardStyles.d.ts +14 -0
  39. package/dist/components/basics/cardStyles.js +12 -0
  40. package/dist/components/basics/linkStyles.d.ts +23 -0
  41. package/dist/components/basics/linkStyles.js +23 -0
  42. package/dist/components/basics/tabIds.d.ts +23 -0
  43. package/dist/components/basics/tabIds.js +23 -0
  44. package/dist/components/filters/Chip.d.ts +13 -5
  45. package/dist/components/filters/Chip.js +20 -11
  46. package/dist/components/filters/ChipGroup.d.ts +11 -12
  47. package/dist/components/filters/ChipGroup.js +5 -10
  48. package/dist/components/filters/ChoiceChips.d.ts +38 -0
  49. package/dist/components/filters/ChoiceChips.js +31 -0
  50. package/dist/components/filters/FilterPanel.d.ts +5 -4
  51. package/dist/components/filters/FilterPanel.js +5 -4
  52. package/dist/components/filters/FilterRow.d.ts +1 -1
  53. package/dist/components/filters/FilterRow.js +3 -2
  54. package/dist/components/filters/GroupFrame.d.ts +27 -0
  55. package/dist/components/filters/GroupFrame.js +27 -0
  56. package/dist/components/filters/RangeBox.d.ts +28 -0
  57. package/dist/components/filters/RangeBox.js +41 -0
  58. package/dist/components/filters/RangeSlider.d.ts +13 -14
  59. package/dist/components/filters/RangeSlider.js +40 -103
  60. package/dist/components/filters/chipStyles.d.ts +17 -0
  61. package/dist/components/filters/chipStyles.js +17 -0
  62. package/dist/components/filters/rangeMath.d.ts +106 -0
  63. package/dist/components/filters/rangeMath.js +146 -0
  64. package/dist/components/forms/CharCounter.d.ts +25 -0
  65. package/dist/components/forms/CharCounter.js +12 -0
  66. package/dist/components/forms/Checkbox.d.ts +5 -4
  67. package/dist/components/forms/Checkbox.js +3 -3
  68. package/dist/components/forms/FieldFrame.d.ts +28 -2
  69. package/dist/components/forms/FieldFrame.js +14 -1
  70. package/dist/components/forms/RadioGroup.d.ts +46 -0
  71. package/dist/components/forms/RadioGroup.js +32 -0
  72. package/dist/components/forms/ReportDisclosure.d.ts +52 -0
  73. package/dist/components/forms/ReportDisclosure.js +54 -0
  74. package/dist/components/forms/Select.d.ts +1 -1
  75. package/dist/components/forms/Select.js +2 -2
  76. package/dist/components/forms/TextInput.d.ts +1 -1
  77. package/dist/components/forms/TextInput.js +2 -2
  78. package/dist/components/forms/Textarea.d.ts +1 -1
  79. package/dist/components/forms/Textarea.js +2 -2
  80. package/dist/components/forms/TypeToConfirm.d.ts +40 -0
  81. package/dist/components/forms/TypeToConfirm.js +46 -0
  82. package/dist/components/forms/VisibilitySelect.d.ts +52 -0
  83. package/dist/components/forms/VisibilitySelect.js +45 -0
  84. package/dist/components/forms/fieldStyles.d.ts +5 -2
  85. package/dist/components/forms/fieldStyles.js +5 -2
  86. package/dist/components/{actions → meta}/JsonLd.d.ts +2 -2
  87. package/dist/components/osu/BeatmapStats.d.ts +36 -0
  88. package/dist/components/osu/BeatmapStats.js +44 -0
  89. package/dist/components/osu/ModBadge.d.ts +23 -0
  90. package/dist/components/osu/ModBadge.js +22 -0
  91. package/dist/components/osu/StarRating.d.ts +26 -0
  92. package/dist/components/osu/StarRating.js +17 -0
  93. package/dist/components/osu/starColors.d.ts +23 -0
  94. package/dist/components/osu/starColors.js +48 -0
  95. package/dist/components/shell/HeaderMenu.d.ts +39 -0
  96. package/dist/components/shell/HeaderMenu.js +58 -0
  97. package/dist/components/shell/LinkNote.d.ts +16 -0
  98. package/dist/components/shell/LinkNote.js +17 -0
  99. package/dist/components/shell/LinkTabs.d.ts +29 -0
  100. package/dist/components/shell/LinkTabs.js +15 -0
  101. package/dist/components/shell/NavItem.d.ts +1 -7
  102. package/dist/components/shell/NavItem.js +5 -10
  103. package/dist/components/shell/NavLinks.d.ts +1 -1
  104. package/dist/components/shell/NavLinks.js +3 -3
  105. package/dist/components/shell/NavListClient.d.ts +1 -1
  106. package/dist/components/shell/NavListClient.js +4 -4
  107. package/dist/components/shell/SiteFooter.d.ts +6 -4
  108. package/dist/components/shell/SiteFooter.js +8 -6
  109. package/dist/components/shell/links.d.ts +8 -1
  110. package/dist/components/shell/links.js +8 -1
  111. package/dist/components/tables/TBody.d.ts +16 -0
  112. package/dist/components/tables/TBody.js +10 -0
  113. package/dist/components/tables/THead.d.ts +16 -0
  114. package/dist/components/tables/THead.js +10 -0
  115. package/dist/components/tables/Table.d.ts +26 -0
  116. package/dist/components/tables/Table.js +12 -0
  117. package/dist/components/tables/Td.d.ts +19 -0
  118. package/dist/components/tables/Td.js +11 -0
  119. package/dist/components/tables/Th.d.ts +20 -0
  120. package/dist/components/tables/Th.js +11 -0
  121. package/dist/components/tables/tableStyles.d.ts +12 -0
  122. package/dist/components/tables/tableStyles.js +12 -0
  123. package/dist/index.d.ts +28 -2
  124. package/dist/index.js +32 -2
  125. package/dist/utils/href.d.ts +7 -4
  126. package/dist/utils/href.js +23 -7
  127. package/package.json +3 -3
  128. package/dist/components/shell/AutoLink.d.ts +0 -19
  129. package/dist/components/shell/AutoLink.js +0 -23
  130. /package/dist/components/{actions → meta}/JsonLd.js +0 -0
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../../utils/cx.js";
3
+ import { FIELD_LABEL } from "../forms/fieldStyles.js";
4
+ /**
5
+ * @function Table
6
+ * @param props {TableProps} native table props (rows as children), a caption, hideCaption and
7
+ * wrapperClassName; `className` and `ref` go on the `<table>`
8
+ * @returns {JSX.Element} an `overflow-x-auto` `<div>` around the `<table>` and its caption
9
+ */
10
+ export function Table({ caption, hideCaption = false, wrapperClassName, className, children, ...props }) {
11
+ return (_jsx("div", { className: cx("overflow-x-auto", wrapperClassName), children: _jsxs("table", { className: cx("w-full text-left text-sm", className), ...props, children: [caption ? (_jsx("caption", { className: hideCaption ? "sr-only" : `mb-2 text-left ${FIELD_LABEL}`, children: caption })) : null, children] }) }));
12
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file src/components/tables/Td.tsx
3
+ * @desc A data cell with the shared padding. Server-safe.
4
+ * @author David @dvhsh (https://dvh.sh)
5
+ * @created Mon Sep 28, 2026
6
+ * @modified Mon Sep 28, 2026
7
+ */
8
+ import type { ComponentProps } from "react";
9
+ /** Every native `<td>` prop (including `ref`), plus a flag for a number. */
10
+ export type TdProps = ComponentProps<"td"> & {
11
+ /** Line up digits (`tabular-nums`), for a number. */
12
+ numeric?: boolean | undefined;
13
+ };
14
+ /**
15
+ * @function Td
16
+ * @param props {TdProps} native td props, plus numeric
17
+ * @returns {JSX.Element} a `<td>` with the shared cell padding
18
+ */
19
+ export declare function Td({ numeric, className, ...props }: TdProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from "../../utils/cx.js";
3
+ import { CELL, NUMERIC } from "./tableStyles.js";
4
+ /**
5
+ * @function Td
6
+ * @param props {TdProps} native td props, plus numeric
7
+ * @returns {JSX.Element} a `<td>` with the shared cell padding
8
+ */
9
+ export function Td({ numeric = false, className, ...props }) {
10
+ return _jsx("td", { className: cx(CELL, numeric && NUMERIC, className), ...props });
11
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @file src/components/tables/Th.tsx
3
+ * @desc A header cell: a column heading by default (scope col), or a row's heading (scope row,
4
+ * bold c1). Server-safe.
5
+ * @author David @dvhsh (https://dvh.sh)
6
+ * @created Mon Sep 28, 2026
7
+ * @modified Mon Sep 28, 2026
8
+ */
9
+ import type { ComponentProps } from "react";
10
+ /** Every native `<th>` prop (including `ref`), plus a flag for a numbers column. */
11
+ export type ThProps = ComponentProps<"th"> & {
12
+ /** Line up digits (`tabular-nums`), for a column of numbers. */
13
+ numeric?: boolean | undefined;
14
+ };
15
+ /**
16
+ * @function Th
17
+ * @param props {ThProps} native th props (`scope` defaults to "col"), plus numeric
18
+ * @returns {JSX.Element} a `<th>` with the shared cell padding; bold c1 when it heads a row
19
+ */
20
+ export declare function Th({ scope, numeric, className, ...props }: ThProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from "../../utils/cx.js";
3
+ import { CELL, NUMERIC } from "./tableStyles.js";
4
+ /**
5
+ * @function Th
6
+ * @param props {ThProps} native th props (`scope` defaults to "col"), plus numeric
7
+ * @returns {JSX.Element} a `<th>` with the shared cell padding; bold c1 when it heads a row
8
+ */
9
+ export function Th({ scope = "col", numeric = false, className, ...props }) {
10
+ return (_jsx("th", { scope: scope, className: cx(CELL, scope === "row" && "font-bold text-c1", numeric && NUMERIC, className), ...props }));
11
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @file src/components/tables/tableStyles.ts
3
+ * @desc The cell padding Th and Td share (internal): room on the right between columns, none after
4
+ * the last one, as the apps' tables have it.
5
+ * @author David @dvhsh (https://dvh.sh)
6
+ * @created Mon Sep 28, 2026
7
+ * @modified Mon Sep 28, 2026
8
+ */
9
+ /** A header or data cell: py-2 and pr-3, with no right padding on the row's last cell. */
10
+ export declare const CELL = "py-2 pr-3 last:pr-0";
11
+ /** Numbers line up in a column. */
12
+ export declare const NUMERIC = "tabular-nums";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @file src/components/tables/tableStyles.ts
3
+ * @desc The cell padding Th and Td share (internal): room on the right between columns, none after
4
+ * the last one, as the apps' tables have it.
5
+ * @author David @dvhsh (https://dvh.sh)
6
+ * @created Mon Sep 28, 2026
7
+ * @modified Mon Sep 28, 2026
8
+ */
9
+ /** A header or data cell: py-2 and pr-3, with no right padding on the row's last cell. */
10
+ export const CELL = "py-2 pr-3 last:pr-0";
11
+ /** Numbers line up in a column. */
12
+ export const NUMERIC = "tabular-nums";
package/dist/index.d.ts CHANGED
@@ -5,35 +5,61 @@
5
5
  * their own "use client" directive, so this barrel is safe to import from Server Components.
6
6
  * @author David @dvhsh (https://dvh.sh)
7
7
  * @created Wed Sep 23, 2026
8
- * @modified Fri Sep 25, 2026
8
+ * @modified Mon Sep 28, 2026
9
9
  */
10
+ export { AsyncButton, type AsyncButtonProps } from "./components/actions/AsyncButton.js";
10
11
  export { CopyButton, type CopyButtonProps } from "./components/actions/CopyButton.js";
11
- export { JsonLd, type JsonLdProps } from "./components/actions/JsonLd.js";
12
+ export { InlineConfirm, type InlineConfirmProps } from "./components/actions/InlineConfirm.js";
12
13
  export { Pagination, type PaginationProps } from "./components/actions/Pagination.js";
14
+ export { Badge, type BadgeProps, type BadgeTone } from "./components/basics/Badge.js";
13
15
  export { Button, type ButtonProps } from "./components/basics/Button.js";
14
16
  export { ButtonLink, type ButtonLinkProps } from "./components/basics/ButtonLink.js";
15
17
  export { type ButtonClassOptions, type ButtonSize, type ButtonVariant, buttonClasses, } from "./components/basics/buttonStyles.js";
16
18
  export { Card, type CardProps } from "./components/basics/Card.js";
19
+ export type { HeadingLevel } from "./components/basics/cardStyles.js";
20
+ export { Disclosure, type DisclosureProps } from "./components/basics/Disclosure.js";
21
+ export { type LinkClassOptions, linkClasses, type TextLinkVariant, } from "./components/basics/linkStyles.js";
17
22
  export { Notice, type NoticeProps, type NoticeTone } from "./components/basics/Notice.js";
18
23
  export { PageHeader, type PageHeaderProps } from "./components/basics/PageHeader.js";
19
24
  export { Prose, type ProseProps } from "./components/basics/Prose.js";
25
+ export { type TabItem, Tabs, type TabsProps } from "./components/basics/Tabs.js";
26
+ export { TextLink, type TextLinkProps } from "./components/basics/TextLink.js";
27
+ export { tabId, tabPanelId } from "./components/basics/tabIds.js";
20
28
  export { Chip, type ChipProps } from "./components/filters/Chip.js";
21
29
  export { ChipGroup, type ChipGroupProps, type ChipOption } from "./components/filters/ChipGroup.js";
30
+ export { type ChoiceChipOption, ChoiceChips, type ChoiceChipsProps, } from "./components/filters/ChoiceChips.js";
22
31
  export { FilterPanel, type FilterPanelProps } from "./components/filters/FilterPanel.js";
23
32
  export { FilterRow, type FilterRowProps } from "./components/filters/FilterRow.js";
24
33
  export { RangeSlider, type RangeSliderProps, type RangeSliderValue, } from "./components/filters/RangeSlider.js";
34
+ export { CharCounter, type CharCounterProps } from "./components/forms/CharCounter.js";
25
35
  export { Checkbox, type CheckboxProps } from "./components/forms/Checkbox.js";
26
36
  export type { FieldProps } from "./components/forms/FieldFrame.js";
27
37
  export { fieldClasses } from "./components/forms/fieldStyles.js";
38
+ export { RadioGroup, type RadioGroupProps, type RadioOption, } from "./components/forms/RadioGroup.js";
39
+ export { ReportDisclosure, type ReportDisclosureProps, type ReportResult, } from "./components/forms/ReportDisclosure.js";
28
40
  export { Select, type SelectProps } from "./components/forms/Select.js";
29
41
  export { Textarea, type TextareaProps } from "./components/forms/Textarea.js";
30
42
  export { TextInput, type TextInputProps } from "./components/forms/TextInput.js";
43
+ export { TypeToConfirm, type TypeToConfirmProps } from "./components/forms/TypeToConfirm.js";
44
+ export { VISIBILITIES, VISIBILITY_TEXT, type Visibility, VisibilitySelect, type VisibilitySelectProps, type VisibilityText, } from "./components/forms/VisibilitySelect.js";
31
45
  export { DiscordIcon, type DiscordIconProps } from "./components/icons/DiscordIcon.js";
32
46
  export { GitHubIcon, type GitHubIconProps } from "./components/icons/GitHubIcon.js";
33
47
  export { HaruhimeWordmark, type HaruhimeWordmarkProps, } from "./components/icons/HaruhimeWordmark.js";
34
48
  export { HaruhimeWordmarkLink, type HaruhimeWordmarkLinkProps, } from "./components/icons/HaruhimeWordmarkLink.js";
49
+ export { JsonLd, type JsonLdProps } from "./components/meta/JsonLd.js";
50
+ export { type BeatmapStatKey, BeatmapStats, type BeatmapStatsProps, } from "./components/osu/BeatmapStats.js";
51
+ export { ModBadge, type ModBadgeProps } from "./components/osu/ModBadge.js";
52
+ export { StarRating, type StarRatingProps } from "./components/osu/StarRating.js";
53
+ export { HeaderMenu, type HeaderMenuItem, type HeaderMenuProps, } from "./components/shell/HeaderMenu.js";
54
+ export { type LinkTabItem, LinkTabs, type LinkTabsProps } from "./components/shell/LinkTabs.js";
35
55
  export type { SiteLinkItem } from "./components/shell/links.js";
36
56
  export { NavLinks, type NavLinksProps, type SiteNavAlign } from "./components/shell/NavLinks.js";
37
57
  export { PageShell, type PageShellProps } from "./components/shell/PageShell.js";
38
58
  export { SiteFooter, type SiteFooterColumn, type SiteFooterProps, } from "./components/shell/SiteFooter.js";
39
59
  export { SiteHeader, type SiteHeaderProps } from "./components/shell/SiteHeader.js";
60
+ export { Table, type TableProps } from "./components/tables/Table.js";
61
+ export { TBody, type TBodyProps } from "./components/tables/TBody.js";
62
+ export { Td, type TdProps } from "./components/tables/Td.js";
63
+ export { THead, type THeadProps } from "./components/tables/THead.js";
64
+ export { Th, type ThProps } from "./components/tables/Th.js";
65
+ export { type ClassValue, cx } from "./utils/cx.js";
package/dist/index.js CHANGED
@@ -5,38 +5,68 @@
5
5
  * their own "use client" directive, so this barrel is safe to import from Server Components.
6
6
  * @author David @dvhsh (https://dvh.sh)
7
7
  * @created Wed Sep 23, 2026
8
- * @modified Fri Sep 25, 2026
8
+ * @modified Mon Sep 28, 2026
9
9
  */
10
10
  // Actions
11
+ export { AsyncButton } from "./components/actions/AsyncButton.js";
11
12
  export { CopyButton } from "./components/actions/CopyButton.js";
12
- export { JsonLd } from "./components/actions/JsonLd.js";
13
+ export { InlineConfirm } from "./components/actions/InlineConfirm.js";
13
14
  export { Pagination } from "./components/actions/Pagination.js";
14
15
  // Basics
16
+ export { Badge } from "./components/basics/Badge.js";
15
17
  export { Button } from "./components/basics/Button.js";
16
18
  export { ButtonLink } from "./components/basics/ButtonLink.js";
17
19
  export { buttonClasses, } from "./components/basics/buttonStyles.js";
18
20
  export { Card } from "./components/basics/Card.js";
21
+ export { Disclosure } from "./components/basics/Disclosure.js";
22
+ export { linkClasses, } from "./components/basics/linkStyles.js";
19
23
  export { Notice } from "./components/basics/Notice.js";
20
24
  export { PageHeader } from "./components/basics/PageHeader.js";
21
25
  export { Prose } from "./components/basics/Prose.js";
26
+ export { Tabs } from "./components/basics/Tabs.js";
27
+ export { TextLink } from "./components/basics/TextLink.js";
28
+ export { tabId, tabPanelId } from "./components/basics/tabIds.js";
22
29
  // Filters
23
30
  export { Chip } from "./components/filters/Chip.js";
24
31
  export { ChipGroup } from "./components/filters/ChipGroup.js";
32
+ export { ChoiceChips, } from "./components/filters/ChoiceChips.js";
25
33
  export { FilterPanel } from "./components/filters/FilterPanel.js";
26
34
  export { FilterRow } from "./components/filters/FilterRow.js";
27
35
  export { RangeSlider, } from "./components/filters/RangeSlider.js";
28
36
  // Forms
37
+ export { CharCounter } from "./components/forms/CharCounter.js";
29
38
  export { Checkbox } from "./components/forms/Checkbox.js";
30
39
  export { fieldClasses } from "./components/forms/fieldStyles.js";
40
+ export { RadioGroup, } from "./components/forms/RadioGroup.js";
41
+ export { ReportDisclosure, } from "./components/forms/ReportDisclosure.js";
31
42
  export { Select } from "./components/forms/Select.js";
32
43
  export { Textarea } from "./components/forms/Textarea.js";
33
44
  export { TextInput } from "./components/forms/TextInput.js";
45
+ export { TypeToConfirm } from "./components/forms/TypeToConfirm.js";
46
+ export { VISIBILITIES, VISIBILITY_TEXT, VisibilitySelect, } from "./components/forms/VisibilitySelect.js";
34
47
  // Icons
35
48
  export { DiscordIcon } from "./components/icons/DiscordIcon.js";
36
49
  export { GitHubIcon } from "./components/icons/GitHubIcon.js";
37
50
  export { HaruhimeWordmark, } from "./components/icons/HaruhimeWordmark.js";
38
51
  export { HaruhimeWordmarkLink, } from "./components/icons/HaruhimeWordmarkLink.js";
52
+ // Meta
53
+ export { JsonLd } from "./components/meta/JsonLd.js";
54
+ // osu!
55
+ export { BeatmapStats, } from "./components/osu/BeatmapStats.js";
56
+ export { ModBadge } from "./components/osu/ModBadge.js";
57
+ export { StarRating } from "./components/osu/StarRating.js";
58
+ // Shell
59
+ export { HeaderMenu, } from "./components/shell/HeaderMenu.js";
60
+ export { LinkTabs } from "./components/shell/LinkTabs.js";
39
61
  export { NavLinks } from "./components/shell/NavLinks.js";
40
62
  export { PageShell } from "./components/shell/PageShell.js";
41
63
  export { SiteFooter, } from "./components/shell/SiteFooter.js";
42
64
  export { SiteHeader } from "./components/shell/SiteHeader.js";
65
+ // Tables
66
+ export { Table } from "./components/tables/Table.js";
67
+ export { TBody } from "./components/tables/TBody.js";
68
+ export { Td } from "./components/tables/Td.js";
69
+ export { THead } from "./components/tables/THead.js";
70
+ export { Th } from "./components/tables/Th.js";
71
+ // Utilities
72
+ export { cx } from "./utils/cx.js";
@@ -1,15 +1,18 @@
1
1
  /**
2
2
  * @file src/utils/href.ts
3
3
  * @desc Tells hrefs that leave the app (a URL scheme or a protocol-relative //host) from paths
4
- * inside it, so links can pick a plain <a> or next/link.
4
+ * inside it, so links can pick a plain <a> or next/link. It reads an href the way a
5
+ * browser's URL parser does: leading spaces and control characters don't count, tabs and
6
+ * newlines anywhere are dropped, and a backslash counts as a slash (`/\host` is `//host`).
5
7
  * @author David @dvhsh (https://dvh.sh)
6
8
  * @created Wed Sep 23, 2026
7
- * @modified Wed Sep 23, 2026
9
+ * @modified Mon Sep 28, 2026
8
10
  */
9
11
  /**
10
12
  * @function isExternalHref
11
13
  * @param href {string} a link target
12
- * @returns {boolean} true when the href has a scheme or starts with `//`, so it leaves the app
13
- * and should be a plain `<a>` instead of `next/link`
14
+ * @returns {boolean} true when a browser reads the href as a scheme or a `//host` (so also
15
+ * `/\host`, `\\host` and `" https://host"`): it leaves the app and should be a plain
16
+ * `<a>` instead of `next/link`
14
17
  */
15
18
  export declare const isExternalHref: (href: string) => boolean;
@@ -1,17 +1,33 @@
1
1
  /**
2
2
  * @file src/utils/href.ts
3
3
  * @desc Tells hrefs that leave the app (a URL scheme or a protocol-relative //host) from paths
4
- * inside it, so links can pick a plain <a> or next/link.
4
+ * inside it, so links can pick a plain <a> or next/link. It reads an href the way a
5
+ * browser's URL parser does: leading spaces and control characters don't count, tabs and
6
+ * newlines anywhere are dropped, and a backslash counts as a slash (`/\host` is `//host`).
5
7
  * @author David @dvhsh (https://dvh.sh)
6
8
  * @created Wed Sep 23, 2026
7
- * @modified Wed Sep 23, 2026
9
+ * @modified Mon Sep 28, 2026
8
10
  */
9
- // A URL scheme ("https:", "mailto:") or a protocol-relative "//host".
10
- const EXTERNAL = /^(?:[a-z][a-z\d+.-]*:|\/\/)/i;
11
+ // A URL scheme ("https:", "mailto:") or a protocol-relative "//host", with "\" read as "/".
12
+ const EXTERNAL = /^(?:[a-z][a-z\d+.-]*:|[/\\]{2})/i;
13
+ /**
14
+ * @function parsedStart
15
+ * @param href {string} a link target
16
+ * @returns {string} the href as the URL parser starts reading it: every tab, line feed and
17
+ * carriage return removed, then the leading C0 controls and spaces
18
+ */
19
+ const parsedStart = (href) => {
20
+ const text = href.replace(/[\t\n\r]/g, "");
21
+ let start = 0;
22
+ while (start < text.length && text.charCodeAt(start) <= 0x20)
23
+ start++;
24
+ return text.slice(start);
25
+ };
11
26
  /**
12
27
  * @function isExternalHref
13
28
  * @param href {string} a link target
14
- * @returns {boolean} true when the href has a scheme or starts with `//`, so it leaves the app
15
- * and should be a plain `<a>` instead of `next/link`
29
+ * @returns {boolean} true when a browser reads the href as a scheme or a `//host` (so also
30
+ * `/\host`, `\\host` and `" https://host"`): it leaves the app and should be a plain
31
+ * `<a>` instead of `next/link`
16
32
  */
17
- export const isExternalHref = (href) => EXTERNAL.test(href);
33
+ export const isExternalHref = (href) => EXTERNAL.test(parsedStart(href));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@haruhimemoe/ui",
3
- "version": "0.3.0",
4
- "description": "React components for the haruhime.moe osu! tools on Next.js: the osu!-web-style palette as a Tailwind theme, buttons, cards, form fields, filter controls and the site header and footer.",
3
+ "version": "0.5.0",
4
+ "description": "React components for the haruhime.moe osu! tools on Next.js: the osu!-web-style palette as a Tailwind theme, buttons, cards, form fields and confirms, filter controls, tables, osu! beatmap display pieces and the site header and footer.",
5
5
  "keywords": [
6
6
  "osu",
7
7
  "osu!",
@@ -50,7 +50,7 @@
50
50
  "build": "bun run clean && tsc -p tsconfig.build.json && node -e \"require('node:fs').copyFileSync('src/theme.css','dist/theme.css')\"",
51
51
  "check": "biome check .",
52
52
  "check:fix": "biome check --write .",
53
- "typecheck": "tsc --noEmit",
53
+ "typecheck": "tsc --noEmit && tsc -p scripts/consumer-fixture/tsconfig.json",
54
54
  "test": "vitest run",
55
55
  "test:coverage": "vitest run --coverage",
56
56
  "test:watch": "vitest",
@@ -1,19 +0,0 @@
1
- /**
2
- * @file src/components/shell/AutoLink.tsx
3
- * @desc A link that picks its element from the href: `next/link` for paths inside the app, a
4
- * plain `<a>` for anything with a scheme (https:, mailto:) or a protocol-relative URL.
5
- * @author David @dvhsh (https://dvh.sh)
6
- * @created Wed Sep 23, 2026
7
- * @modified Wed Sep 23, 2026
8
- */
9
- import type { ComponentProps } from "react";
10
- /** Every native `<a>` prop (including `ref`), with `href` required and a plain string. */
11
- export type AutoLinkProps = Omit<ComponentProps<"a">, "href"> & {
12
- href: string;
13
- };
14
- /**
15
- * @function AutoLink
16
- * @param props {AutoLinkProps} native anchor props with a string href
17
- * @returns {JSX.Element} a `next/link` for internal paths, a plain `<a>` for external URLs
18
- */
19
- export declare function AutoLink({ href, ...props }: AutoLinkProps): import("react").JSX.Element;
@@ -1,23 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- /**
3
- * @file src/components/shell/AutoLink.tsx
4
- * @desc A link that picks its element from the href: `next/link` for paths inside the app, a
5
- * plain `<a>` for anything with a scheme (https:, mailto:) or a protocol-relative URL.
6
- * @author David @dvhsh (https://dvh.sh)
7
- * @created Wed Sep 23, 2026
8
- * @modified Wed Sep 23, 2026
9
- */
10
- import Link from "next/link.js";
11
- import { isExternalHref } from "./links.js";
12
- /**
13
- * @function AutoLink
14
- * @param props {AutoLinkProps} native anchor props with a string href
15
- * @returns {JSX.Element} a `next/link` for internal paths, a plain `<a>` for external URLs
16
- */
17
- export function AutoLink({ href, ...props }) {
18
- if (isExternalHref(href))
19
- return _jsx("a", { href: href, ...props });
20
- // Link takes every anchor prop, but types a few handlers (onClick, onMouseEnter) without
21
- // `| undefined`, which exactOptionalPropertyTypes rejects for a spread of optional props.
22
- return _jsx(Link, { href: href, ...props });
23
- }
File without changes