feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -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>
);

View File

@@ -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>
);

View File

@@ -0,0 +1,6 @@
@media (max-width: 640px) {
.grid-cols-1 {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}

View 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>>;

View File

@@ -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,
});
};

View 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';

View 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>
>;

View 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' />

View 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.
*/
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(' ');