From 4e4b015288b14af1001533702dca94993fe8b028 Mon Sep 17 00:00:00 2001 From: TechQuery Date: Mon, 22 Jan 2024 01:42:14 +0800 Subject: [PATCH] [optimize] upgrade models, components & pages of Community & Acitivity --- .eslintrc.json | 4 +- package.json | 2 +- pnpm-lock.yaml | 8 +- .../{Feature.less => Feature.module.less} | 0 source/component/Feature.tsx | 38 ++-- source/component/MonthCalendar.tsx | 165 ++++++++++++++++++ source/component/SessionBox.tsx | 55 +++--- source/model/Activity.ts | 17 +- source/model/App.ts | 9 +- source/model/service.ts | 6 +- source/page/Activity.tsx | 105 +++++------ source/page/Community.tsx | 50 +++--- source/page/data.ts | 2 +- source/page/index.tsx | 10 +- tsconfig.json | 3 - 15 files changed, 307 insertions(+), 167 deletions(-) rename source/component/{Feature.less => Feature.module.less} (100%) create mode 100644 source/component/MonthCalendar.tsx diff --git a/.eslintrc.json b/.eslintrc.json index 0040e16..7f37e95 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,7 +21,9 @@ "prefer-const": "warn", "no-unused-vars": "warn", "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-unsafe-declaration-merging": "warn", - "@typescript-eslint/explicit-module-boundary-types": "off" + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/ban-ts-comment": "warn" } } diff --git a/package.json b/package.json index e334d8c..457d712 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "*.{html,md,css,less,js,ts,tsx,json}": "prettier --write" }, "dependencies": { - "boot-cell": "^2.0.0-beta.7", + "boot-cell": "^2.0.0-beta.8", "browser-unhandled-rejection": "^1.0.2", "cell-router": "^3.0.0-rc.5", "classnames": "^2.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0c94d1..c915141 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,8 +6,8 @@ settings: dependencies: boot-cell: - specifier: ^2.0.0-beta.7 - version: 2.0.0-beta.7(typescript@5.3.3) + specifier: ^2.0.0-beta.8 + version: 2.0.0-beta.8(typescript@5.3.3) browser-unhandled-rejection: specifier: ^1.0.2 version: 1.0.2 @@ -3015,8 +3015,8 @@ packages: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true - /boot-cell@2.0.0-beta.7(typescript@5.3.3): - resolution: {integrity: sha512-W7m0hso7hRh86g+R9nNUL4iiBQpQP5rkX0adoZIhQmIliq671kpobAAvc7D0XBy+04teXc+TMlLeGvzP7UZfdg==} + /boot-cell@2.0.0-beta.8(typescript@5.3.3): + resolution: {integrity: sha512-qSxE0+N3XGjzYIXrO3j1nPvuVmgfApONcRD9hTqQzjn36W/QBgW4eYdqnOcdUqu1vMs8E6FpS2SXEk3thbQS3Q==} peerDependencies: '@fortawesome/fontawesome-free': ^6 '@nuintun/qrcode': ^3 diff --git a/source/component/Feature.less b/source/component/Feature.module.less similarity index 100% rename from source/component/Feature.less rename to source/component/Feature.module.less diff --git a/source/component/Feature.tsx b/source/component/Feature.tsx index eeb3943..84aff02 100644 --- a/source/component/Feature.tsx +++ b/source/component/Feature.tsx @@ -1,26 +1,28 @@ -import { WebCellElement, createCell } from 'web-cell'; +import { JsxChildren } from 'dom-renderer'; +import { FC } from 'web-cell'; import classNames from 'classnames'; -import { Image } from 'boot-cell/source/Media/Image'; +import { Image } from 'boot-cell'; -import style from './Feature.less'; +import * as style from './Feature.module.less'; -export interface FeatureProps { +export interface FeatureProps extends Record<'title' | 'summary', JsxChildren> { reverse?: boolean; - title: WebCellElement; - summary: WebCellElement; logo: string; } -export function Feature({ reverse, title, summary, logo }: FeatureProps) { - return ( -
-
-

{title}

-

{summary}

-
-
- -
+export const Feature: FC = ({ + reverse, + title, + summary, + logo +}) => ( +
+
+

{title}

+

{summary}

- ); -} +
+ +
+
+); diff --git a/source/component/MonthCalendar.tsx b/source/component/MonthCalendar.tsx new file mode 100644 index 0000000..56c95e3 --- /dev/null +++ b/source/component/MonthCalendar.tsx @@ -0,0 +1,165 @@ +import { Badge, Button, ButtonProps, Table, TableProps } from 'boot-cell'; +import classNames from 'classnames'; +import { JsxChildren } from 'dom-renderer'; +import { computed, observable } from 'mobx'; +import { WebCell, attribute, component, observer } from 'web-cell'; +import { + Day, + TimeData, + changeMonth, + formatDate, + splitArray +} from 'web-utility'; + +// import { text2color } from './color'; +// import { OverlayBox } from './OverlayBox'; + +export interface DateData { + date: TimeData; + content: JsxChildren; + link?: string; +} + +export interface MonthCalendarProps + extends Omit, + Pick { + locale?: Navigator['language']; + value?: DateData[]; + onSelect?: (event: CustomEvent) => any; + onChange?: (event: CustomEvent) => any; +} + +export interface MonthCalendar extends WebCell {} + +@component({ tagName: 'month-calendar' }) +@observer +export class MonthCalendar + extends HTMLElement + implements WebCell +{ + @attribute + @observable + accessor variant: MonthCalendarProps['variant'] = 'primary'; + + @attribute + @observable + accessor locale: Navigator['language']; + + @observable + accessor value: DateData[] = []; + + @computed + get weekFormatter() { + const { locale = globalThis.navigator?.language } = this; + + return new Intl.DateTimeFormat(locale, { weekday: 'long' }); + } + + @observable + accessor currentDate = new Date(); + + @computed + get dateGrid() { + let startDate = new Date(this.currentDate); + startDate.setDate(1); + startDate = new Date(+startDate - startDate.getDay() * Day); + + const dateList = Array.from( + new Array(42), + (_, index) => new Date(+startDate + index * Day) + ); + return splitArray(dateList, 7); + } + + changeMonth(delta: number) { + this.currentDate = changeMonth(this.currentDate, delta); + + this.emit('change', this.currentDate); + } + + renderDate = (date: Date) => { + const { value } = this, + dateText = formatDate(date, 'YYYY-MM-DD'); + const list = value?.filter( + ({ date }) => formatDate(date, 'YYYY-MM-DD') === dateText + ); + + return ( + + + + {list?.map(item => + typeof item.content === 'object' ? ( + item.content + ) : ( + // + this.emit('select', item)} + > + {item.content} + + // + ) + )} + + ); + }; + + render() { + const { style, variant, weekFormatter, currentDate, dateGrid } = this; + + return ( + + + + + {dateGrid[0].map((date, index, { length }) => ( + + ))} + + + + {dateGrid.map(days => ( + {days.map(this.renderDate)} + ))} + +
+
+ + + {formatDate(currentDate, 'YYYY-MM')} + + +
+
+ {weekFormatter.format(date)} +
+ ); + } +} diff --git a/source/component/SessionBox.tsx b/source/component/SessionBox.tsx index 90211dc..5694369 100644 --- a/source/component/SessionBox.tsx +++ b/source/component/SessionBox.tsx @@ -1,24 +1,21 @@ -import { component, mixin, watch, createCell } from 'web-cell'; -import { observer } from 'mobx-web-cell'; -import { InputGroup } from 'boot-cell/source/Form/InputGroup'; -import { Field } from 'boot-cell/source/Form/Field'; -import { Button } from 'boot-cell/source/Form/Button'; +import { attribute, component, observer } from 'web-cell'; +import { observable } from 'mobx'; +import { InputGroup, FormControl, Button } from 'boot-cell'; import { session } from '../model'; -@observer @component({ tagName: 'session-box', - renderTarget: 'children' + mode: 'open' }) -export class SessionBox extends mixin() { - @watch - countDown = 0; +@observer +export class SessionBox extends HTMLElement { + @attribute + @observable + accessor countDown = 0; connectedCallback() { session.getProfile(); - - super.connectedCallback!(); } handleSMSCode = () => { @@ -46,20 +43,19 @@ export class SessionBox extends mixin() { ); }; - render() { + renderForm() { const { countDown } = this; - return session.user ? ( - this.defaultSlot - ) : ( + return (

参会者登录

- - - ); } + + render() { + return ( + <> + + {session.user ? : this.renderForm()} + + ); + } } diff --git a/source/model/Activity.ts b/source/model/Activity.ts index 9261e37..f901c50 100644 --- a/source/model/Activity.ts +++ b/source/model/Activity.ts @@ -1,24 +1,17 @@ import { observable } from 'mobx'; -import { buildURLData } from 'web-utility/source/URL'; -import { Day, formatDate } from 'web-utility/source/date'; +import { buildURLData, Day, formatDate } from 'web-utility'; import { DataItem, client } from './service'; -export interface Activity extends DataItem { - title: string; - start: string; - end: string; - address: string; - banner: string; - link: string; -} +export type Activity = DataItem & + Record<'title' | 'start' | 'end' | 'address' | 'banner' | 'link', string>; export class ActivityModel { @observable - loading = false; + accessor loading = false; @observable - list: Activity[] = []; + accessor list: Activity[] = []; async getDayList(date: Date) { this.loading = true; diff --git a/source/model/App.ts b/source/model/App.ts index 7edbf03..d2e60b7 100644 --- a/source/model/App.ts +++ b/source/model/App.ts @@ -1,16 +1,15 @@ +import { HTTPError } from 'koajax'; import { observable } from 'mobx'; import { DataItem, client } from './service'; -export interface User extends DataItem { - username: string; +export interface User extends DataItem, Record<'username' | 'gender', string> { mobilePhoneNumber?: string; - gender: string; } export class Session { @observable - user?: User; + accessor user: User | undefined; async getProfile() { try { @@ -18,7 +17,7 @@ export class Session { return (this.user = body); } catch (error) { - if (error.status !== 401) throw error; + if ((error as HTTPError).status !== 401) throw error; } } diff --git a/source/model/service.ts b/source/model/service.ts index 0663280..e8a1fdd 100644 --- a/source/model/service.ts +++ b/source/model/service.ts @@ -9,8 +9,4 @@ export const client = new HTTPClient({ withCredentials: true }); -export interface DataItem { - objectId: string; - createdAt: string; - updatedAt: string; -} +export type DataItem = Record<'objectId' | 'createdAt' | 'updatedAt', string>; diff --git a/source/page/Activity.tsx b/source/page/Activity.tsx index 43aaf07..6616cf8 100644 --- a/source/page/Activity.tsx +++ b/source/page/Activity.tsx @@ -1,93 +1,66 @@ -import { - WebCellProps, - component, - mixin, - attribute, - watch, - createCell, - Fragment -} from 'web-cell'; -import { observer } from 'mobx-web-cell'; -import classNames from 'classnames'; -import { formatDate } from 'web-utility/source/date'; - -import { MonthCalendar } from 'boot-cell/source/Calendar/MonthCalendar'; -import { SpinnerBox } from 'boot-cell/source/Prompt/Spinner'; -import { Card } from 'boot-cell/source/Content/Card'; +import { WebCell, component, attribute, reaction, observer } from 'web-cell'; +import { observable } from 'mobx'; +import { formatDate } from 'web-utility'; +import { MonthCalendar } from '../component/MonthCalendar'; +import { SpinnerBox, Card, CardBody, CardImg, CardTitle } from 'boot-cell'; import { TopNavBar } from '../component'; -import style from './Home.less'; +import * as style from './Home.module.less'; import { common_menu } from './data'; import { activity, Activity } from '../model'; -export interface ActivityPageProps extends WebCellProps { - date?: string; -} +export interface ActivityPage extends WebCell {} +@component({ tagName: 'activity-page' }) @observer -@component({ - tagName: 'activity-page', - renderTarget: 'children' -}) -export class ActivityPage extends mixin() { +export class ActivityPage extends HTMLElement implements WebCell { @attribute - @watch - set date(date: string) { - this.setProps({ date }).then(() => activity.getDayList(new Date(date))); - } + @observable + accessor date = new Date(); - connectedCallback() { - if (!this.date) this.date = formatDate(new Date(), 'YYYY-MM-DD'); - - super.connectedCallback!(); + @reaction(({ date }) => date) + handleDate(date: Date) { + activity.getDayList(date); } renderCards(list: Activity[]) { return list.map(({ banner, link, title, start, end, address }) => ( - - {title} - - } - > -
    -
  • 开始:{formatDate(start)}
  • -
  • 结束:{formatDate(end)}
  • -
  • 地点:{address}
  • -
+ + + + + + {title} + + +
    +
  • 开始:{formatDate(start)}
  • +
  • 结束:{formatDate(end)}
  • +
  • 地点:{address}
  • +
+
)); } - render({ date }: ActivityPageProps) { - const { loading, list } = activity; + render() { + const { date } = this, + { loading, list } = activity; return ( <> +

全国 IT 活动

+ { - const current = formatDate(time, 'YYYY-MM-DD'); - - return ( - - {time.getDate()} - - ); - }} + // @ts-ignore + onChange={({ detail }) => (this.date = detail)} /> - -
- -

成都社区

-
+export const CommunityPage: FC = props => ( +
+ +
+ +

成都社区

+
-
- {features.map((item, index) => ( - <> - - {index + 1 < features.length ? ( -
- ) : null} - - ))} -
- - ); -} +
+ {features.map((item, index) => ( + <> + + + {index + 1 < features.length &&
} + + ))} +
+
+); diff --git a/source/page/data.ts b/source/page/data.ts index 204c7f4..085ef2a 100644 --- a/source/page/data.ts +++ b/source/page/data.ts @@ -82,7 +82,7 @@ export const features = [ title: '中文技术社区友军众多', summary: '开源社、开源工场、微软 MVP 社区、成都 Google 开发者社区、阿里云 ACE 成都同城会、成都 Linux 用户组、重庆前端社区……', - logo: 'https://opensource.org/files/osi_keyhole_300X300_90ppi_0.png' + logo: 'https://i0.wp.com/opensource.org/wp-content/uploads/2009/06/osi_symbol.png?w=628&ssl=1' }, { title: '更多精彩,期待你的参与……', diff --git a/source/page/index.tsx b/source/page/index.tsx index a45abb8..91421cb 100644 --- a/source/page/index.tsx +++ b/source/page/index.tsx @@ -2,6 +2,8 @@ import { createRouter } from 'cell-router'; import WeChat_QRC from '../image/FCC-CDG-WeChat.png'; import { HomePage } from './Home'; +import { CommunityPage } from './Community'; +import { ActivityPage } from './Activity'; const { Route } = createRouter(); @@ -9,14 +11,14 @@ export const PageRouter = () => (
- {/* { paths: ['community'], component: CommunityPage }, - { paths: ['2017'], component: Page2017 }, + + {/* { paths: ['2017'], component: Page2017 }, { paths: ['2018/Code4City'], component: Code4City }, { paths: ['2018'], component: Page2018 }, { paths: ['2019'], component: Page2019 }, { paths: ['2019/invitation'], component: InvitationCard }, - { paths: ['2019/accounts'], component: PageAccount }, - { paths: ['activity'], component: ActivityPage } */} + { paths: ['2019/accounts'], component: PageAccount }, */} +