@meith/theme-default 0.4.0 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meith/theme-default",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Meith's default theme — every slot and every token, and the reference for what a complete theme fills.",
5
5
  "license": "LGPL-3.0-or-later",
6
6
  "repository": {
@@ -20,8 +20,8 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@meith/theme-kit": "^0.4.0",
24
- "@meith/ui": "^0.4.0"
23
+ "@meith/theme-kit": "^0.6.0",
24
+ "@meith/ui": "^0.6.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": "^19.2.0"
@@ -0,0 +1,64 @@
1
+ import type { PanelNavIcon } from '@meith/theme-kit'
2
+
3
+ const SHARED = {
4
+ 'aria-hidden': true,
5
+ viewBox: '0 0 16 16',
6
+ fill: 'none',
7
+ stroke: 'currentColor',
8
+ strokeWidth: 1.5,
9
+ strokeLinecap: 'round',
10
+ strokeLinejoin: 'round',
11
+ className: 'size-4 shrink-0',
12
+ } as const
13
+
14
+ const PATHS: Record<PanelNavIcon, string> = {
15
+ overview: 'M2.5 8.5h4l1.5 3 2-6 1.5 3h2.5M2.5 4.5v7a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-7',
16
+ settings: 'M3 4.5h10M3 8h10M3 11.5h10M6 3v3M10.5 6.5v3M5 10v3',
17
+ forums: 'M2.5 3.5h4l1 1.5h6v7a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1Zm3 5.5h5M5.5 11h3',
18
+ groups:
19
+ 'M6 7.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm5.5.5a1.75 1.75 0 1 0 0-3.5M2 13v-.5A3.5 3.5 0 0 1 5.5 9h1A3.5 3.5 0 0 1 10 12.5v.5m1.5-4A3 3 0 0 1 14 12v1',
20
+ users:
21
+ 'M8 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Zm-4.5 5v-.5A3.5 3.5 0 0 1 7 9h2a3.5 3.5 0 0 1 3.5 3.5v.5',
22
+ content:
23
+ 'M4 2.5h5l3 3v8a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1Zm5 0v3h3M5.5 8.5h5M5.5 11h3',
24
+ antispam: 'M8 2 3 4v4c0 3 2.2 5.2 5 6 2.8-.8 5-3 5-6V4Zm-2 5.5 1.5 1.5L10.5 6',
25
+ themes:
26
+ 'M8 2.5a5.5 5.5 0 1 0 0 11c.9 0 1.5-.7 1.5-1.5 0-.4-.2-.7-.4-1a1.4 1.4 0 0 1 1-2.4h1.3A2.6 2.6 0 0 0 14 6C14 4 11.3 2.5 8 2.5Zm-2.5 3.7h.01M9 4.7h.01M11.3 6.4h.01M5.2 9.2h.01',
27
+ plugins:
28
+ 'M6 2.8a1.3 1.3 0 0 1 2.6 0V4h2.2a.7.7 0 0 1 .7.7V7h1.2a1.3 1.3 0 0 1 0 2.6H11.5v2.2a.7.7 0 0 1-.7.7H8.6v-1.2a1.3 1.3 0 0 0-2.6 0V12.5H3.8a.7.7 0 0 1-.7-.7V4.7A.7.7 0 0 1 3.8 4H6Z',
29
+ tokens:
30
+ 'M9.8 3.2a3.2 3.2 0 1 1-3 5.3L3 12.3v1.2h1.5l.6-.7h1.2v-1.2h1.2l1-1a3.2 3.2 0 0 0 1.3-7.4Zm.9 2.6h.01',
31
+ system:
32
+ 'M3 3.5h10a.5.5 0 0 1 .5.5v2.5a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V4a.5.5 0 0 1 .5-.5Zm0 5.5h10a.5.5 0 0 1 .5.5V12a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V9.5A.5.5 0 0 1 3 9Zm2 1.75h.01M5 5.25h.01',
33
+ log: 'M4.5 2.5h7a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1Zm1.5 3h4m-4 2.5h4m-4 2.5h2.5',
34
+ queue:
35
+ 'M2.5 9.5h3l1 2h3l1-2h3M2.5 9.5 4 4a1 1 0 0 1 1-.8h6a1 1 0 0 1 1 .8l1.5 5.5v2.5a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1Z',
36
+ reports: 'M4 14V3m0 0h7.5l-1.5 3 1.5 3H4',
37
+ ip: 'M8 2.5a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Zm0 0c-1.6 1.4-2.4 3.3-2.4 5.5S6.4 12.1 8 13.5c1.6-1.4 2.4-3.3 2.4-5.5S9.6 3.9 8 2.5ZM2.8 6h10.4M2.8 10h10.4',
38
+ messages:
39
+ 'M2.5 5a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1Zm.4-.4 5.1 4 5.1-4',
40
+ notifications:
41
+ 'M8 2.5a3.5 3.5 0 0 0-3.5 3.5c0 3-1 4-1 4h9s-1-1-1-4A3.5 3.5 0 0 0 8 2.5ZM6.8 12a1.3 1.3 0 0 0 2.4 0',
42
+ subscriptions: 'M4 2.8h8a.5.5 0 0 1 .5.5v10L8 10.6l-4.5 2.7v-10a.5.5 0 0 1 .5-.5Z',
43
+ profile:
44
+ 'M8 8.2a2.6 2.6 0 1 0 0-5.2 2.6 2.6 0 0 0 0 5.2ZM3.5 13.2v-.7A3.3 3.3 0 0 1 6.8 9.2h2.4a3.3 3.3 0 0 1 3.3 3.3v.7',
45
+ avatar:
46
+ 'M3.5 2.8h9a.7.7 0 0 1 .7.7v9a.7.7 0 0 1-.7.7h-9a.7.7 0 0 1-.7-.7v-9a.7.7 0 0 1 .7-.7Zm.3 9.6 3.4-3.9 2 2.2 1.6-1.7 1.6 1.9M6 6.6h.01',
47
+ signature:
48
+ 'M2.8 11.5c2.5 0 3-6.5 4.7-6.5 1.2 0 .5 4.5 1.9 4.5 1 0 1.3-2 2.3-2 .8 0 1 1.3 1.5 1.3M3 13.5h10',
49
+ buddies:
50
+ 'M5.8 7.6a2.1 2.1 0 1 0 0-4.2 2.1 2.1 0 0 0 0 4.2ZM2 13v-.4a3.3 3.3 0 0 1 3.3-3.3h1a3.3 3.3 0 0 1 3.3 3.3v.4m2.4-7.5v3.4m1.7-1.7h-3.4',
51
+ security:
52
+ 'M4.5 7V5.5a3.5 3.5 0 1 1 7 0V7m-7.7 0h8.4a.8.8 0 0 1 .8.8v5a.8.8 0 0 1-.8.8H3.8a.8.8 0 0 1-.8-.8v-5a.8.8 0 0 1 .8-.8Z',
53
+ }
54
+
55
+ export function PanelIcon({ icon }: { icon: PanelNavIcon }) {
56
+ const path = PATHS[icon]
57
+ if (path === undefined) return null
58
+
59
+ return (
60
+ <svg {...SHARED}>
61
+ <path d={path} />
62
+ </svg>
63
+ )
64
+ }
@@ -40,7 +40,7 @@ export function Header({ boardTitle, homeHref, navigation, logo, children }: Hea
40
40
  {navigation.length > 0 && (
41
41
  <nav aria-label="Board sections" className="border-t border-border bg-surface">
42
42
  <div className={PAGE}>
43
- <ul className="-mx-4 flex items-stretch gap-1 overflow-x-auto px-4 text-sm sm:-mx-6 sm:px-6">
43
+ <ul className="-mx-4 flex items-stretch gap-1 overflow-x-auto px-4 text-sm [mask-image:linear-gradient(to_right,black_calc(100%-1.5rem),transparent)] sm:-mx-6 sm:px-6 sm:[mask-image:none]">
44
44
  {navigation.map((item) => (
45
45
  <li key={item.href} className="shrink-0">
46
46
  <a
@@ -3,7 +3,14 @@ import type { PaginationModel } from '@meith/theme-kit'
3
3
 
4
4
  import { NUMERIC } from '../shared'
5
5
 
6
- export function Pagination({ page, pageCount, pages, previousHref, nextHref }: PaginationModel) {
6
+ export function Pagination({
7
+ page,
8
+ pageCount,
9
+ pageCountIsExact,
10
+ pages,
11
+ previousHref,
12
+ nextHref,
13
+ }: PaginationModel) {
7
14
  if (pageCount <= 1 && previousHref === null && nextHref === null) return null
8
15
 
9
16
  const step = cn(buttonVariants({ variant: 'outline', size: 'sm' }), 'min-w-20')
@@ -54,9 +61,8 @@ export function Pagination({ page, pageCount, pages, previousHref, nextHref }: P
54
61
  })}
55
62
 
56
63
  <li className="ml-1 text-xs whitespace-nowrap text-muted-foreground">
57
- <span className="sr-only">Page </span>
58
64
  <span className={NUMERIC}>
59
- {page} of {pageCount}
65
+ {pageCountIsExact ? `Page ${page} of ${pageCount}` : `Page ${page}`}
60
66
  </span>
61
67
  </li>
62
68
  </ol>
@@ -1,10 +1,13 @@
1
1
  import { Disclosure, cn } from '@meith/ui'
2
2
  import type { PanelNavItemModel, PanelNavModel } from '@meith/theme-kit'
3
3
 
4
+ import { PanelIcon } from '../panel-icons'
5
+
4
6
  const ITEM =
5
- 'flex items-baseline gap-2 rounded-lg px-3 py-2 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
7
+ 'relative flex items-center gap-2.5 rounded-lg px-2.5 py-2 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
6
8
 
7
- const HERE = 'bg-secondary font-semibold text-foreground'
9
+ const HERE =
10
+ 'bg-secondary font-semibold text-foreground before:absolute before:top-1.5 before:bottom-1.5 before:-left-2.5 before:w-0.5 before:rounded-full before:bg-primary'
8
11
  const OPEN = 'font-semibold text-foreground hover:bg-accent'
9
12
  const ELSEWHERE = 'text-muted-foreground hover:bg-accent hover:text-foreground'
10
13
 
@@ -13,7 +16,7 @@ function Count({ count }: { count: number }) {
13
16
  <>
14
17
  <span
15
18
  aria-hidden="true"
16
- className="ml-auto rounded-full bg-surface px-2 py-0.5 text-xs font-semibold tabular-nums text-foreground"
19
+ className="ml-auto min-w-5 rounded-full bg-primary px-1.5 py-0.5 text-center text-xs font-semibold tabular-nums text-primary-foreground"
17
20
  >
18
21
  {count > 99 ? '99+' : count}
19
22
  </span>
@@ -25,7 +28,12 @@ function Count({ count }: { count: number }) {
25
28
  function Item({ item, className }: { item: PanelNavItemModel; className: string }) {
26
29
  const body = (
27
30
  <>
28
- <span className="min-w-0 flex-1">{item.title}</span>
31
+ {item.icon !== null && (
32
+ <span className="text-muted-foreground/80">
33
+ <PanelIcon icon={item.icon} />
34
+ </span>
35
+ )}
36
+ <span className="min-w-0 flex-1 truncate">{item.title}</span>
29
37
  {item.count !== null && <Count count={item.count} />}
30
38
  </>
31
39
  )
@@ -44,7 +52,10 @@ function Item({ item, className }: { item: PanelNavItemModel; className: string
44
52
  className={cn(ITEM, className)}
45
53
  {...(item.current === null
46
54
  ? {}
47
- : { 'aria-current': item.current === 'here' ? ('page' as const) : ('true' as const) })}
55
+ : {
56
+ 'aria-current':
57
+ item.current === 'here' ? ('page' as const) : ('true' as const),
58
+ })}
48
59
  >
49
60
  {body}
50
61
  </a>
@@ -54,7 +65,7 @@ function Item({ item, className }: { item: PanelNavItemModel; className: string
54
65
  function Sections({ label, sections }: Pick<PanelNavModel, 'label' | 'sections'>) {
55
66
  return (
56
67
  <nav aria-label={label} className="text-sm">
57
- <ul className="flex flex-col gap-0.5">
68
+ <ul className="flex flex-col gap-0.5 pl-2.5">
58
69
  {sections.map((section) => (
59
70
  <li
60
71
  key={section.href}
@@ -68,7 +79,7 @@ function Sections({ label, sections }: Pick<PanelNavModel, 'label' | 'sections'>
68
79
  />
69
80
 
70
81
  {section.isOpen && section.children.length > 0 && (
71
- <ul className="mt-0.5 ml-3 flex flex-col gap-0.5 border-l border-border pl-2">
82
+ <ul className="mt-0.5 ml-4 flex flex-col gap-0.5 border-l border-border pl-2.5">
72
83
  {section.children.map((child) => (
73
84
  <li key={child.href}>
74
85
  <Item
@@ -89,16 +100,17 @@ function Sections({ label, sections }: Pick<PanelNavModel, 'label' | 'sections'>
89
100
  export function PanelNav({ label, sections, currentTitle }: PanelNavModel) {
90
101
  return (
91
102
  <>
92
- <Disclosure
93
- summary={label}
94
- className="lg:hidden"
95
- contentClassName="p-2"
96
- {...(currentTitle === null ? {} : { aside: currentTitle })}
97
- >
98
- <Sections label={label} sections={sections} />
99
- </Disclosure>
103
+ <div className="sticky top-0 z-20 -mx-4 bg-background/95 px-4 py-2 backdrop-blur-sm sm:-mx-6 sm:px-6 lg:hidden">
104
+ <Disclosure
105
+ summary={label}
106
+ contentClassName="p-2"
107
+ {...(currentTitle === null ? {} : { aside: currentTitle })}
108
+ >
109
+ <Sections label={label} sections={sections} />
110
+ </Disclosure>
111
+ </div>
100
112
 
101
- <div className="hidden rounded-lg border border-border bg-card p-2 shadow-elevation lg:block">
113
+ <div className="hidden rounded-xl border border-border bg-card p-2 shadow-elevation lg:block">
102
114
  <Sections label={label} sections={sections} />
103
115
  </div>
104
116
  </>
@@ -3,22 +3,34 @@ import type { PanelShellModel } from '@meith/theme-kit'
3
3
  import { MUTED_LINK } from '../shared'
4
4
 
5
5
  const RAIL =
6
- 'flex flex-col gap-4 px-6 pt-6 lg:sticky lg:w-56 lg:shrink-0 lg:self-start lg:py-8 lg:pr-0'
6
+ 'flex flex-col gap-4 px-4 pt-4 sm:px-6 lg:sticky lg:w-60 lg:shrink-0 lg:self-start lg:py-8 lg:pr-0'
7
7
 
8
- export function PanelShell({ panel, links, linksLabel, regions, children }: PanelShellModel) {
8
+ export function PanelShell({
9
+ panel,
10
+ links,
11
+ linksLabel,
12
+ regions,
13
+ children,
14
+ }: PanelShellModel) {
9
15
  return (
10
16
  <div className="mx-auto flex w-full max-w-6xl flex-1 flex-col lg:flex-row">
11
17
  <aside className={`${RAIL} ${panel === 'admincp' ? 'lg:top-14' : 'lg:top-6'}`}>
12
18
  {regions.nav}
13
19
 
14
20
  {links.length > 0 && (
15
- <nav aria-label={linksLabel} className="border-t border-border pt-3 text-sm">
16
- <ul className="flex flex-col gap-0.5">
21
+ <nav
22
+ aria-label={linksLabel}
23
+ className="hidden rounded-xl border border-border bg-card p-2 shadow-elevation lg:block"
24
+ >
25
+ <p className="px-2.5 pt-1 pb-1.5 text-xs font-medium tracking-wide text-muted-foreground uppercase">
26
+ {linksLabel}
27
+ </p>
28
+ <ul className="flex flex-col gap-0.5 text-sm">
17
29
  {links.map((link) => (
18
30
  <li key={link.href}>
19
31
  <a
20
32
  href={link.href}
21
- className={`block rounded-md px-3 py-1.5 ${MUTED_LINK} hover:bg-muted/60 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`}
33
+ className={`block rounded-lg px-2.5 py-1.5 ${MUTED_LINK} hover:bg-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`}
22
34
  >
23
35
  {link.label}
24
36
  </a>
@@ -30,6 +42,19 @@ export function PanelShell({ panel, links, linksLabel, regions, children }: Pane
30
42
  </aside>
31
43
 
32
44
  <div className="min-w-0 flex-1">{children}</div>
45
+
46
+ {links.length > 0 && (
47
+ <nav
48
+ aria-label={linksLabel}
49
+ className="flex flex-wrap gap-x-4 gap-y-1 px-4 pt-2 text-sm sm:px-6 lg:hidden"
50
+ >
51
+ {links.map((link) => (
52
+ <a key={link.href} href={link.href} className={MUTED_LINK}>
53
+ {link.label}
54
+ </a>
55
+ ))}
56
+ </nav>
57
+ )}
33
58
  </div>
34
59
  )
35
60
  }
@@ -25,6 +25,7 @@ export function SearchResults({
25
25
  newSearchHref,
26
26
  within,
27
27
  refine,
28
+ regions,
28
29
  }: SearchResultsModel) {
29
30
  return (
30
31
  <main
@@ -71,12 +72,16 @@ export function SearchResults({
71
72
  </CardRows>
72
73
  )}
73
74
 
74
- {nextHref !== null && (
75
- <CardFooter>
76
- <a href={nextHref} className={`font-medium text-foreground ${LINK}`}>
77
- {nextLabel}
78
- </a>
79
- </CardFooter>
75
+ {regions?.pagination !== undefined ? (
76
+ <CardFooter>{regions.pagination}</CardFooter>
77
+ ) : (
78
+ nextHref !== null && (
79
+ <CardFooter>
80
+ <a href={nextHref} className={`font-medium text-foreground ${LINK}`}>
81
+ {nextLabel} →
82
+ </a>
83
+ </CardFooter>
84
+ )
80
85
  )}
81
86
  </Card>
82
87