Merge branch 'develop' of https://github.com/shrimpia/misskey-tools into develop

This commit is contained in:
Xeltica 2023-06-14 15:06:03 +09:00
commit ba60b42ac7
2 changed files with 121 additions and 18 deletions

View File

@ -1,12 +1,12 @@
import React from 'react';
import { useState } from 'react';
import { useLayoutEffect } from 'react';
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { styled } from '@/libs/stitches';
export type TabProp = {
children: React.ReactElement<TabItem> | React.ReactElement<TabItem>[]
value?: string;
direction?: 'vertical' | 'horizontal';
onChange?: (newValue: string) => void;
};
@ -31,29 +31,115 @@ const Ul = styled('ul', {
display: 'flex',
margin: 0,
padding: 0,
defaultVariants: {
direction: 'horizontal',
},
variants: {
direction: {
horizontal: {
flexDirection: 'row',
},
vertical: {
flexDirection: 'column',
},
},
},
});
const Li = styled('li', {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '$m',
padding: '$s $l',
margin: 0,
borderBottom: '2px solid $muted',
color: '$muted',
defaultVariants: {
direction: 'horizontal',
},
variants: {
active: {
true: {
color: '$primary',
},
},
direction: {
horizontal: {
borderBottom: '2px solid $divider',
},
vertical: {
borderLeft: '2px solid $divider',
justifyContent: 'flex-start',
},
},
},
});
const Indicator = styled('div', {
position: 'absolute',
background: '$primary',
transition: 'all 0.2s $timingFunction$default',
defaultVariants: {
direction: 'horizontal',
},
variants: {
direction: {
horizontal: {
bottom: 0,
height: 2,
},
vertical: {
left: 0,
width: 2,
},
},
},
});
export const Tab: React.FC<TabProp> = (p) => {
const c = Array.isArray(p.children) ? p.children.map(c => c.props) : [p.children.props];
const [value, setValue] = useState(c[0].value);
const items = useMemo(() => Array.isArray(p.children) ? p.children.map(c => c.props) : [p.children.props], [p.children]);
const [value, setValue] = useState<string | null>(null);
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
const position = useMemo(() => {
let sum = 0;
const max = items.findIndex(item => item.value === value);
for (let i = 0; i < max; i++) {
const ref = itemRefs.current[i];
if (ref == null) return 0;
sum += p.direction === 'vertical' ? ref.clientHeight : ref.clientWidth;
}
console.log(`left: ${sum}`);
return sum;
}, [items, p.direction, value]);
const size = useMemo(() => {
const i = items.findIndex(item => item.value === value);
const ref = itemRefs.current[i];
if (ref == null) return 0;
return p.direction === 'vertical' ? ref.clientHeight : ref.clientWidth;
}, [items, p.direction, value]);
const indicatorStyle = useMemo(() => (p.direction === 'vertical' ? {
top: position, height: size,
}: {
left: position, width: size,
}), [p.direction, position, size]);
useEffect(() => {
itemRefs.current = itemRefs.current.slice(0, items.length);
if (value === null) {
setValue(items[0].value);
}
}, [items, value]);
useLayoutEffect(() => {
if (!p.value) return;
@ -62,9 +148,9 @@ export const Tab: React.FC<TabProp> = (p) => {
return (
<Container>
<Ul>
{c.map(item => (
<Li key={item.value} role="button" active={item.value === value} onClick={() => {
<Ul direction={p.direction}>
{items.map((item, i) => (
<Li direction={p.direction} key={item.value} ref={el => itemRefs.current[i] = el} role="button" active={item.value === value} onClick={() => {
if (item.value === value) return;
console.log(item.value);
setValue(item.value);
@ -74,6 +160,7 @@ export const Tab: React.FC<TabProp> = (p) => {
</Li>
))}
</Ul>
<Indicator aria-hidden direction={p.direction} css={indicatorStyle} />
</Container>
);
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import type { Meta } from '@storybook/react';
import type { Meta, StoryObj } from '@storybook/react';
import { Tab, TabItem } from '@/components/primitives/Tab';
@ -9,14 +9,30 @@ const meta = {
parameters: {
layouts: 'centered',
},
args: {
children: ([
<TabItem key="home" value="home"></TabItem>,
<TabItem key="notification" value="notification"></TabItem>,
<TabItem key="explore" value="explore"></TabItem>,
]),
},
} satisfies Meta<typeof Tab>;
export default meta;
export const Default = () => (
<Tab>
<TabItem value="home"></TabItem>
<TabItem value="notification"></TabItem>
<TabItem value="explore"></TabItem>
</Tab>
);
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
export const Horizontal: Story = {
args: {
direction: 'horizontal',
},
};
export const Vertical: Story = {
args: {
direction: 'vertical',
},
};