diff --git a/extensions/messages/js/src/forum/components/MessagesPage.tsx b/extensions/messages/js/src/forum/components/MessagesPage.tsx index 8c3ce07232..4abfd0e6ff 100644 --- a/extensions/messages/js/src/forum/components/MessagesPage.tsx +++ b/extensions/messages/js/src/forum/components/MessagesPage.tsx @@ -2,13 +2,13 @@ import app from 'flarum/forum/app'; import Page, { IPageAttrs } from 'flarum/common/components/Page'; import PageStructure from 'flarum/forum/components/PageStructure'; import Mithril from 'mithril'; -import Icon from 'flarum/common/components/Icon'; import DialogList from './DialogList'; import Dialog from '../../common/models/Dialog'; import LoadingIndicator from 'flarum/common/components/LoadingIndicator'; import Stream from 'flarum/common/utils/Stream'; import InfoTile from 'flarum/common/components/InfoTile'; import MessagesSidebar from './MessagesSidebar'; +import MessagesPageHero from './MessagesPageHero'; import DialogSection from './DialogSection'; import listItems from 'flarum/common/helpers/listItems'; import ItemList from 'flarum/common/utils/ItemList'; @@ -110,18 +110,7 @@ export default class MessagesPage -
-
-

- {app.translator.trans('flarum-messages.forum.messages_page.hero.title')} -

