@meith/theme-default 0.15.0 → 0.17.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.15.0",
3
+ "version": "0.17.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.15.0",
24
- "@meith/ui": "^0.15.0"
23
+ "@meith/theme-kit": "^0.17.0",
24
+ "@meith/ui": "^0.17.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": "^19.2.0"
package/src/copy.ts CHANGED
@@ -178,6 +178,7 @@ export function panelPageCopy(t: Translator): SlotCopy {
178
178
  export function postActionsCopy(t: Translator): SlotCopy {
179
179
  return copyFor(t, [
180
180
  'default.postActions.edit',
181
+ 'default.postActions.history',
181
182
  'default.postActions.moderate',
182
183
  'default.postActions.nav',
183
184
  'default.postActions.quote',
@@ -85,6 +85,7 @@
85
85
  "default.panelNav.waiting": "waiting",
86
86
  "default.panelPage.back": "←",
87
87
  "default.postActions.edit": "Edit",
88
+ "default.postActions.history": "History",
88
89
  "default.postActions.moderate": "Moderate",
89
90
  "default.postActions.nav": "Post actions",
90
91
  "default.postActions.quote": "Quote",
@@ -0,0 +1,72 @@
1
+ 'use client'
2
+
3
+ import {
4
+ applyEditorTag,
5
+ type EditorTag,
6
+ type EditorToolbarModel,
7
+ type SlotCopy,
8
+ } from '@meith/theme-kit'
9
+
10
+ const GLYPHS: Readonly<Record<EditorTag, string>> = {
11
+ bold: 'B',
12
+ italic: 'I',
13
+ strikethrough: 'S',
14
+ link: 'Link',
15
+ quote: '“”',
16
+ code: '</>',
17
+ spoiler: 'Spoiler',
18
+ bulletedList: '•',
19
+ numberedList: '1.',
20
+ heading: 'H',
21
+ }
22
+
23
+ const BUTTON =
24
+ 'min-w-8 rounded px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-background hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
25
+
26
+ function runTag(textareaId: string, tag: EditorTag, placeholder: string | null): void {
27
+ const field = document.getElementById(textareaId)
28
+ if (field instanceof HTMLTextAreaElement) applyEditorTag(field, tag, placeholder)
29
+ }
30
+
31
+ export function EditorToolbar({
32
+ textareaId,
33
+ groupLabel,
34
+ buttons,
35
+ attachment,
36
+ }: EditorToolbarModel & { copy: SlotCopy }) {
37
+ return (
38
+ <div
39
+ role="group"
40
+ aria-label={groupLabel}
41
+ className="flex flex-wrap gap-0.5 border-b border-border bg-muted/40 p-2"
42
+ >
43
+ {buttons.map((button) => (
44
+ <button
45
+ key={button.tag}
46
+ type="button"
47
+ onMouseDown={(event) => event.preventDefault()}
48
+ onClick={() => runTag(textareaId, button.tag, button.placeholder)}
49
+ title={button.title}
50
+ aria-label={button.label}
51
+ {...(button.keyShortcut === null ? {} : { 'aria-keyshortcuts': button.keyShortcut })}
52
+ className={BUTTON}
53
+ >
54
+ <span aria-hidden="true">{GLYPHS[button.tag]}</span>
55
+ </button>
56
+ ))}
57
+
58
+ {attachment !== null && (
59
+ <button
60
+ type="button"
61
+ onMouseDown={(event) => event.preventDefault()}
62
+ onClick={() => document.getElementById(attachment.inputId)?.click()}
63
+ title={attachment.label}
64
+ aria-label={attachment.label}
65
+ className={BUTTON}
66
+ >
67
+ <span aria-hidden="true">{attachment.label}</span>
68
+ </button>
69
+ )}
70
+ </div>
71
+ )
72
+ }
@@ -17,6 +17,7 @@ export function PostActions({
17
17
  const reader: Action[] = [
18
18
  actions.quoteHref === null ? null : { href: actions.quoteHref, label: c('quote') },
19
19
  actions.editHref === null ? null : { href: actions.editHref, label: c('edit') },
20
+ actions.historyHref == null ? null : { href: actions.historyHref, label: c('history') },
20
21
  actions.rateHref === null ? null : { href: actions.rateHref, label: c('rate') },
21
22
  actions.reportHref === null ? null : { href: actions.reportHref, label: c('report') },
22
23
  ].filter((action): action is Action => action !== null)
@@ -0,0 +1,9 @@
1
+ 'use client'
2
+
3
+ import type { QuickReplyModel, SlotCopy } from '@meith/theme-kit'
4
+
5
+ export function QuickReply({ placeholder, children }: QuickReplyModel & { copy: SlotCopy }) {
6
+ if (children === undefined) return null
7
+
8
+ return <section aria-label={placeholder}>{children}</section>
9
+ }
package/src/theme.ts CHANGED
@@ -37,6 +37,7 @@ import { BoardIndex } from './slots/board-index'
37
37
  import { BoardStats } from './slots/board-stats'
38
38
  import { CategoryBlock } from './slots/category-block'
39
39
  import { DiscoveryView } from './slots/discovery-view'
40
+ import { EditorToolbar } from './slots/editor-toolbar'
40
41
  import { ErrorNotice } from './slots/error-notice'
41
42
  import { Footer } from './slots/footer'
42
43
  import { ForumDisplay } from './slots/forum-display'
@@ -55,6 +56,7 @@ import { PanelShell } from './slots/panel-shell'
55
56
  import { PostActions } from './slots/post-actions'
56
57
  import { PostBit } from './slots/post-bit'
57
58
  import { PostForm } from './slots/post-form'
59
+ import { QuickReply } from './slots/quick-reply'
58
60
  import { RedirectNotice } from './slots/redirect-notice'
59
61
  import { SearchForm } from './slots/search-form'
60
62
  import { SearchResults } from './slots/search-results'
@@ -93,8 +95,10 @@ export const defaultTheme = defineTheme({
93
95
  ThreadView,
94
96
  PostBit,
95
97
  PostActions,
98
+ QuickReply,
96
99
 
97
100
  PostForm,
101
+ EditorToolbar,
98
102
 
99
103
  MemberProfile,
100
104