Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added KMenu component #440

Merged
merged 74 commits into from
Jan 13, 2025
Merged
Changes from 1 commit
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
db4d588
wip: init KMenu component
baiwusanyu-c Apr 15, 2024
2e6cf3d
wip: KMEnu api design
baiwusanyu-c Apr 15, 2024
747e776
wip: temp commit
baiwusanyu-c Apr 15, 2024
c7b1f03
wip: temp commit
baiwusanyu-c Apr 15, 2024
8a4f7f9
wip: recursive slots and dynamic context
baiwusanyu-c Apr 16, 2024
8292b95
wip: initial drawing of menu component inline mode
baiwusanyu-c Apr 16, 2024
f1f00c5
wip: KMenu component completes submenu expansion animation
baiwusanyu-c Apr 16, 2024
2ba3233
wip: KMenu component completes submenu expansion animation
baiwusanyu-c Apr 16, 2024
ea22ff8
wip: fix expend animation
baiwusanyu-c Apr 17, 2024
f625554
wip: complete the selected style of the KMenu component submenu
baiwusanyu-c Apr 17, 2024
9f39ad6
wip: KMenu component completes incremental changes in submenu backgro…
baiwusanyu-c Apr 17, 2024
f05ed44
wip: KMenu component completes inline mode default submenu expansion
baiwusanyu-c Apr 17, 2024
a760a5a
wip: KMenu component completes inline mode default submenu select item
baiwusanyu-c Apr 17, 2024
0882d56
wip: KMenu component completes inline mode expendIcon and item icon s…
baiwusanyu-c Apr 17, 2024
beadd41
wip: temp commit
baiwusanyu-c Apr 17, 2024
7073947
wip: KMenu component completes inline mode click event
baiwusanyu-c Apr 17, 2024
1a65321
wip: KMenu component completes inline mode openChange event
baiwusanyu-c Apr 18, 2024
54d6397
wip: KMenu component completes inline mode select event
baiwusanyu-c Apr 18, 2024
cd4e682
wip: updated todo list
baiwusanyu-c Apr 18, 2024
fc0d119
wip: remove types
baiwusanyu-c Apr 18, 2024
9c53c1b
wip: KMenu component completes inline mode selectable props
baiwusanyu-c Apr 19, 2024
522036c
wip:temp commit
baiwusanyu-c Apr 21, 2024
4566872
wip: KMenu component completes vertical mode group item render
baiwusanyu-c Apr 21, 2024
7633e40
wip: optimize the grouping style of KMenu component in vertical mode
baiwusanyu-c Apr 21, 2024
06305bf
wip: temp commit
baiwusanyu-c Apr 21, 2024
b0b0c1b
wip: fix gorup item render error
baiwusanyu-c Apr 21, 2024
72ad9fb
wip: KMenu component completes vertical mode subMenuCloseDelay and su…
baiwusanyu-c Apr 22, 2024
81854d4
wip: KMenu component completes vertical and inline mode onDeSelect event
baiwusanyu-c Apr 22, 2024
c5f8bcc
wip: KMenu component vertical mode openUids props temp commit
baiwusanyu-c Apr 22, 2024
6fefbd7
wip: complete KMenu component vertical mode openUids props
baiwusanyu-c Apr 22, 2024
024a978
wip: complete the styling of the KMenu component in horizontal mode
baiwusanyu-c Apr 23, 2024
a1a40a4
wip: optimization the styling of the KMenu component in horizontal mode
baiwusanyu-c Apr 23, 2024
65dc2cb
wip: complete KMenu component temp commit
baiwusanyu-c Apr 23, 2024
3d16806
wip: temp commit
baiwusanyu-c Apr 24, 2024
af38ed4
wip: complete KMenu component temp commit
baiwusanyu-c Apr 24, 2024
275cd8d
wip: horizontal mode is turned on by default and is compatible with
baiwusanyu-c Apr 25, 2024
969fe23
wip: set horizontal width more correctly
baiwusanyu-c Apr 25, 2024
add53d0
wip: Preliminary drawing of omitted icons
baiwusanyu-c Apr 26, 2024
ddbb701
wip: complete KMenu component horizontal mode omit display
baiwusanyu-c Apr 28, 2024
718ec97
wip: complete KMenu component label slots
Oct 10, 2024
1bb7eb5
wip: complete KMenu component inlineCollapsed prop
Oct 10, 2024
6f61491
wip: complete KMenu component title & label props
Oct 23, 2024
c6e12b6
wip: KTooltip added width props
Oct 23, 2024
cfc67b0
wip: complete KMenu component danger and popupClassName props
Oct 23, 2024
4ff0111
wip: complete KMenu component disabled props
Dec 23, 2024
c51d0ff
wip: complete KMenu component titleClick event
Dec 23, 2024
f998151
test: added KMenu unit test
Dec 24, 2024
5d17ab5
test: added KMenu onSelect and onDeSelect unit test
Dec 25, 2024
db152a7
test: added KMenu onOpenChange unit test
Jan 8, 2025
3783c1c
test: added KMenu selectable props unit test
baiwusanyu-c Jan 8, 2025
472d911
test: added KMenu expandIcon props unit test
baiwusanyu-c Jan 8, 2025
21eda6b
test: added KMenu expandIcon slots unit test
baiwusanyu-c Jan 8, 2025
4e9cb2d
test: added KMenu icon slots unit test
baiwusanyu-c Jan 8, 2025
f35ec41
test: added KMenu label slots unit test
baiwusanyu-c Jan 8, 2025
e4eddea
test: added KMenu onTitleClick unit test
Jan 10, 2025
fb0e66a
test: added unit test about type attribute with KMenuItem item props
Jan 10, 2025
98e5e8a
test: added unit test about icon attribute with KMenuItem item props
Jan 10, 2025
a672d56
test: added unit test about label attribute with KMenuItem item props
Jan 10, 2025
46b8d06
test: added unit test about children attribute with KMenuItem item props
Jan 10, 2025
c98e434
ci: use pnpm
Jan 10, 2025
68e1fb4
test: update unit test snap
Jan 10, 2025
6550732
chore: updated tour component unit test snap
baiwusanyu-c Jan 10, 2025
d95584d
test: added unit test about KMenu multiple props
baiwusanyu-c Jan 10, 2025
0954297
test: added unit test about danger attribute with KMenu item props
baiwusanyu-c Jan 10, 2025
525a27b
test: added unit test about disabled attribute with KMenu item props
baiwusanyu-c Jan 10, 2025
917f369
ci: use npm registry
baiwusanyu-c Jan 10, 2025
9d0028d
ci: use npm registry
baiwusanyu-c Jan 10, 2025
e5460ce
chore: updated deps lock
baiwusanyu-c Jan 10, 2025
991893a
ci: updated node version
baiwusanyu-c Jan 10, 2025
c009490
ci: updated node version
baiwusanyu-c Jan 10, 2025
e0cbb99
test: updated unit test snap
baiwusanyu-c Jan 10, 2025
ad9436e
feat: KMenu component support dark theme
baiwusanyu-c Jan 10, 2025
026d08c
feat: KMenu component added theme prop
baiwusanyu-c Jan 10, 2025
52448ea
fix: Properly adjust the height of the KMenu component in horizontal …
baiwusanyu-c Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip: initial drawing of menu component inline mode
baiwusanyu-c committed Apr 22, 2024
commit 8292b95d106a76deb29bed13d152cf35f23a86c3
1 change: 1 addition & 0 deletions components/Menu/package.json
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@
"dependencies": {
"@ikun-ui/icon": "workspace:*",
"@ikun-ui/utils": "workspace:*",
"@ikun-ui/divider": "workspace:*",
"baiwusanyu-utils": "^1.0.18",
"clsx": "^2.0.0",
"svelte": "^4.2.7"
18 changes: 14 additions & 4 deletions components/Menu/src/index.svelte
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
export let triggerSubMenuAction: KMenuProps['triggerSubMenuAction'] = 'hover';
export let subMenuCloseDelay: KMenuProps['subMenuCloseDelay'] = 100;
export let subMenuOpenDelay: KMenuProps['subMenuOpenDelay'] = 0;
// export let items: KMenuProps['items'] = [];
export let inlineIndent: KMenuProps['inlineIndent'] = 24
export let expandIcon: KMenuProps['expandIcon'] = 'i-carbon-chevron-down';
export let overflowedIndicator: KMenuProps['overflowedIndicator'] =
'i-carbon-overflow-menu-horizontal';
@@ -22,6 +22,9 @@
expandIcon,
overflowedIndicator,
mode,
inlineIndent,
cls,
attrs,
})
setContext(menuKey, menuInst);
$: {
@@ -33,14 +36,21 @@
expandIcon,
overflowedIndicator,
mode,
inlineIndent,
cls,
attrs,
});
});
}

