diff --git a/packages/docs/scripts/generateApi.ts b/packages/docs/scripts/generateApi.ts index 7924c489c42c7164312ec64dc58cad900ec165bf..50dff716610f2ddfe600b32a3ab10d0a469cb35e 100644 --- a/packages/docs/scripts/generateApi.ts +++ b/packages/docs/scripts/generateApi.ts @@ -133,6 +133,7 @@ const pathReg = /\/(O.*)\.vue/; const tagTypes = { deprecated: '(warning)', }; +const exposeDesReg = /^\s*expose:([\s\S]+)/; glob('*/O*.vue', { cwd: srcDir, posix: true }).then((files) => { files.forEach(async (file, index) => { const fullPath = join(srcDir, file); @@ -212,14 +213,14 @@ glob('*/O*.vue', { cwd: srcDir, posix: true }).then((files) => { mdContent = `${mdContent}\n\n#### slots\n\n${markdownTable(slotsData)}`; } // expose - const selfExposed = meta.exposed.filter((expose) => expose.description); + const selfExposed = meta.exposed.filter((expose) => expose.description && exposeDesReg.test(expose.description)); if (selfExposed.length) { const tableHeader = { 'zh-CN': ['名称', '类型', '说明'], 'en-US': ['Name', 'Type', 'Description'], }; - let exposeData =selfExposed.map((expose) => { - return [expose.name, expose.type, expose.description]; + let exposeData = selfExposed.map((expose) => { + return [expose.name, expose.type, (expose.description.match(exposeDesReg)?.[1] || '').trim()]; }); exposeData.unshift(tableHeader[lang]); exposeData = cleanTableData(exposeData); diff --git a/packages/docs/src/assets/style/markdown.scss b/packages/docs/src/assets/style/markdown.scss index 65d65ddaa23e45316cbb18b843c6a3b3692764b6..89e5e369655afc5266d7d60df028adb82d73101e 100644 --- a/packages/docs/src/assets/style/markdown.scss +++ b/packages/docs/src/assets/style/markdown.scss @@ -12,7 +12,8 @@ [data-o-theme$='dark'] pre span { color: var(--shiki-dark); } -p + .code-container, p + .o-table { +p + .code-container, +p + .o-table { margin-top: 8px; } ol, @@ -81,3 +82,29 @@ li { } } } + +.markdown-body { + padding: 0 var(--o3-gap-5); + .markdown-body { + padding: 0; + } + .o-table-wrap { + overflow-x: auto; + th:first-child, + td:first-child { + position: sticky; + left: 0; + } + td:first-child { + background-color: var(--table-bg-color); + } + @media (hover: hover) { + tr:hover td { + background-color: var(--table-row-hover); + } + } + } + @include respond-to('<=pad_v') { + padding: 0; + } +} diff --git a/packages/docs/src/assets/style/style.scss b/packages/docs/src/assets/style/style.scss index 237e243ff1307dc1d1a5986faa67505a72e115be..daeb67f17606c27b5c035dcabb2f8d379ee3ae28 100644 --- a/packages/docs/src/assets/style/style.scss +++ b/packages/docs/src/assets/style/style.scss @@ -154,13 +154,3 @@ section { border-color: #aaa; } } - -.markdown-body { - padding: 0 var(--o3-gap-5); - .markdown-body { - padding: 0; - } - @include respond-to('<=pad_v') { - padding: 0; - } -} diff --git a/packages/docs/src/components/TheHeader.vue b/packages/docs/src/components/TheHeader.vue index 167ce136db1a8b0d9c55a79fa356c66c31ee1d21..ad3583890d1a8a27f5b3c18237c8bb1b4255ba2d 100644 --- a/packages/docs/src/components/TheHeader.vue +++ b/packages/docs/src/components/TheHeader.vue @@ -128,6 +128,9 @@ watch(locale, (newLocale, oldLocale) => { left: 50%; top: 50%; transform: translate3d(-50%, -50%, 0); + @include respond-to('phone') { + display: none; + } } .left { display: flex; diff --git a/packages/docs/src/utils/getHeads.ts b/packages/docs/src/utils/getHeads.ts index 4d8fbf65dfd081a7667fae45faf04bdd5027dfe0..c6b0b510672aee8d13facc0cdf25778d40c8b559 100644 --- a/packages/docs/src/utils/getHeads.ts +++ b/packages/docs/src/utils/getHeads.ts @@ -1,13 +1,20 @@ /** * 将h标签的标题转换为规范的id(该id会作为h标签的id以及a便签的href) - * @param title 待转换的标题 + * @param str 待转换的标题 * @returns id */ -function escapeTitle(title: string) { - return title +function slugify(str: string) { + return str + // 将驼峰转为中横线 + .replace(/(?<=[a-z])([A-Z])|(?<=[A-Z])([A-Z][a-z])/g, '-$&') .toLowerCase() + // 将空白字符转为中横线 .replace(/\s+/g, '-') - // 转换特殊字符,但不转化 unicode 字符 + // 合并多个中横线 + .replace(/-+/g, '-') + // 移除首尾中横线 + .replace(/(^-|-$)/g, '') + // 转义特殊字符 .replace(/[;,/?:@&=+$#]/g, (char) => encodeURIComponent(char)); } /** @@ -16,11 +23,11 @@ function escapeTitle(title: string) { * @param minLevel 查找的最小级别,默认为2,即只查找h2到h6的标题 * @returns 查找结果,格式为[{title: string, level: number, id: string}] */ -export function getHeads(el: HTMLElement, minLevel = 2) { - let headerId: Record = {}; +export function getHeads(el: HTMLElement, _minLevel = 2) { + const headerId: Record = {}; const heads: Array<{ title: string; level: number; id: string }> = []; let levels = ''; - minLevel = Math.max(Math.floor(minLevel), 1); + const minLevel = Math.max(Math.floor(_minLevel), 1); if (minLevel > 6) { return heads; } @@ -38,7 +45,7 @@ export function getHeads(el: HTMLElement, minLevel = 2) { if (dom.id) { id = dom.id; } else { - id = escapeTitle(title); + id = slugify(title); } // 判断是否有重名id,如果有则加上数字编号;该id会作为锚点的href if (headerId[id]) { diff --git a/packages/opendesign/src/dialog/ODialog.vue b/packages/opendesign/src/dialog/ODialog.vue index ca982b139e27e5f0b99b34d345c3a97cd7f810bf..42d4a4419e9b6265abfb193bb43c3ddb7165bb43 100644 --- a/packages/opendesign/src/dialog/ODialog.vue +++ b/packages/opendesign/src/dialog/ODialog.vue @@ -46,6 +46,7 @@ const scrollbarProps = computed(() => { }); defineExpose({ + /** expose: Toggle the ODialog */ toggle(show?: boolean) { layerRef.value?.toggle(show); }, diff --git a/packages/opendesign/src/dialog/__docs__/__case__/DialogActions.vue b/packages/opendesign/src/dialog/__docs__/__case__/DialogActions.vue new file mode 100644 index 0000000000000000000000000000000000000000..c32206416446bb53d79560c2a4b719c28df71c51 --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/__case__/DialogActions.vue @@ -0,0 +1,48 @@ + + + +### 底部操作按钮 + +`ODialog` 可以通过 `actions` 属性定义对话框底部的操作按钮。`actions` 属性的类型为 [DialogActionT\[\]](#dialog-action-t)。 + + + +### Bottom Action Buttons + +Bottom action buttons in `ODialog` can be defined via the `actions` prop. The `actions` prop is of type [DialogActionT\[\]](#dialog-action-t). + + + + diff --git a/packages/opendesign/src/dialog/__docs__/__case__/DialogSizeUsage.vue b/packages/opendesign/src/dialog/__docs__/__case__/DialogSizeUsage.vue new file mode 100644 index 0000000000000000000000000000000000000000..c36d58471bc299674741d5fd2ace6abf9235ce92 --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/__case__/DialogSizeUsage.vue @@ -0,0 +1,65 @@ + + + +### 尺寸 + +ODialog 有不同的尺寸:`'exlarge'` `'large'` `'medium'` `'small'` `'auto'`,通过 `size` 属性设置 + +- 当尺寸为 `auto` 时,对话框的大小适应内容。 +- 当尺寸为其它值时,对话框的宽是相对固定的,高度会被钳制在对应值内。 + 当客户端视口尺寸变化时,对话框尺寸会根据 `size` 自动适配,这在大部分情况下都能获得较好的效果,否则您可以将 `noResponsive` 设置为 `true` 取消自动适配,或者通过 css 变量细颗粒地自定义尺寸。 +- 将 `phoneHalfFull` 设置为 `true`,移动端(视口宽度小于 600px)对话框的宽度会占满视口。**注**: + - 此时 `size` 只影响高度,不影响宽度 + - `noResponsive` 不能为 `true` + + + +### Size + +ODialog offers various size options: `'exlarge'`, `'large'`, `'medium'`, `'small'`, and `'auto'`, configured through the `size` prop. + +- When set to `auto`, the dialog adapts its dimensions to fit the content. +- For other size values, the dialog maintains a relatively fixed width while its height is clamped within the corresponding range. + When the viewport size changes, the dialog automatically adjusts based on the `size` setting. This generally provides optimal display results. + To disable this responsive behavior, set `noResponsive` to `true`. Alternatively, use CSS variables for granular customization. +- Setting `phoneHalfFull` to `true` enables full-width display on mobile devices (viewport width < 600px). **Notes**: + - In this mode, `size` only affects height and does not impact width + - `noResponsive` must not be set to `true` + + diff --git a/packages/opendesign/src/dialog/__docs__/__case__/DialogSlot.vue b/packages/opendesign/src/dialog/__docs__/__case__/DialogSlot.vue new file mode 100644 index 0000000000000000000000000000000000000000..9fac140b5d90b3988a8087ca20a47e2880cb0c53 --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/__case__/DialogSlot.vue @@ -0,0 +1,119 @@ + + + +### 插槽 + +对话框有四个插槽: + +- `header`: 头部,用于放置标题 +- `default`: 主体,用于放置内容。**注**:`default` 插槽中的内容有溢出滚动的功能(可通过 `scrollbar` 参数调整滚动条,参数类型[BaseScrollerPropsT](#base-scroller-props-t)),其余插槽位置固定不能滚动; +- `actions`: 按钮,用于自定义底部按钮 +- `footer`: 底部,用于防止底部内容。**注**:`footer` 插槽会覆盖 `actions` 插槽。 + + + + + + + \ No newline at end of file diff --git a/packages/opendesign/src/dialog/__docs__/index.en-US.md b/packages/opendesign/src/dialog/__docs__/index.en-US.md new file mode 100644 index 0000000000000000000000000000000000000000..410634e03a634c5895e323826ba8a6b124d6cd83 --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/index.en-US.md @@ -0,0 +1,73 @@ +--- +sidebar: ODialog +--- + +# ODialog + +## Demo + + + + +## API + +### CSS Variables + +| CSS Variable | Description | +| --- | --- | +| --dlg-close-size | Size of the close button | +| --dlg-close-color | Color of the close button | +| --dlg-close-color-hover | Color of the close button when hovered | +| --dlg-close-color-active | Color of the close button when active | +| --dlg-color | Text color | +| --dlg-header-color | Header color, overrides `--dlg-color` | +| --dlg-bg-color | Background color | +| --dlg-radius | Border radius | +| --dlg-shadow | Box shadow | +| --dlg-max-height | Maximum height | +| --dlg-min-height | Minimum height | +| --dlg-min-width | Minimum width | +| --dlg-width | Width | +| --dlg-margin | Margin | +| --dlg-edge-gap | Padding | +| --dlg-inner-gap | Spacing between the default header and footer | +| --dlg-header-gap | Spacing between the header and default content, overrides `--dlg-inner-gap` | +| --actions-justify | Alignment of the action buttons at the bottom | + + + +### DialogActionT + +```ts +interface DialogActionT { + id: string | number; + color?: 'normal' | 'primary' | 'success' | 'warning' | 'danger'; + variant?: 'solid' | 'outline' | 'text'; + size?: 'large' | 'medium' | 'small'; + label?: string; + round?: 'pill' | (string & {}); + icon?: Component; + disabled?: boolean; + loading?: boolean; + // Button click event callback function + onClick: () => void; +} +``` + +See [OButton API Props](/en-US/components/button#props) + +### BaseScrollerPropsT + +```ts +type BaseScrollerPropsT = { + disabledX: boolean; + disabledY: boolean; + duration: number; + showType: 'auto' | 'always' | 'hover' | 'never'; + size: 'medium' | 'small'; + autoUpdateOnScrollSize: boolean; + barClass?: string | undefined; +}; +``` + +See [Scroller API Props](/en-US/components/scroller#props) diff --git a/packages/opendesign/src/dialog/__docs__/index.zh-CN.md b/packages/opendesign/src/dialog/__docs__/index.zh-CN.md new file mode 100644 index 0000000000000000000000000000000000000000..2c6385a0e3d14c6af911a8d596c995ec5d210bd8 --- /dev/null +++ b/packages/opendesign/src/dialog/__docs__/index.zh-CN.md @@ -0,0 +1,74 @@ +--- +sidebar: ODialog 对话框 +--- + +# ODialog 对话框 + +## 示例 + + + + + +## API + +### CSS 变量 + +| CSS变量 | 描述 | +| --- | --- | +| --dlg-close-size | 关闭按钮大小 | +| --dlg-close-color | 关闭按钮颜色 | +| --dlg-close-color-hover | 鼠标悬停关闭按钮颜色 | +| --dlg-close-color-active | 鼠标按下关闭按钮颜色 | +| --dlg-color | 文字颜色 | +| --dlg-header-color | 标题颜色,覆盖 `--dlg-color` | +| --dlg-bg-color | 背景颜色 | +| --dlg-radius | 圆角 | +| --dlg-shadow | 阴影 | +| --dlg-max-height | 最大高度 | +| --dlg-min-height | 最小高度 | +| --dlg-min-width | 最小宽度 | +| --dlg-width | 宽度 | +| --dlg-margin | 外边距 | +| --dlg-edge-gap | 内边距 | +| --dlg-inner-gap | header default 和 footer 之间的间距 | +| --dlg-header-gap | header 和 default 之间的间距,覆盖 `--dlg-inner-gap` | +| --actions-justify | 底部操作按钮的对齐方式 | + + + +### DialogActionT + +```ts +interface DialogActionT { + id: string | number; + color?: 'normal' | 'primary' | 'success' | 'warning' | 'danger'; + variant?: 'solid' | 'outline' | 'text'; + size?: 'large' | 'medium' | 'small'; + label?: string; + round?: 'pill' | (string & {}); + icon?: Component; + disabled?: boolean; + loading?: boolean; + // 按钮点击事件回调函数 + onClick: () => void; +} +``` + +参考 [OButton API Props](/zh-CN/components/button#props) + +### BaseScrollerPropsT + +```ts +type BaseScrollerPropsT = { + disabledX: boolean; + disabledY: boolean; + duration: number; + showType: 'auto' | 'always' | 'hover' | 'never'; + size: 'medium' | 'small'; + autoUpdateOnScrollSize: boolean; + barClass?: string | undefined; +}; +``` + +参考 [Scroller API Props](/zh-CN/components/scroller#props) diff --git a/packages/opendesign/src/dialog/types.ts b/packages/opendesign/src/dialog/types.ts index 66e1383d9aaa7107731215a8ef62b0d094bf6c8c..badf76e965cc27fde0d0077fb5ad629db357fb59 100644 --- a/packages/opendesign/src/dialog/types.ts +++ b/packages/opendesign/src/dialog/types.ts @@ -22,13 +22,15 @@ export interface DialogActionT { export const dialogProps = { ...layerProps, /** - * 是否隐藏可以关闭 + * @zh-CN 是否隐藏对话框的关闭按钮 + * @en-US Whether to hide the close button of the dialog */ hideClose: { type: Boolean, }, /** - * 弹窗尺寸 + * @zh-CN 对话框尺寸 + * @en-US Dialog size */ size: { type: String as PropType, @@ -36,25 +38,29 @@ export const dialogProps = { }, /** - * 弹窗底部按钮 + * @zh-CN 对话框底部按钮 + * @en-US Dialog bottom button */ actions: { type: Array as PropType, }, /** - * 是否响应式 + * @zh-CN 是否禁用响应式 + * @en-US Whether to disable responsive */ noResponsive: { type: Boolean, }, /** - * 移动端响应为半屏 + * @zh-CN 移动端是否渲染为半屏(宽度占满,高度占一半) + * @en-US Whether to render as half screen (width full, height half) on mobile phone */ phoneHalfFull: { type: Boolean, }, /** - * 是否使用scrollbar + * @zh-CN 是否使用scrollbar,值为 false 不使用,值为 true 或 scrollbar 的配置对象则使用 + * @en-US Whether to use scrollbar, value false not use, value true or scrollbar configuration object to use */ scrollbar: { type: [Boolean, Object] as PropType>,