-
{app.translator.trans('flarum-messages.forum.messages_page.hero.subtitle')}
-
-
- - ); + return ; } contentItems() { diff --git a/extensions/messages/js/src/forum/components/MessagesPageHero.tsx b/extensions/messages/js/src/forum/components/MessagesPageHero.tsx new file mode 100644 index 0000000000..a8d7d2062e --- /dev/null +++ b/extensions/messages/js/src/forum/components/MessagesPageHero.tsx @@ -0,0 +1,38 @@ +import app from 'flarum/forum/app'; +import Hero, { IHeroAttrs } from 'flarum/forum/components/Hero'; +import Icon from 'flarum/common/components/Icon'; +import ItemList from 'flarum/common/utils/ItemList'; + +import type Mithril from 'mithril'; + +export interface IMessagesPageHeroAttrs extends IHeroAttrs {} + +export default class MessagesPageHero extends Hero { + className(): string { + return 'MessagesPageHero'; + } + + bodyItems(): ItemList { + const items = new ItemList(); + + items.add('content',
{this.contentItems().toArray()}
, 80); + + return items; + } + + contentItems(): ItemList { + const items = new ItemList(); + + items.add( + 'title', +

+ {app.translator.trans('flarum-messages.forum.messages_page.hero.title')} +

, + 100 + ); + + items.add('subtitle',
{app.translator.trans('flarum-messages.forum.messages_page.hero.subtitle')}
, 90); + + return items; + } +} diff --git a/extensions/tags/js/src/forum/components/TagHero.js b/extensions/tags/js/src/forum/components/TagHero.js deleted file mode 100644 index 5e77ad302d..0000000000 --- a/extensions/tags/js/src/forum/components/TagHero.js +++ /dev/null @@ -1,53 +0,0 @@ -import Component from 'flarum/common/Component'; -import textContrastClass from 'flarum/common/helpers/textContrastClass'; -import tagIcon from '../../common/helpers/tagIcon'; -import classList from 'flarum/common/utils/classList'; -import ItemList from 'flarum/common/utils/ItemList'; -import Mithril from 'mithril'; - -export default class TagHero extends Component { - view() { - const tag = this.attrs.model; - const color = tag.color(); - - return ( -
-
{this.viewItems().toArray()}
-
- ); - } - - /** - * @returns {ItemList} - */ - viewItems() { - const items = new ItemList(); - - items.add('content',
{this.contentItems().toArray()}
, 80); - - return items; - } - - /** - * @returns {ItemList} - */ - contentItems() { - const items = new ItemList(); - const tag = this.attrs.model; - - items.add( - 'tag-title', -

- {tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()} -

, - 100 - ); - - items.add('tag-subtitle',
{tag.description()}
, 90); - - return items; - } -} diff --git a/extensions/tags/js/src/forum/components/TagHero.tsx b/extensions/tags/js/src/forum/components/TagHero.tsx new file mode 100644 index 0000000000..04ebf767a3 --- /dev/null +++ b/extensions/tags/js/src/forum/components/TagHero.tsx @@ -0,0 +1,56 @@ +import Hero, { IHeroAttrs } from 'flarum/forum/components/Hero'; +import textContrastClass from 'flarum/common/helpers/textContrastClass'; +import tagIcon from '../../common/helpers/tagIcon'; +import classList from 'flarum/common/utils/classList'; +import ItemList from 'flarum/common/utils/ItemList'; + +import type Tag from '../../common/models/Tag'; +import type Mithril from 'mithril'; + +export interface ITagHeroAttrs extends IHeroAttrs { + model: Tag; +} + +export default class TagHero extends Hero { + className(): string { + const tag = this.attrs.model; + const color = tag.color(); + + return classList('TagHero', { + 'TagHero--colored': color, + [textContrastClass(color)]: color, + }); + } + + style(): Record | undefined { + const tag = this.attrs.model; + const color = tag.color(); + + return color ? { '--hero-bg': color } : undefined; + } + + bodyItems(): ItemList { + const items = new ItemList(); + + items.add('content',
{this.contentItems().toArray()}
, 80); + + return items; + } + + contentItems(): ItemList { + const items = new ItemList(); + const tag = this.attrs.model; + + items.add( + 'tag-title', +

+ {tag.icon() && tagIcon(tag, {}, { useColor: false })} {tag.name()} +

, + 100 + ); + + items.add('tag-subtitle',
{tag.description()}
, 90); + + return items; + } +} diff --git a/framework/core/js/src/forum/components/DiscussionHero.js b/framework/core/js/src/forum/components/DiscussionHero.js deleted file mode 100644 index 5e5f2534a6..0000000000 --- a/framework/core/js/src/forum/components/DiscussionHero.js +++ /dev/null @@ -1,41 +0,0 @@ -import Component from '../../common/Component'; -import ItemList from '../../common/utils/ItemList'; -import listItems from '../../common/helpers/listItems'; - -/** - * The `DiscussionHero` component displays the hero on a discussion page. - * - * ### attrs - * - * - `discussion` - */ -export default class DiscussionHero extends Component { - view() { - return ( -
-
-
    {listItems(this.items().toArray())}
-
-
- ); - } - - /** - * Build an item list for the contents of the discussion hero. - * - * @return {ItemList} - */ - items() { - const items = new ItemList(); - const discussion = this.attrs.discussion; - const badges = discussion.badges().toArray(); - - if (badges.length) { - items.add('badges',
    {listItems(badges)}
, 10); - } - - items.add('title',

{discussion.title()}

); - - return items; - } -} diff --git a/framework/core/js/src/forum/components/DiscussionHero.tsx b/framework/core/js/src/forum/components/DiscussionHero.tsx new file mode 100644 index 0000000000..eb7bd4dbdd --- /dev/null +++ b/framework/core/js/src/forum/components/DiscussionHero.tsx @@ -0,0 +1,39 @@ +import Hero, { IHeroAttrs } from './Hero'; +import ItemList from '../../common/utils/ItemList'; +import listItems from '../../common/helpers/listItems'; + +import type Discussion from '../../common/models/Discussion'; +import type Mithril from 'mithril'; + +export interface IDiscussionHeroAttrs extends IHeroAttrs { + discussion: Discussion; +} + +export default class DiscussionHero extends Hero { + className(): string { + return 'DiscussionHero'; + } + + bodyItems(): ItemList { + const items = new ItemList(); + + items.add('items',
    {listItems(this.items().toArray())}
, 100); + + return items; + } + + items(): ItemList { + const items = new ItemList(); + + const discussion = this.attrs.discussion; + const badges = discussion.badges().toArray(); + + if (badges.length) { + items.add('badges',
    {listItems(badges)}
, 10); + } + + items.add('title',

{discussion.title()}

); + + return items; + } +} diff --git a/framework/core/js/src/forum/components/Hero.tsx b/framework/core/js/src/forum/components/Hero.tsx new file mode 100644 index 0000000000..5338621ff2 --- /dev/null +++ b/framework/core/js/src/forum/components/Hero.tsx @@ -0,0 +1,71 @@ +import app from '../app'; +import Component from '../../common/Component'; +import classList from '../../common/utils/classList'; + +import ItemList from '../../common/utils/ItemList'; +import type Mithril from 'mithril'; + +export interface IHeroAttrs {} + +export default abstract class Hero extends Component { + /** + * Defines the primary CSS class name for the hero component's root element. + * Subclasses MUST implement this method to provide a specific class name. + * + * @example + * ```ts + * className(): string { + * return 'FoobarHero'; + * } + * ``` + */ + abstract className(): string; + + /** + * Defines the child elements that will be rendered within the main container of the hero. + * Subclasses MUST implement this method to define the specific content of the hero. + * + * @example + * ```tsx + * bodyItems(): ItemList { + * const items = new ItemList(); + * items.add('title',

Welcome!

); + * return items; + * } + * ``` + */ + abstract bodyItems(): ItemList; + + /** + * Defines inline CSS styles for the hero component's root element. + * Subclasses can override this method to provide custom styles. + * + * @example + * ```ts + * style(): Record { + * return { + * backgroundColor: '#e7672e', + * }; + * } + * ``` + */ + style(): Record | undefined { + return undefined; + } + + view(): Mithril.Vnode | null { + return ( +
+ {this.viewItems().toArray()} +
+ ); + } + + viewItems(): ItemList { + const items = new ItemList(); + + items.add('container',
{this.bodyItems().toArray()}
, 100); + + return items; + } +} diff --git a/framework/core/js/src/forum/components/WelcomeHero.tsx b/framework/core/js/src/forum/components/WelcomeHero.tsx index fc87b63d89..2d4ab34354 100644 --- a/framework/core/js/src/forum/components/WelcomeHero.tsx +++ b/framework/core/js/src/forum/components/WelcomeHero.tsx @@ -1,10 +1,10 @@ import app from '../app'; -import Component from '../../common/Component'; +import Hero, { IHeroAttrs } from './Hero'; import Button from '../../common/components/Button'; import type Mithril from 'mithril'; import ItemList from '../../common/utils/ItemList'; -export interface IWelcomeHeroAttrs {} +export interface IWelcomeHeroAttrs extends IHeroAttrs {} const LOCAL_STORAGE_KEY = 'welcomeHidden'; @@ -12,26 +12,18 @@ const LOCAL_STORAGE_KEY = 'welcomeHidden'; * The `WelcomeHero` component displays a hero that welcomes the user to the * forum. */ -export default class WelcomeHero extends Component { - oninit(vnode: Mithril.Vnode) { - super.oninit(vnode); +export default class WelcomeHero extends Hero { + className(): string { + return 'WelcomeHero'; } - view(vnode: Mithril.Vnode) { + view() { if (this.isHidden()) return null; - const slideUp = () => { - this.$().slideUp(this.hide.bind(this)); - }; - - return ( -
-
{this.viewItems().toArray()}
-
- ); + return super.view(); } - viewItems(): ItemList { + bodyItems(): ItemList { const items = new ItemList(); const slideUp = () => {