const prefixCls = getPrefixCls('menu');
$: cnames = clsx(prefixCls, cls);
$: cnames = clsx(
prefixCls,
`${prefixCls}-${mode}`,
cls
);
</script>

<div class={cnames} {...$$restProps} {...attrs}>
<ul class={cnames} {...$$restProps} {...attrs}>
<slot/>
</div>
</ul>
190 changes: 161 additions & 29 deletions components/Menu/src/item.svelte
Original file line number Diff line number Diff line change
@@ -1,54 +1,186 @@
<script lang="ts">
import { getContext } from 'svelte';
import type { KMenuInstance, KMenuInstanceOption, KMenuItemProps } from "./types";
import { getContext, tick } from "svelte";
import { KIcon } from "@ikun-ui/icon";
import type { KMenuInstance, KMenuInstanceOption, KMenuItemProps, SubMenuType } from "./types";
import { getPrefixCls, menuKey } from "@ikun-ui/utils";
import { clsx } from "clsx";
import { KDivider } from "@ikun-ui/divider";
import { KMenu } from "@ikun-ui/menu";
export let items: KMenuItemProps['items'] = [];
export let cls: KMenuItemProps['cls'] = undefined;
export let attrs: KMenuItemProps['attrs'] = {};
export let level: KMenuItemProps['level'] = 1;

