mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor(console): add missing section title and adjust card layout in get-started page (#4428)
This commit is contained in:
parent
5e8b1bd598
commit
54321d93a2
3 changed files with 67 additions and 28 deletions
27
packages/console/src/hooks/use-window-resize.ts
Normal file
27
packages/console/src/hooks/use-window-resize.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { useEffect, useLayoutEffect, useRef } from 'react';
|
||||
|
||||
type Callback = (event?: UIEvent) => void;
|
||||
|
||||
const useWindowResize = (callback: Callback) => {
|
||||
const callbackRef = useRef<Callback>(callback);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
callbackRef.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const handler: Callback = (event) => {
|
||||
callbackRef.current(event);
|
||||
};
|
||||
|
||||
handler();
|
||||
window.addEventListener('resize', handler);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handler);
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
export default useWindowResize;
|
|
@ -1,4 +1,5 @@
|
|||
import classNames from 'classnames';
|
||||
import { type Ref, forwardRef } from 'react';
|
||||
|
||||
import { type Guide } from '@/assets/docs/guides/types';
|
||||
|
||||
|
@ -15,20 +16,16 @@ type GuideGroupProps = {
|
|||
onClickGuide: (data: SelectedGuide) => void;
|
||||
};
|
||||
|
||||
function GuideGroup({
|
||||
className,
|
||||
categoryName,
|
||||
guides,
|
||||
hasCardBorder,
|
||||
isCompact,
|
||||
onClickGuide,
|
||||
}: GuideGroupProps) {
|
||||
function GuideGroup(
|
||||
{ className, categoryName, guides, hasCardBorder, isCompact, onClickGuide }: GuideGroupProps,
|
||||
ref: Ref<HTMLDivElement>
|
||||
) {
|
||||
if (!guides?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.guideGroup, className)}>
|
||||
<div ref={ref} className={classNames(styles.guideGroup, className)}>
|
||||
{categoryName && <label>{categoryName}</label>}
|
||||
<div className={styles.grid}>
|
||||
{guides.map((guide) => (
|
||||
|
@ -45,4 +42,4 @@ function GuideGroup({
|
|||
);
|
||||
}
|
||||
|
||||
export default GuideGroup;
|
||||
export default forwardRef<HTMLDivElement, GuideGroupProps>(GuideGroup);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { withAppInsights } from '@logto/app-insights/react';
|
||||
import { Theme, type Application } from '@logto/schemas';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import CheckPreviewDark from '@/assets/icons/check-demo-dark.svg';
|
||||
|
@ -19,9 +19,11 @@ import Spacer from '@/ds-components/Spacer';
|
|||
import TextLink from '@/ds-components/TextLink';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
import useWindowResize from '@/hooks/use-window-resize';
|
||||
|
||||
import CreateForm from '../Applications/components/CreateForm';
|
||||
import GuideCard, { type SelectedGuide } from '../Applications/components/GuideCard';
|
||||
import { type SelectedGuide } from '../Applications/components/GuideCard';
|
||||
import GuideGroup from '../Applications/components/GuideGroup';
|
||||
import useAppGuideMetadata from '../Applications/components/GuideLibrary/hook';
|
||||
|
||||
import FreePlanNotification from './FreePlanNotification';
|
||||
|
@ -39,16 +41,26 @@ function GetStarted() {
|
|||
const [selectedGuide, setSelectedGuide] = useState<SelectedGuide>();
|
||||
const [_, getStructuredMetadata] = useAppGuideMetadata();
|
||||
const [showCreateForm, setShowCreateForm] = useState<boolean>(false);
|
||||
// The number of visible guide cards to show in one row per the current screen width
|
||||
const [visibleCardCount, setVisibleCardCount] = useState(4);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const theme = useTheme();
|
||||
const { PreviewIcon, SocialIcon, RbacIcon } = icons[theme];
|
||||
|
||||
useWindowResize(() => {
|
||||
const containerWidth = containerRef.current?.clientWidth ?? 0;
|
||||
|
||||
// Responsive breakpoints (1080, 680px) are defined in `GuideGroup` component SCSS,
|
||||
// and we need to keep them consistent.
|
||||
setVisibleCardCount(containerWidth > 1080 ? 4 : containerWidth > 680 ? 3 : 2);
|
||||
});
|
||||
|
||||
/**
|
||||
* Only need 4 featured guides at most, since by design in get-started page we need to show
|
||||
* a few most popular SDK guides, and 4 makes it easy to have a 4 x 1 or 2 x 2 card layout.
|
||||
* Slice the guide metadata as we only need to show 1 row of guide cards in get-started page
|
||||
*/
|
||||
const featuredAppGuides = useMemo(
|
||||
() => getStructuredMetadata().featured.slice(0, 4),
|
||||
[getStructuredMetadata]
|
||||
() => getStructuredMetadata().featured.slice(0, visibleCardCount),
|
||||
[visibleCardCount, getStructuredMetadata]
|
||||
);
|
||||
|
||||
const onClickGuide = useCallback((data: SelectedGuide) => {
|
||||
|
@ -78,10 +90,12 @@ function GetStarted() {
|
|||
<FreePlanNotification />
|
||||
<Card className={styles.card}>
|
||||
<div className={styles.title}>{t('get_started.develop.title')}</div>
|
||||
<div className={styles.grid}>
|
||||
{featuredAppGuides.map((guide) => (
|
||||
<GuideCard key={guide.id} hasBorder data={guide} onClick={onClickGuide} />
|
||||
))}
|
||||
<GuideGroup
|
||||
ref={containerRef}
|
||||
hasCardBorder
|
||||
guides={featuredAppGuides}
|
||||
onClickGuide={onClickGuide}
|
||||
/>
|
||||
{selectedGuide?.target !== 'API' && showCreateForm && (
|
||||
<CreateForm
|
||||
defaultCreateType={selectedGuide?.target}
|
||||
|
@ -89,10 +103,10 @@ function GetStarted() {
|
|||
onClose={onCloseCreateForm}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<TextLink to="/applications/create">{t('get_started.view_all')}</TextLink>
|
||||
</Card>
|
||||
<Card className={styles.card}>
|
||||
<div className={styles.title}>{t('get_started.customize.title')}</div>
|
||||
<div className={styles.borderBox}>
|
||||
<div className={styles.rowWrapper}>
|
||||
<div className={styles.icon}>
|
||||
|
@ -134,6 +148,7 @@ function GetStarted() {
|
|||
</div>
|
||||
</Card>
|
||||
<Card className={styles.card}>
|
||||
<div className={styles.title}>{t('get_started.manage.title')}</div>
|
||||
<div className={styles.borderBox}>
|
||||
<div className={styles.rowWrapper}>
|
||||
<div className={styles.icon}>
|
||||
|
|
Loading…
Reference in a new issue