feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { tokenMapToStr } from '../../utils/token-map-to-str';
|
||||
import { type ResponsiveTokenMap } from '../../types';
|
||||
import { type ScreenRange } from '../../constant';
|
||||
|
||||
interface ResponsiveBoxProps {
|
||||
contents: React.ReactNode[]; // array of content
|
||||
colReverse?: boolean; // direction is col or col-reverse
|
||||
rowReverse?: boolean; // direction is row or row-reverse
|
||||
gaps?: ResponsiveTokenMap<ScreenRange>;
|
||||
}
|
||||
export const ResponsiveBox = ({
|
||||
contents = [],
|
||||
colReverse = false,
|
||||
rowReverse = false,
|
||||
gaps,
|
||||
}: ResponsiveBoxProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
'w-full flex overflow-hidden',
|
||||
colReverse
|
||||
? 'flex-col-reverse sm:flex-col-reverse'
|
||||
: 'flex-col sm:flex-col',
|
||||
rowReverse
|
||||
? 'md:flex-row-reverse lg:flex-row-reverse'
|
||||
: 'md:flex-row lg:flex-row',
|
||||
gaps && tokenMapToStr(gaps, 'gap'),
|
||||
)}
|
||||
>
|
||||
{contents}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const ResponsiveBox2 = ({
|
||||
contents = [],
|
||||
colReverse = false,
|
||||
rowReverse = false,
|
||||
gaps,
|
||||
}: ResponsiveBoxProps) => (
|
||||
<div
|
||||
className={classNames(
|
||||
'w-full flex overflow-hidden',
|
||||
colReverse ? 'flex-col-reverse' : 'flex-col',
|
||||
rowReverse ? 'lg:flex-row-reverse' : 'lg:flex-row',
|
||||
gaps && tokenMapToStr(gaps, 'gap'),
|
||||
)}
|
||||
>
|
||||
{contents}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { tokenMapToStr } from '../../utils/token-map-to-str';
|
||||
import { type ResponsiveTokenMap } from '../../types';
|
||||
import { type ScreenRange } from '../../constant';
|
||||
|
||||
import styles from './responsive.module.less';
|
||||
|
||||
interface ResponsiveListProps<T> {
|
||||
dataSource: T[];
|
||||
renderItem: (item: T, index: number) => React.ReactNode;
|
||||
|
||||
className?: string;
|
||||
emptyContent?: React.ReactNode;
|
||||
|
||||
footer?: React.ReactNode;
|
||||
|
||||
gridCols?: ResponsiveTokenMap<ScreenRange>; // 响应式列数
|
||||
gridGapXs?: ResponsiveTokenMap<ScreenRange>; // 响应式X轴gap
|
||||
gridGapYs?: ResponsiveTokenMap<ScreenRange>; // 响应式Y轴gap
|
||||
}
|
||||
|
||||
// 通过tailwind动态根据媒体查询设置List列数
|
||||
export const ResponsiveList = <T extends object>({
|
||||
dataSource,
|
||||
renderItem,
|
||||
|
||||
className,
|
||||
emptyContent,
|
||||
footer,
|
||||
|
||||
gridCols = {
|
||||
sm: 1,
|
||||
md: 2,
|
||||
lg: 3,
|
||||
xl: 4,
|
||||
},
|
||||
gridGapXs,
|
||||
gridGapYs,
|
||||
}: ResponsiveListProps<T>) => (
|
||||
<div className={classNames('flex flex-col justify-items-center', className)}>
|
||||
<div
|
||||
className={classNames(
|
||||
'w-full grid justify-content-center responsive-list-container',
|
||||
gridCols && tokenMapToStr(gridCols, 'grid-cols'),
|
||||
gridGapXs && tokenMapToStr(gridGapXs, 'gap-x'),
|
||||
gridGapYs && tokenMapToStr(gridGapYs, 'gap-y'),
|
||||
styles['grid-cols-1'],
|
||||
)}
|
||||
>
|
||||
{dataSource.length
|
||||
? dataSource.map((data, idx) => renderItem(data, idx))
|
||||
: emptyContent}
|
||||
</div>
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,6 @@
|
||||
@media (max-width: 640px) {
|
||||
.grid-cols-1 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
39
frontend/packages/arch/responsive-kit/src/constant.ts
Normal file
39
frontend/packages/arch/responsive-kit/src/constant.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export enum ScreenRange {
|
||||
SM = 'sm',
|
||||
MD = 'md',
|
||||
LG = 'lg',
|
||||
XL = 'xl',
|
||||
XL1_5 = 'xl1.5',
|
||||
XL2 = '2xl',
|
||||
}
|
||||
|
||||
export const SCREENS_TOKENS = {
|
||||
[ScreenRange.SM]: '640px',
|
||||
[ScreenRange.MD]: '768px',
|
||||
[ScreenRange.LG]: '1200px',
|
||||
[ScreenRange.XL]: '1600px',
|
||||
[ScreenRange.XL1_5]: '1680px',
|
||||
[ScreenRange.XL2]: '1920px',
|
||||
};
|
||||
|
||||
export const SCREENS_TOKENS_2 = {
|
||||
[ScreenRange.XL1_5]: '1680px',
|
||||
};
|
||||
|
||||
export type ResponsiveTokenMap = Partial<Record<ScreenRange | 'basic', number>>;
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// @coze-arch/responsive-kit
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
SCREENS_TOKENS,
|
||||
SCREENS_TOKENS_2,
|
||||
type ScreenRange,
|
||||
} from '../constant';
|
||||
|
||||
export const useCustomMediaQuery = ({
|
||||
rangeMinPx,
|
||||
rangeMaxPx,
|
||||
}: {
|
||||
rangeMinPx?: string;
|
||||
rangeMaxPx?: string;
|
||||
}) => {
|
||||
// 1. 根据查询范围拼凑query语句
|
||||
const getQuery = () => {
|
||||
const minQuery = rangeMinPx ? `(min-width: ${rangeMinPx})` : '';
|
||||
const maxQuery = rangeMaxPx ? `(max-width: ${rangeMaxPx})` : '';
|
||||
return minQuery && maxQuery
|
||||
? `${minQuery} and ${maxQuery}`
|
||||
: minQuery || maxQuery;
|
||||
};
|
||||
const query = getQuery();
|
||||
|
||||
// 2. 配合监听事件动态判断是否在区域中
|
||||
const [matches, setMatches] = useState(window.matchMedia(query).matches);
|
||||
useEffect(() => {
|
||||
const mediaQueryList = window.matchMedia(query);
|
||||
const documentChangeHandler = () => {
|
||||
setMatches(mediaQueryList.matches);
|
||||
};
|
||||
|
||||
mediaQueryList?.addEventListener?.('change', documentChangeHandler);
|
||||
documentChangeHandler();
|
||||
return () => {
|
||||
mediaQueryList?.removeEventListener?.('change', documentChangeHandler);
|
||||
};
|
||||
}, [query]);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
// 判断当前屏幕像素是否match媒体查询条件
|
||||
export const useMediaQuery = ({
|
||||
rangeMin,
|
||||
rangeMax,
|
||||
}: {
|
||||
rangeMin?: ScreenRange;
|
||||
rangeMax?: ScreenRange;
|
||||
}) => {
|
||||
const tokens = { ...SCREENS_TOKENS, ...SCREENS_TOKENS_2 };
|
||||
|
||||
const rangeMinPx = rangeMin ? tokens[rangeMin] : '';
|
||||
const rangeMaxPx = rangeMax ? tokens[rangeMax] : '';
|
||||
return useCustomMediaQuery({
|
||||
rangeMinPx,
|
||||
rangeMaxPx,
|
||||
});
|
||||
};
|
||||
26
frontend/packages/arch/responsive-kit/src/index.tsx
Normal file
26
frontend/packages/arch/responsive-kit/src/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { SCREENS_TOKENS, ScreenRange } from './constant';
|
||||
|
||||
export { useMediaQuery, useCustomMediaQuery } from './hooks/media-query';
|
||||
|
||||
export { ResponsiveList } from './components/layout/ResponsiveList';
|
||||
export {
|
||||
ResponsiveBox,
|
||||
ResponsiveBox2,
|
||||
} from './components/layout/ResponsiveBox';
|
||||
export { type ResponsiveTokenMap } from './types';
|
||||
19
frontend/packages/arch/responsive-kit/src/types.ts
Normal file
19
frontend/packages/arch/responsive-kit/src/types.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type ResponsiveTokenMap<T extends string> = Partial<
|
||||
Record<T | 'basic', number>
|
||||
>;
|
||||
17
frontend/packages/arch/responsive-kit/src/typings.d.ts
vendored
Normal file
17
frontend/packages/arch/responsive-kit/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ResponsiveTokenMap } from '../types';
|
||||
import { type ScreenRange } from '../constant';
|
||||
|
||||
export const tokenMapToStr = (
|
||||
tokenMap: ResponsiveTokenMap<ScreenRange>,
|
||||
prefix: string,
|
||||
): string =>
|
||||
Object.entries(tokenMap)
|
||||
.map(([k, v]) => `${k === 'basic' ? '' : `${k}:`}${prefix}-${v}`)
|
||||
.join(' ');
|
||||
Reference in New Issue
Block a user