const hasSub = (it: SubMenuType) => it.children && it.children.length
const isNotHorizontal = () => ctxProps.mode !== 'horizontal'
const isGroup = (it: SubMenuType) => it.type === 'group'
const getLevel = (it: SubMenuType, lv: number) => {
if(isGroup(it)){
return lv - 1
}
return lv
}


const menuCtx = getContext(menuKey) as KMenuInstance
let ctxProps:KMenuInstanceOption = {}
function updatedCtxProps(props: Record<any, any>){
ctxProps = {...props}
console.log(ctxProps)
}
if(menuCtx){
ctxProps = {...menuCtx.__dynamicProps}
menuCtx.__propHandleEvtMap.push(updatedCtxProps)
}


let itemsList = items
$: {
itemsList = items
}

function setOpenAndSelectStatus(it: SubMenuType, list = itemsList){
return list.map(value => {
if(value.uid === it.uid && !isGroup(it)){
// set selected
it.selected = value.selected = !value.selected
it.selected = value.selected = ctxProps.selectedUids?.includes(value.uid || '') || value.selected
// set open
it.open = value.open = !value.open
it.open = value.open = ctxProps.openUids?.includes(value.uid || '') || value.open
}
if(hasSub(value)){
it.children = value.children = setOpenAndSelectStatus(it, value.children)
}
return value
})
}
async function handleSelect(it: SubMenuType){

const parent = document.querySelector('#bwsy');
// parent.style.maxHeight = parent.scrollHeight + 'px';

itemsList = setOpenAndSelectStatus(it)
setTimeout(async () => {
console.log(parent?.getClientRects().height)
console.log(it.open, parent.scrollHeight)
parent.style.height = parent.scrollHeight + 'px';
}, 100)

await tick()
setTimeout(() => {
parent.style.height = 'auto'
console.log('RRRRRRRRRRRRRRR')
}, 500)
}

const menuPrefixCls = getPrefixCls('menu');
const prefixCls = getPrefixCls('menu-item');
$: cnames = clsx(prefixCls, cls);
$: cnames = (it: SubMenuType) => {
return clsx(
prefixCls,
{
[`${prefixCls}-${ctxProps.mode}-group`]: isGroup(it),
[`${prefixCls}-${ctxProps.mode}`]: !isGroup(it),
[`${prefixCls}-selected`]: !isGroup(it) && isNotHorizontal() && ctxProps.selectedUids?.includes(it.uid || '') || it.selected,
[`${prefixCls}-hover-ih`]: !isGroup(it) && isNotHorizontal() && !(ctxProps.selectedUids?.includes(it.uid || '') || it.selected),
[`${prefixCls}-vh-child`]: !isGroup(it) && isNotHorizontal() && hasSub(it)
},
cls);
}

const iconCls = clsx(
`${prefixCls}-icon`,
);

const expendIconCls = (it: SubMenuType) => {
return it.open ? `${ctxProps.expandIcon} rotate-180 k-icon-transition` : `${ctxProps.expandIcon} k-icon-transition`
}

