HumanBreak/packages-user/client-modules/src/render/components/list.tsx

219 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { DefaultProps } from '@motajs/render-vue';
import { computed, defineComponent, ref, SlotsType, VNode } from 'vue';
import { Selection } from './misc';
import { ElementLocator } from '@motajs/render-core';
import { Font } from '@motajs/render-style';
import { SetupComponentOptions } from '@motajs/system-ui';
import { Scroll } from './scroll';
export interface ListProps extends DefaultProps {
/** 列表内容,第一项表示 id第二项表示显示的内容 */
list: [string, string][];
/** 当前选中的项 */
selected: string;
/** 定位 */
loc: ElementLocator;
/** 每行的高度,默认 18 */
lineHeight?: number;
/** 字体 */
font?: Font;
/** 使用 winskin 作为光标 */
winskin?: ImageIds;
/** 使用指定样式作为光标背景 */
color?: CanvasStyle;
/** 使用指定样式作为光标边框 */
border?: CanvasStyle;
/** 选择图标的不透明度范围 */
alphaRange?: [number, number];
}
export type ListEmits = {
/**
* 当用户选中某一项时触发
* @param key 选中的项的 id
*/
update: (key: string) => void;
'update:selected': (value: string) => void;
};
const listProps = {
props: [
'list',
'selected',
'loc',
'lineHeight',
'font',
'winskin',
'color',
'border',
'alphaRange'
],
emits: ['update', 'update:selected']
} satisfies SetupComponentOptions<ListProps, ListEmits, keyof ListEmits>;
export const List = defineComponent<ListProps, ListEmits, keyof ListEmits>(
(props, { emit }) => {
const selected = ref(props.list[0][0]);
const lineHeight = computed(() => props.lineHeight ?? 18);
const select = (value: string) => {
selected.value = value;
emit('update', value);
emit('update:selected', value);
};
return () => (
<Scroll loc={props.loc}>
{props.list.map((v, i) => {
const [key, value] = v;
const loc: ElementLocator = [
0,
lineHeight.value * i,
props.loc[2] ?? 200,
lineHeight.value
];
const selectionLoc: ElementLocator = [
0,
0,
(props.loc[2] ?? 200) - 10,
lineHeight.value
];
const textLoc: ElementLocator = [
10,
lineHeight.value / 2,
void 0,
void 0,
0,
0.5
];
return (
<container loc={loc} onClick={() => select(key)}>
{selected.value === key && (
<Selection
loc={selectionLoc}
color={props.color}
border={props.border}
winskin={props.winskin}
alphaRange={props.alphaRange}
/>
)}
<text
loc={textLoc}
text={value}
font={props.font}
/>
</container>
);
})}
</Scroll>
);
},
listProps
);
export interface ListPageProps extends ListProps {
/** 组件定位 */
loc: ElementLocator;
/** 列表所占比例 */
basis?: number;
/** 列表是否排列在右侧 */
right?: boolean;
/** 是否显示关闭按钮 */
close?: boolean;
/** 关闭按钮的位置,相对于组件定位 */
closeLoc?: ElementLocator;
}
export type ListPageEmits = {
close: () => void;
} & ListEmits;
export type ListPageSlots = SlotsType<{
default: (key: string) => VNode | VNode[];
[x: string]: (key: string) => VNode | VNode[];
}>;
const listPageProps = {
props: [
'basis',
'right',
'list',
'selected',
'loc',
'lineHeight',
'font',
'winskin',
'color',
'border',
'alphaRange',
'close',
'closeLoc'
],
emits: ['update', 'update:selected', 'close']
} satisfies SetupComponentOptions<
ListPageProps,
ListPageEmits,
keyof ListPageEmits,
ListPageSlots
>;
export const ListPage = defineComponent<
ListPageProps,
ListPageEmits,
keyof ListPageEmits,
ListPageSlots
>((props, { emit, slots }) => {
const selected = ref(props.selected);
const basis = computed(() => props.basis ?? 0.3);
const width = computed(() => props.loc[2] ?? 200);
const height = computed(() => props.loc[3] ?? 200);
const listLoc = computed<ElementLocator>(() => {
const listWidth = width.value * basis.value;
if (props.right) {
return [width.value - listWidth, 0, listWidth, height.value];
} else {
return [0, 0, listWidth, height.value];
}
});
const contentLoc = computed<ElementLocator>(() => {
const contentWidth = width.value * (1 - basis.value);
if (props.right) {
return [0, 0, contentWidth, height.value];
} else {
return [width.value - contentWidth, 0, contentWidth, height.value];
}
});
const update = (key: string) => {
emit('update', key);
emit('update:selected', key);
};
return () => (
<container loc={props.loc}>
<List
{...props}
loc={listLoc.value}
list={props.list}
v-model:selected={selected.value}
onUpdate={update}
></List>
<container loc={contentLoc.value}>
{slots[selected.value]?.(selected.value) ??
slots.default?.(selected.value)}
</container>
{props.close && (
<text
loc={props.closeLoc}
text="关闭"
cursor="pointer"
font={props.font}
></text>
)}
</container>
);
}, listPageProps);