const titleContentCls = (hasIcon: boolean) => {
return clsx({
[`${menuPrefixCls}-title-content`]: !hasIcon,
[`${menuPrefixCls}-title-content-i`]: hasIcon
})
}
const dividerCls = clsx(`${prefixCls}-divider`)
$: subMenuCls = clsx(`${menuPrefixCls}-sub`, ctxProps.cls)
</script>

{#each items as it (it.uid)}
<div
class={cnames}
{...$$restProps}
{...attrs}>
<slot name="item" item={it}>

<div>
{it.label}
<slot name="ic" item={it}></slot>
</div>

</slot>
{#if it.children && it.children.length}
<svelte:self items={it.children}>
<svelte:fragment let:item slot="item">
<slot name="item" item={item}>

<div>
{item.label}
<slot name="ic" item={it}></slot>
</div>
{#each itemsList as it (it.uid)}
{#if it.type !== 'divider'}
<li
on:click={() => handleSelect(it)}
aria-hidden="true"
style:padding-left={`${(ctxProps.inlineIndent || 24 ) * getLevel(it, level)}px`}
class={cnames(it)}
{...$$restProps}
{...attrs}>
<slot name="item" item={it}>
<div>
<slot name="icon" item={it}>
{#if it.icon}
<KIcon width="14px"
cls={iconCls}
height="14px"
icon={it.icon}>
</KIcon>
{/if}
</slot>
<span class={titleContentCls(!!it.icon)}>{it.label}</span>
</div>

{#if hasSub(it) && !isGroup(it)}
<slot name="expandIcon" item={it}>
<KIcon width="14px"
cls={iconCls}
height="14px"
icon={expendIconCls(it)}>
</KIcon>
</slot>
</svelte:fragment>
</svelte:self>
{/if}
</slot>


</li>

{#if (hasSub(it) && it.open) || isGroup(it)}
<KMenu {...ctxProps} cls={subMenuCls}>
<svelte:self items={it.children} level={getLevel(it, level) + 1}>
<svelte:fragment let:item slot="item">


<slot name="item" item={item}>
<div>
<slot name="icon" item={item}>
{#if item.icon}
<KIcon width="14px"
cls={iconCls}
height="14px"
icon={item.icon}>
</KIcon>
{/if}
</slot>
<span class={titleContentCls(!!item.icon)}>self {item.label}</span>
</div>

{#if hasSub(item) && !isGroup(item)}
<slot name="expandIcon" item={item}>
<KIcon width="14px"
cls={iconCls}
height="14px"
icon={expendIconCls(item)}>
</KIcon>
</slot>
{/if}

</slot>

</svelte:fragment>
</svelte:self>
</KMenu>
{/if}
</div>
{:else}
<KDivider cls={dividerCls}></KDivider>
{/if}
{/each}

24 changes: 16 additions & 8 deletions components/Menu/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import type { ClassValue } from 'clsx';
export type KMenuInstanceOption = {
expandIcon?: string;
inlineIndent?: number;
inlineCollapsed?: number;
inlineCollapsed?: boolean;
mode?: `vertical` | `horizontal` | `inline`;
openUids?: string[];
overflowedIndicator?: string;
@@ -13,6 +13,8 @@ export type KMenuInstanceOption = {
subMenuOpenDelay?: number;
theme?: 'light' | 'dark';
triggerSubMenuAction?: 'hover' | 'click';
cls?: ClassValue;
attrs?: Record<string, string>;
}
export type KMenuInstance = {
__propHandleEvtMap: Array<(props: Record<any, any>) => void>
@@ -32,13 +34,9 @@ export type KMenuProps = {
inlineIndent?: number;
/**
* TODO: inline 时菜单是否收起状态 (指显示图标的mini模式和展开到常规模式)
* @default 24
*/
inlineCollapsed?: number;
/**
* TODO: 🎯 菜单内容
* @default false
*/
// items: KItemType[]
inlineCollapsed?: boolean;
/**
* TODO: 🎯 菜单类型,现在支持垂直、水平、和内嵌模式三种
* `vertical` 和 `inline` 的区别在于 `vertical` 子菜单以 popover 形式出现
@@ -96,6 +94,10 @@ export type KMenuProps = {
// TODO: slots expandIcon 展开图标

export type KMenuItemProps = {
/**
* @internal
*/
level: number
items: SubMenuType[]
cls: ClassValue;
attrs: Record<string, string>;
@@ -132,14 +134,20 @@ export type SubMenuType = {
/**
* TODO: 子菜单的菜单项
*/
children?: KItemType[];
children?: SubMenuType[];
/**
* TODO: 子菜单样式,mode="inline" 时无效
*/
popupClassName?: string;
[property: string]: any
};

// TODO: onTitleClick 点击子菜单标题

// TODO: Items Slots slots label 分组标题
// TODO: Items Slots slots icon 菜单图标


// TODO: 高度动画
// TODO: 缩略文字
// TODO: 背景色随着层级加深
2 changes: 2 additions & 0 deletions preset/src/rules/index.ts
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import { carouselRules } from './src/carousel';
import { skeletonRules } from './src/skeleton';
import { colorPickerRules } from './src/color-picker';
import { timelineRules } from './src/timeline';
import { menuRules } from './src/menu';
import { baseRules } from './src/base';

declare type dynamicRulesFunc = (...args: any[]) => Array<RegExp | ((...args: any[]) => any)>;
@@ -34,6 +35,7 @@ export const defaultRules = {
...carouselRules,
...skeletonRules,
...colorPickerRules,
...menuRules,
...timelineRules,
...baseRules,
...getColCls(),
7 changes: 7 additions & 0 deletions preset/src/rules/src/menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const menuRules = {
'k-menu-w': { width: 'calc(100% - 8px)' },
'k-menu-transition-c': { transition: 'color 0.3s' },
'k-menu-transition-o': { transition: 'opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),margin 0.3s,color 0.3s' },
'k-menu-transition-w': { transition: 'width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s' },
'k-menu-transition': { transition: 'border-color 0.3s,background 0.3s,padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1)' },
};
1 change: 1 addition & 0 deletions preset/src/shortcuts/src/common.ts
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ export const commonShortcuts: UserShortcuts<Theme> = {
infcc: 'inline-flex justify-center items-center',
'bd-1': 'border-t-1 border-l-1 border-b-1 border-r-1',
'bdt-1': 'border-t-1 border-l-0 border-b-0 border-r-0',
'bdr-1': 'border-t-0 border-l-0 border-b-0 border-r-1',
'bdl-1': 'border-l-1 border-t-0 border-b-0 border-r-0',
'bdb-1': 'border-l-0 border-t-0 border-b-1 border-r-0',
'bdlb-1': 'border-l-1 border-t-0 border-b-1 border-r-0',
33 changes: 32 additions & 1 deletion preset/src/shortcuts/src/menu.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
export const menuShortcuts: Record<string, string> = {
'k-menu': ''
'k-menu': 'bg-white text-ikun-tx-base p-0 m-0 box-border text-14px leading-0 list-none outline-none focus:outline-none k-menu-transition-w',
'k-menu-item': 'box-border',
'k-menu-item-icon': 'inline-flex text-inherit items-center leading-0',
'k-menu-title-content': 'box-border k-menu-transition-c',
'k-menu-title-content-i': 'm-is-10px k-menu-transition-o opacity-[1]',
'k-menu-item-divider': 'my-0',



'k-menu-sub': 'border-none',


'k-menu-horizontal': '',
'k-menu-item-horizontal': '',





'k-menu-item-selected': 'ikun:20:bg-ikun-main text-ikun-main hover:(ikun:20:bg-ikun-main)',
'k-menu-item-hover-ih': 'hover:(ikun:6:bg-ikun-black)',
'k-menu-item-vh-child': 'justify-between',

'k-menu-inline': 'bdr-1 b-e-solid border-ikun-bd-base',
'k-menu-vertical': 'bdr-1 b-e-solid border-ikun-bd-base',
'k-menu-item-vertical': '',
'k-menu-item-inline': 'cursor-pointer k-menu-w k-menu-transition ' +
'flex items-center pr h-40px leading-40px list-style-position truncate ps-16px p-ie-16px m-is-4px m-ie-4px m-bs-4px m-be-4px ' +
'rounded-8px active:(ikun:20:bg-ikun-main)',
'k-menu-item-inline-group': 'k-menu-w k-menu-transition ' +
'flex items-center pr h-40px leading-40px list-style-position truncate ps-16px p-ie-16px m-is-4px m-ie-4px m-bs-4px m-be-4px ' +
'rounded-8px ikun:50:text-black',
};