forked from opentiny/tiny-vue
Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
61aa2cd82c |
|
@ -51,7 +51,11 @@
|
||||||
"build:entry-react": "esno src/index.ts build:entry-react",
|
"build:entry-react": "esno src/index.ts build:entry-react",
|
||||||
"create:mapping-react": "esno src/commands/create/create-mapping-react.ts",
|
"create:mapping-react": "esno src/commands/create/create-mapping-react.ts",
|
||||||
"build:react": "esno src/index.ts build:react",
|
"build:react": "esno src/index.ts build:react",
|
||||||
"build:chartTheme": "esno src/index.ts build:chartTheme"
|
"// ----------------------OpenInula脚本---------------------- ": "",
|
||||||
|
"build:entry-openinula": "esno src/index.ts build:entry-openinula",
|
||||||
|
"create:mapping-openinula": "esno src/commands/create/create-mapping-openinula.ts",
|
||||||
|
"build:openinula": "esno src/index.ts build:openinula",
|
||||||
|
"create:ui-openinula": "esno src/index.ts create:ui-openinula"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
|
"@vue/babel-helper-vue-jsx-merge-props": "^1.4.0",
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import [[UNAME]] from './src/index[[SUFFIX]]'
|
||||||
|
import '@opentiny/vue-theme/[[NAME]]/index.less'
|
||||||
|
import { version } from './package.json'
|
||||||
|
|
||||||
|
|
||||||
|
[[UNAME]].version = version
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default [[UNAME]]
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/openinula-[[NAME]]",
|
||||||
|
"version": "3.[[MINOR]].0",
|
||||||
|
"description": "",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"module": "index.ts",
|
||||||
|
"sideEffects": false,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm -w build:ui-openinula $npm_package_name",
|
||||||
|
"//postversion": "pnpm build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@opentiny-internal/vue-test-utils": "workspace:*",
|
||||||
|
"vitest": "^0.31.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
|
"@opentiny/openinula-common": "workspace:~",
|
||||||
|
"@opentiny/openinula-icon": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~",
|
||||||
|
"@opentiny/vue-theme-mobile": "workspace:~"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import pc from './pc'
|
||||||
|
import mobile from './mobile'
|
||||||
|
import mobileFirst from './mobile-first'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const { tiny_mode = 'pc' } = props
|
||||||
|
|
||||||
|
const S = {
|
||||||
|
pc,
|
||||||
|
mobile,
|
||||||
|
'mobile-first': mobileFirst
|
||||||
|
}[tiny_mode]
|
||||||
|
|
||||||
|
return S(props)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/[[NAME]]/vue'
|
||||||
|
import { vc, If, Component, Slot, Transition, useSetup, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconClose, IconSuccess, IconError, IconHelp, IconWarning } from '@opentiny/openinula-icon'
|
||||||
|
|
||||||
|
const $constants = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function [[UNAME]](props) {
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
_constants,
|
||||||
|
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
const { ref, current: vm, parent } = useVm()
|
||||||
|
|
||||||
|
const { [[API]] } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
constants: _constants,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
[[TEMPLATE]]
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/[[NAME]]/vue'
|
||||||
|
import { vc, If, Component, Slot, Transition, useSetup, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconClose, IconSuccess, IconError, IconHelp, IconWarning } from '@opentiny/openinula-icon'
|
||||||
|
import '@opentiny/vue-theme-mobile/[[NAME]]/index.less'
|
||||||
|
|
||||||
|
const $constants = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function [[UNAME]](props) {
|
||||||
|
|
||||||
|
const {
|
||||||
|
_constants,
|
||||||
|
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const { ref, current: vm, parent } = useVm()
|
||||||
|
|
||||||
|
const { [[API]] } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
constants: _constants,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
[[TEMPLATE]]
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/[[NAME]]/vue'
|
||||||
|
import { vc, If, Component, Slot, Transition, useSetup, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconClose, IconSuccess, IconError, IconHelp, IconWarning } from '@opentiny/openinula-icon'
|
||||||
|
import '@opentiny/vue-theme/[[NAME]]/index.less'
|
||||||
|
|
||||||
|
const $constants = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function [[UNAME]](props) {
|
||||||
|
|
||||||
|
const {
|
||||||
|
_constants,
|
||||||
|
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const { ref, current: vm, parent } = useVm()
|
||||||
|
|
||||||
|
const { [[API]] } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
constants: _constants,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
[[TEMPLATE]]
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
* 生成入口文件,包括 pc.js / mobile.js / mobile-first.js / index.js
|
||||||
|
*/
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import { EOL as endOfLine } from 'node:os'
|
||||||
|
import { pathFromWorkspaceRoot, capitalizeKebabCase, prettierFormat, logGreen } from '../../shared/utils'
|
||||||
|
import { getAllModules2 } from './build-ui-openinula'
|
||||||
|
import handlebarsRender from './handlebars.render'
|
||||||
|
|
||||||
|
const version = (({ key }) => {
|
||||||
|
const packageJSON = fs.readJSONSync(pathFromWorkspaceRoot('packages/openinula/package.json'))
|
||||||
|
const packageJsonOption = packageJSON[key] || packageJSON
|
||||||
|
|
||||||
|
return packageJsonOption
|
||||||
|
})({ key: 'version' })
|
||||||
|
|
||||||
|
const outputDir = 'packages/openinula'
|
||||||
|
|
||||||
|
const fileNames = {
|
||||||
|
all: 'index.ts',
|
||||||
|
pc: 'pc.ts',
|
||||||
|
mobile: 'mobile.ts',
|
||||||
|
'mobile-first': 'mobile-first.ts'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMainTemplate() {
|
||||||
|
return `{{{include}}}
|
||||||
|
import { $prefix } from '@opentiny/openinula-common'
|
||||||
|
|
||||||
|
const components = [{{{components}}}]
|
||||||
|
|
||||||
|
export const version = '${version}'
|
||||||
|
|
||||||
|
export {
|
||||||
|
{{{components}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
{{{components}}}
|
||||||
|
} as any
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponents(mode) {
|
||||||
|
const modules = getAllModules2()
|
||||||
|
|
||||||
|
const components = modules
|
||||||
|
.filter((item) => item.type === 'component')
|
||||||
|
.filter((item) => mode === 'all' || !item.mode || item.mode.includes(mode))
|
||||||
|
|
||||||
|
return components
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEntry(mode) {
|
||||||
|
const OUTPUT_PATH = pathFromWorkspaceRoot(outputDir, fileNames[mode])
|
||||||
|
const MAIN_TEMPLATE = getMainTemplate({ mode })
|
||||||
|
const includeTemplate: string[] = []
|
||||||
|
const componentsTemplate: string[] = []
|
||||||
|
const components = getComponents(mode)
|
||||||
|
const PKG_PATH = pathFromWorkspaceRoot(outputDir, 'package.json')
|
||||||
|
const PKGContent = fs.readJSONSync(PKG_PATH)
|
||||||
|
const PKGDeps = {
|
||||||
|
'@opentiny/openinula-common': 'workspace:~'
|
||||||
|
}
|
||||||
|
|
||||||
|
components.forEach((item) => {
|
||||||
|
const component = capitalizeKebabCase(item.name)
|
||||||
|
PKGDeps[item.importName] = 'workspace:~'
|
||||||
|
componentsTemplate.push(` ${component}`)
|
||||||
|
const importName = mode === 'all' ? item.importName : `${item.importName}/src/${mode}`
|
||||||
|
includeTemplate.push(`import ${item.name} from '${importName}'`)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (mode === 'all') {
|
||||||
|
PKGContent.dependencies = PKGDeps
|
||||||
|
fs.writeFileSync(PKG_PATH, JSON.stringify(PKGContent, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = handlebarsRender({
|
||||||
|
template: MAIN_TEMPLATE,
|
||||||
|
data: {
|
||||||
|
include: includeTemplate.join(endOfLine),
|
||||||
|
components: componentsTemplate.join(',' + endOfLine)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const output = prettierFormat({ str: template })
|
||||||
|
|
||||||
|
fs.writeFileSync(OUTPUT_PATH, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildEntryOpenInula() {
|
||||||
|
;['all', 'pc', 'mobile', 'mobile-first'].forEach(createEntry)
|
||||||
|
|
||||||
|
logGreen(
|
||||||
|
`npm run build:entry done. [${outputDir}/index.ts,${outputDir}/pc.ts,${outputDir}/mobile.ts,${outputDir}/mobile-first.ts]`
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,479 @@
|
||||||
|
import { logGreen, kebabCase, capitalizeKebabCase } from '../../shared/utils'
|
||||||
|
import { pathFromWorkspaceRoot } from '../../shared/utils'
|
||||||
|
import fg from 'fast-glob'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { build, defineConfig } from 'vite'
|
||||||
|
import { getBabelOutputPlugin } from '@rollup/plugin-babel'
|
||||||
|
import { external } from '../../shared/config'
|
||||||
|
import type { Plugin, NormalizedOutputOptions, OutputBundle } from 'rollup'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import { sync as findUpSync } from 'find-up'
|
||||||
|
import replace from 'rollup-plugin-replace'
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
||||||
|
const moduleMap = require(pathFromWorkspaceRoot('packages/openinula/modules.json'))
|
||||||
|
type mode = 'pc' | 'mobile' | 'mobile-first'
|
||||||
|
|
||||||
|
const pathFromPackages = (...args) => pathFromWorkspaceRoot('packages', ...args)
|
||||||
|
|
||||||
|
let scopeName = '@opentiny'
|
||||||
|
let buildVersion = '1.0.0'
|
||||||
|
|
||||||
|
export interface Module2 {
|
||||||
|
/** 源码路径,如 vue/src/button/index.ts */
|
||||||
|
path: string
|
||||||
|
/** 模块类型,可选 component, template, module */
|
||||||
|
type: 'component' | 'template' | 'module'
|
||||||
|
/** 是否排除构建,例如组件尚未开发完,设置 true */
|
||||||
|
exclude?: boolean
|
||||||
|
/** 组件类型支持的模式 */
|
||||||
|
mode?: mode[]
|
||||||
|
/** 模块名称,如 Button */
|
||||||
|
name: string
|
||||||
|
/** 模块构建物路径,如 vue/button/lib/index */
|
||||||
|
libPath: string
|
||||||
|
/** 模块名,如 @opentiny/vue/vue/lib/button/src,@deprecated */
|
||||||
|
libName?: string
|
||||||
|
/** 模块的npm包名,如 @opentiny/vue-button */
|
||||||
|
importName: string
|
||||||
|
/** 构建物文件名,@deprecated */
|
||||||
|
tmpName?: string
|
||||||
|
/** 全局变量名,如 TinyButton */
|
||||||
|
global?: string
|
||||||
|
/** 组件名的大写形态,如 Button */
|
||||||
|
UpperName?: string
|
||||||
|
/** 组件名的小写形态,如 button */
|
||||||
|
LowerName?: string
|
||||||
|
/** 模块的路径 */
|
||||||
|
parentDir?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntryTasks(): Module2[] {
|
||||||
|
// 读取TinyVue组件库入口文件
|
||||||
|
return ['index', 'pc', 'mobile'].map((mode) => ({
|
||||||
|
path: `openinula/${mode}.ts`,
|
||||||
|
dtsRoot: true,
|
||||||
|
libPath: `openinula/${mode}`,
|
||||||
|
type: 'module',
|
||||||
|
name: kebabCase({ str: `${scopeName}/openinula` }),
|
||||||
|
global: capitalizeKebabCase('opentinyOpenInula'),
|
||||||
|
importName: `${scopeName}/openinula`
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllModules2(): Module2[] {
|
||||||
|
return getSortModules({ filterIntercept: () => true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSortModules = ({ filterIntercept }: { filterIntercept: Function }) => {
|
||||||
|
let modules: Module2[] = []
|
||||||
|
let componentCount = 0
|
||||||
|
const importName = `${scopeName}/openinula`
|
||||||
|
Object.entries(moduleMap).forEach(([key, module]) => {
|
||||||
|
let component = module as Module2
|
||||||
|
|
||||||
|
component.name = key
|
||||||
|
// filterIntercept过滤筛选命令行传过来的组件名称,只输出命令行传递过来的组件
|
||||||
|
if (filterIntercept(component) === true && component.exclude !== true) {
|
||||||
|
const dirs = component.path.split('/')
|
||||||
|
|
||||||
|
// 这段逻辑暂时没有用到
|
||||||
|
const componentName = dirs.slice(1, dirs.indexOf('src'))
|
||||||
|
// UpperName: Todo
|
||||||
|
component.UpperName = capitalizeKebabCase(componentName.pop() ?? '')
|
||||||
|
|
||||||
|
// LowerName: todo
|
||||||
|
component.LowerName = kebabCase({ str: component.UpperName })
|
||||||
|
|
||||||
|
// 工程的父文件夹
|
||||||
|
component.parentDir = componentName
|
||||||
|
|
||||||
|
// libPath: 'packages/todo/dist/pc.ts' 组件输出路径
|
||||||
|
component.libPath = component.path
|
||||||
|
.replace('openinula/src/', 'packages/')
|
||||||
|
.replace('openinula-common/src/', 'packages/common/')
|
||||||
|
.replace('openinula-locale/src/', 'packages/locale/')
|
||||||
|
.replace('openinula-icon/src/', 'packages/icon/')
|
||||||
|
.replace('/index.ts', '/src/index.js')
|
||||||
|
.replace('/src/', '/dist/lib/')
|
||||||
|
.replace('.jsx', '.js')
|
||||||
|
.replace('.tsx', '.js')
|
||||||
|
|
||||||
|
// libName: '@opentiny/vue/todo/pc'
|
||||||
|
component.libName = component.libPath
|
||||||
|
.replace('packages/', '')
|
||||||
|
.replace('/index', '')
|
||||||
|
.replace('.js', '')
|
||||||
|
.replace('/dist/', '/')
|
||||||
|
.replace(/\/lib$/, '')
|
||||||
|
|
||||||
|
// 处理子目录
|
||||||
|
if (componentName.length) {
|
||||||
|
component.libName = component.libName.replace(componentName.join('/'), '').replace(/^\//, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// importName: '@opentiny/vue-tag/pc'
|
||||||
|
component.importName = `${scopeName}/openinula` + '-' + component.libName
|
||||||
|
|
||||||
|
// libName: '@opentiny/vue/todo/pc'
|
||||||
|
component.libName = importName + '/' + component.libName
|
||||||
|
|
||||||
|
// tmpName: 'pc'
|
||||||
|
component.tmpName = component.libPath.replace('.ts', '').split('/').slice(-1)[0]
|
||||||
|
|
||||||
|
// global: 'TinyTodoPc'
|
||||||
|
component.global = 'Tiny' + key
|
||||||
|
|
||||||
|
component.importName = `${scopeName}/openinula-${kebabCase({ str: key })}`
|
||||||
|
|
||||||
|
// "vue-common/src/index.ts" ==> "vue-common/lib/index"
|
||||||
|
if (component.type === 'module') {
|
||||||
|
component.libPath = component.path.replace('/src/', '/lib/').replace('index.ts', 'index')
|
||||||
|
}
|
||||||
|
|
||||||
|
// "vue/src/button/index.ts" ==> "button/lib/index"
|
||||||
|
if (component.type === 'component') {
|
||||||
|
component.libPath = component.path.replace('openinula/src/', '').replace('/index.ts', '/lib/index')
|
||||||
|
}
|
||||||
|
|
||||||
|
// "vue/src/button/src/mobile-first.vue" ==> "button/lib/mobile-first"
|
||||||
|
if (component.type === 'template') {
|
||||||
|
component.libPath = component.path.replace('openinula/src/', '').replace('/src/', '/lib/').replace(/\..+$/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
modules.push(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
component.type === 'component' && componentCount++
|
||||||
|
})
|
||||||
|
|
||||||
|
return modules
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定条件搜索原始模块列表
|
||||||
|
* @private
|
||||||
|
* @param {Function} filterIntercept 搜索条件
|
||||||
|
*/
|
||||||
|
const getModules = (filterIntercept: Function) => {
|
||||||
|
let modules = {}
|
||||||
|
|
||||||
|
if (typeof filterIntercept === 'function') {
|
||||||
|
for (const key in moduleMap) {
|
||||||
|
const component = moduleMap[key]
|
||||||
|
|
||||||
|
if (filterIntercept(component) === true && component.exclude !== true) {
|
||||||
|
modules[key] = component
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modules = moduleMap
|
||||||
|
}
|
||||||
|
|
||||||
|
return modules
|
||||||
|
}
|
||||||
|
|
||||||
|
const getByName = ({
|
||||||
|
name,
|
||||||
|
inversion = false,
|
||||||
|
isOriginal = false
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
inversion?: boolean
|
||||||
|
isOriginal?: boolean
|
||||||
|
}) => {
|
||||||
|
const callback = (item) => {
|
||||||
|
const result = new RegExp(`/${name}/|^openinula-${name}/`).test(item.path)
|
||||||
|
return inversion ? !result : result
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOriginal ? getModules(callback) : getSortModules({ filterIntercept: callback })
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTasks(names: string[]) {
|
||||||
|
// 没有指定组件,则全量构建
|
||||||
|
if (names.length === 0) {
|
||||||
|
return [...getAllModules2(), ...getEntryTasks()]
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
.map((name) =>
|
||||||
|
getByName({
|
||||||
|
name: kebabCase({ str: name.replace('@opentiny/openinula-', '') })
|
||||||
|
// isSort: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.flat()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllIcons = () => {
|
||||||
|
const entries = fg.sync('openinula-icon*/src/*', { cwd: pathFromWorkspaceRoot('packages'), onlyDirectories: true })
|
||||||
|
|
||||||
|
return entries.map((item) => {
|
||||||
|
const name = path.basename(item)
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: item + '/index.ts',
|
||||||
|
libPath: item.replace('/src/', '/lib/'),
|
||||||
|
type: 'component',
|
||||||
|
componentType: 'icon',
|
||||||
|
name: kebabCase({ str: name }),
|
||||||
|
global: capitalizeKebabCase(name),
|
||||||
|
importName: '@opentiny/openinula-' + item
|
||||||
|
} as Module2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toEntry(libs) {
|
||||||
|
return libs.reduce((result, { libPath, path: file }) => {
|
||||||
|
const tLibPath = libPath.replace('-lib/', '/lib/')
|
||||||
|
result[tLibPath] = pathFromPackages(file)
|
||||||
|
return result
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toTsInclued(libs) {
|
||||||
|
return new Set(
|
||||||
|
libs
|
||||||
|
.filter((item) => ['module', 'component'].includes(item.type))
|
||||||
|
.map((lib) => `packages/${lib.dtsRoot ? lib.path : path.dirname(lib.path)}`)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePackageJson({ beforeWriteFile }): Plugin {
|
||||||
|
return {
|
||||||
|
name: 'opentiny-vue:generate-package-json',
|
||||||
|
generateBundle(output: NormalizedOutputOptions, bundle: OutputBundle) {
|
||||||
|
const cache = {}
|
||||||
|
Object.entries(bundle).forEach(([, item]) => {
|
||||||
|
// 主入口文件, button/index.ts, common/src/index.ts
|
||||||
|
if (item.type === 'chunk' && /\/index\.(js|ts)/.test(item.facadeModuleId!)) {
|
||||||
|
// 从源文件中往上查找最近的 package.json 文件
|
||||||
|
const packageJsonFile = findUpSync('package.json', { cwd: item.facadeModuleId! })
|
||||||
|
|
||||||
|
if (!packageJsonFile) return
|
||||||
|
|
||||||
|
if (cache[packageJsonFile]) return
|
||||||
|
|
||||||
|
let packageJson
|
||||||
|
try {
|
||||||
|
packageJson = JSON.parse(fs.readFileSync(packageJsonFile, { encoding: 'utf-8' }))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const { filePath, content } = beforeWriteFile(path.dirname(item.fileName), packageJson)
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
this.emitFile({
|
||||||
|
type: 'asset',
|
||||||
|
fileName: `${filePath}/package.json`,
|
||||||
|
source: typeof content === 'string' ? content : JSON.stringify(content, null, 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const changelogFile = path.join(path.dirname(packageJsonFile), 'CHANGELOG.md')
|
||||||
|
if (fs.existsSync(changelogFile)) {
|
||||||
|
this.emitFile({
|
||||||
|
type: 'asset',
|
||||||
|
fileName: `${filePath}/CHANGELOG.md`,
|
||||||
|
source: fs.readFileSync(changelogFile, { encoding: 'utf-8' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[packageJsonFile] = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const getOpenInulaPlugins = (openinulaVersion: string) => {
|
||||||
|
// const pluginMap = {
|
||||||
|
// '18': () => {
|
||||||
|
// // const openinula18Plugin = requireModules('examples/openinula-docs/node_modules/@vitejs/plugin-openinula')
|
||||||
|
// const openinula18Plugin = requireModules('examples/openinula-docs/node_modules/@vitejs/plugin-react')
|
||||||
|
|
||||||
|
// const openinula18SvgPlugin = svgr
|
||||||
|
|
||||||
|
// return [openinula18Plugin(), openinula18SvgPlugin()]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return pluginMap[openinulaVersion]()
|
||||||
|
// }
|
||||||
|
|
||||||
|
function getBaseConfig() {
|
||||||
|
return defineConfig({
|
||||||
|
publicDir: false,
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.ts', '.tsx', '.jsx']
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
'process.env.BUILD_TARGET': JSON.stringify('component')
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// ...getOpenInulaPlugins('18'),
|
||||||
|
generatePackageJson({
|
||||||
|
beforeWriteFile: (filePath, content) => {
|
||||||
|
const dependencies: any = {}
|
||||||
|
|
||||||
|
Object.entries(content.dependencies).forEach(([key, value]) => {
|
||||||
|
// 只替换 openinula 系的依赖,方便调试
|
||||||
|
// 其他公用的依赖,vue 之前可能发过包
|
||||||
|
const newKey = key.replace('@opentiny/openinula', `${scopeName}/openinula`)
|
||||||
|
if ((value as string).includes('workspace:~')) {
|
||||||
|
dependencies[newKey] = '*'
|
||||||
|
} else {
|
||||||
|
dependencies[newKey] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (filePath.includes('openinula-common')) {
|
||||||
|
dependencies.openinula = '18.2.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是主入口或者svg图标则直接指向相同路径
|
||||||
|
if (filePath === 'openinula' || filePath === 'openinula-icon') {
|
||||||
|
content.main = './index.js'
|
||||||
|
content.module = './index.js'
|
||||||
|
} else {
|
||||||
|
content.main = './lib/index.js'
|
||||||
|
content.module = './lib/index.js'
|
||||||
|
}
|
||||||
|
|
||||||
|
content.version = buildVersion
|
||||||
|
content.dependencies = dependencies
|
||||||
|
content.name = content.name.replace('@opentiny/openinula', `${scopeName}/openinula`)
|
||||||
|
|
||||||
|
delete content.devDependencies
|
||||||
|
delete content.private
|
||||||
|
delete content.exports
|
||||||
|
|
||||||
|
return {
|
||||||
|
filePath: filePath.replace(/[\\/]lib$/, ''),
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchBuild({ tasks, formats, message, emptyOutDir, dts, outDir }) {
|
||||||
|
if (tasks.length === 0) return
|
||||||
|
logGreen(`====== 开始构建 ${message} ======`)
|
||||||
|
const entry = toEntry(tasks)
|
||||||
|
const dtsInclude = toTsInclued(tasks)
|
||||||
|
|
||||||
|
await build({
|
||||||
|
...getBaseConfig({ dts, dtsInclude }),
|
||||||
|
configFile: false,
|
||||||
|
build: {
|
||||||
|
outDir,
|
||||||
|
emptyOutDir,
|
||||||
|
minify: false,
|
||||||
|
rollupOptions: {
|
||||||
|
plugins: [
|
||||||
|
getBabelOutputPlugin({
|
||||||
|
presets: [['@babel/preset-env', { loose: true, modules: false }]]
|
||||||
|
}) as any,
|
||||||
|
replace({
|
||||||
|
'.less': '.css'
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'replace-scope',
|
||||||
|
transform(code) {
|
||||||
|
if (scopeName === '@opentiny') return code
|
||||||
|
|
||||||
|
let modifiedCode = code
|
||||||
|
while (modifiedCode.match(/@opentiny\/openinula/g)) {
|
||||||
|
modifiedCode = modifiedCode.replace('@opentiny/openinula', `${scopeName}/openinula`)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: modifiedCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
strict: false,
|
||||||
|
manualChunks: {}
|
||||||
|
},
|
||||||
|
external: (source, importer, isResolved) => {
|
||||||
|
// vite打包入口文件或者没有解析过得包不能排除依赖
|
||||||
|
if (isResolved || !importer) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子图标排除周边引用, 这里注意不要排除svg图标
|
||||||
|
if (/openinula-icon\/.+\/index/.test(importer)) {
|
||||||
|
return !/\.svg/.test(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @opentiny/vue 总入口,需要排除所有依赖
|
||||||
|
if (/openinula\/(index|pc|mobile|mobile-first)\.ts$/.test(importer)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['openinula', 'openinula/jsx-runtime'].includes(source)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.indexOf(scopeName) === 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return external(source)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lib: {
|
||||||
|
entry,
|
||||||
|
formats,
|
||||||
|
fileName: (format, entryName) => `${entryName}.js`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function batchBuildAll({ tasks, formats, message, emptyOutDir, dts, npmScope }) {
|
||||||
|
const rootDir = pathFromPackages('')
|
||||||
|
const outDir = path.resolve(rootDir, `dist-openinula/${npmScope}`)
|
||||||
|
await batchBuild({
|
||||||
|
tasks,
|
||||||
|
formats,
|
||||||
|
message,
|
||||||
|
emptyOutDir,
|
||||||
|
dts,
|
||||||
|
outDir
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildOpenInula(
|
||||||
|
names: string[] = [],
|
||||||
|
{ buildTarget = '1.0.0', formats = ['es'], clean = false, dts = true, scope = '@opentiny' }
|
||||||
|
) {
|
||||||
|
scopeName = scope
|
||||||
|
buildVersion = buildTarget
|
||||||
|
// 要构建的模块
|
||||||
|
let tasks = getTasks(names)
|
||||||
|
|
||||||
|
// 如果指定了打包icon或者没有传入任何组件
|
||||||
|
if (names.some((name) => name.includes('icon')) || !names.length) {
|
||||||
|
tasks.push(...getAllIcons())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 @opentiny/openinula
|
||||||
|
if (names.some((name) => [`${scopeName}/openinula`, 'openinula'].includes(name))) {
|
||||||
|
tasks.push(...getEntryTasks())
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = `TINY for openinula: ${JSON.stringify(names.length ? names : '全量')}`
|
||||||
|
|
||||||
|
await batchBuildAll({
|
||||||
|
tasks,
|
||||||
|
formats,
|
||||||
|
message,
|
||||||
|
emptyOutDir: clean,
|
||||||
|
dts,
|
||||||
|
npmScope: scope
|
||||||
|
})
|
||||||
|
}
|
|
@ -3,4 +3,5 @@ export * from './build-entry'
|
||||||
export * from './build-runtime'
|
export * from './build-runtime'
|
||||||
export * from './build-ui-react'
|
export * from './build-ui-react'
|
||||||
export * from './build-entry-react'
|
export * from './build-entry-react'
|
||||||
export * from './build-chart-theme'
|
export * from './build-ui-openinula'
|
||||||
|
export * from './build-entry-openinula'
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"Icon": {
|
||||||
|
"path": "openinula/icon/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"exclude": true
|
||||||
|
},
|
||||||
|
"Common": {
|
||||||
|
"path": "openinula/common/src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"exclude": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
import { capitalize, walkFileTree, pathFromWorkspaceRoot, logGreen, prettierFormat } from '../../shared/utils'
|
||||||
|
import { quickSort } from '../../shared/module-utils'
|
||||||
|
import path from 'node:path'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import commonMappingOpenInula from './common-mapping-openinula.json'
|
||||||
|
|
||||||
|
const getBuildEntryFile = (file, dirs, subPath) => {
|
||||||
|
// 模板文件(pc|mobile|mobile-first)需要同级目录有index.ts文件才能成为打包入口
|
||||||
|
const isTemplatePath = dirs.includes('index.ts')
|
||||||
|
const isMainEntry = file.includes('index') && dirs.includes('package.json')
|
||||||
|
const isPcEntry = file.includes('pc.') && subPath.includes(`src${path.sep}pc.`) && isTemplatePath
|
||||||
|
const isMobileEntry = file.includes('mobile.') && subPath.includes(`src${path.sep}mobile.`) && isTemplatePath
|
||||||
|
const isMobileFirstEntry =
|
||||||
|
file.includes('mobile-first.') && subPath.includes(`src${path.sep}mobile-first.`) && isTemplatePath
|
||||||
|
return {
|
||||||
|
isBuildEntryFile: isMainEntry || isPcEntry || isMobileEntry || isMobileFirstEntry,
|
||||||
|
isMainEntry,
|
||||||
|
isPcEntry,
|
||||||
|
isMobileEntry,
|
||||||
|
isMobileFirstEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempMap = {
|
||||||
|
'pc.jsx': 'pc',
|
||||||
|
'mobile.jsx': 'mobile',
|
||||||
|
'mobile-first.jsx': 'mobile-first',
|
||||||
|
'pc.tsx': 'pc',
|
||||||
|
'mobile.tsx': 'mobile',
|
||||||
|
'mobile-first.tsx': 'mobile-first'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTemplateName = (currentPaths, entryObj) => {
|
||||||
|
const entryMaps = {
|
||||||
|
isPcEntry: 'Pc',
|
||||||
|
isMobileEntry: 'Mobile',
|
||||||
|
isMobileFirstEntry: 'MobileFirst',
|
||||||
|
isMainEntry: ''
|
||||||
|
}
|
||||||
|
const mapKey = Object.keys(entryObj).filter((item) => entryObj[item] && item !== 'isBuildEntryFile')[0]
|
||||||
|
const subFix = entryMaps[mapKey]
|
||||||
|
return `${currentPaths.split('-').map(capitalize).join('')}${subFix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const writeModuleMap = (moduleMap) => {
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathFromWorkspaceRoot('packages/openinula/modules.json'),
|
||||||
|
prettierFormat({
|
||||||
|
str: typeof moduleMap === 'string' ? moduleMap : JSON.stringify(moduleMap),
|
||||||
|
options: {
|
||||||
|
parser: 'json',
|
||||||
|
printWidth: 10
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeOpenInulaModules() {
|
||||||
|
const templates = { ...commonMappingOpenInula }
|
||||||
|
|
||||||
|
walkFileTree({
|
||||||
|
isDeep: true,
|
||||||
|
dirPath: pathFromWorkspaceRoot('packages/openinula/src'),
|
||||||
|
fileFilter({ file }) {
|
||||||
|
return !/node_modules/.test(file)
|
||||||
|
},
|
||||||
|
callback({ file, subPath, dirs }) {
|
||||||
|
const entryObj = getBuildEntryFile(file, dirs, subPath)
|
||||||
|
const mode: string[] = []
|
||||||
|
|
||||||
|
if (entryObj.isMainEntry && dirs.includes('src')) {
|
||||||
|
const srcPath = subPath.replace(file, 'src')
|
||||||
|
const srcFiles = fs.readdirSync(srcPath) || []
|
||||||
|
srcFiles.forEach((item) => {
|
||||||
|
if (tempMap[item]) {
|
||||||
|
mode.push(tempMap[item])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entryObj.isBuildEntryFile) {
|
||||||
|
const modulePath = subPath.slice(subPath.lastIndexOf(`openinula${path.sep}src`)).replaceAll(path.sep, '/')
|
||||||
|
const matchArr = modulePath.match(/.+\/(.+?)\/(index\.ts|src\/pc\.|src\/mobile\.|src\/mobile-first\.)/)
|
||||||
|
if (matchArr?.[1]) {
|
||||||
|
const compName = getTemplateName(matchArr[1], entryObj)
|
||||||
|
templates[compName] = {
|
||||||
|
path: modulePath,
|
||||||
|
type: entryObj.isMainEntry ? 'component' : 'template',
|
||||||
|
exclude: false
|
||||||
|
}
|
||||||
|
if (mode.length > 0) {
|
||||||
|
templates[compName].mode = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const modulesJson = quickSort({ sortData: templates, returnType: 'object' })
|
||||||
|
|
||||||
|
writeModuleMap(modulesJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
makeOpenInulaModules()
|
||||||
|
|
||||||
|
logGreen('npm run create:mapping-openinula done.')
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(e)
|
||||||
|
}
|
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* pnpm create:ui-openinula 新建组件,支持格式如下:
|
||||||
|
*
|
||||||
|
* pnpm create:ui-openinula alert 新建组件后,请及时测试,如果报错请根据报错内容做局部修改
|
||||||
|
*/
|
||||||
|
import path from 'node:path'
|
||||||
|
import fs from 'fs-extra'
|
||||||
|
import semver from 'semver'
|
||||||
|
import {
|
||||||
|
walkFileTree,
|
||||||
|
capitalizeKebabCase,
|
||||||
|
logGreen,
|
||||||
|
logYellow,
|
||||||
|
templatePath,
|
||||||
|
pathJoinFromCLI
|
||||||
|
} from '../../shared/utils'
|
||||||
|
import handlebarsRender from '../build/handlebars.render'
|
||||||
|
import { parse } from '@vue/compiler-sfc'
|
||||||
|
import { requireModules } from '../build/build-ui'
|
||||||
|
|
||||||
|
// 从renderless中获取组件API
|
||||||
|
function getApi(name) {
|
||||||
|
const { api } = requireModules(`packages/renderless/src/${name}/vue`)
|
||||||
|
let apiStr = JSON.stringify(api)
|
||||||
|
apiStr = apiStr.replaceAll('"', '').replace('[', '').replace(']', '')
|
||||||
|
return apiStr
|
||||||
|
}
|
||||||
|
// vue index中获取组件Props,该部分待完成
|
||||||
|
// function getProps(name) {
|
||||||
|
// let path = pathJoinFromCLI('../../packages/vue/src/alert/src/index.ts')
|
||||||
|
// let fileContent = fs.readFileSync(path, { encoding: 'utf8' })
|
||||||
|
// // return props
|
||||||
|
// }
|
||||||
|
// 解析vue template部分
|
||||||
|
function vueToJSX(templateSource) {
|
||||||
|
const { descriptor } = parse(templateSource)
|
||||||
|
if (descriptor.template) {
|
||||||
|
const ast = descriptor.template.ast
|
||||||
|
const jsx = JSXFromAST(ast)
|
||||||
|
return jsx
|
||||||
|
} else {
|
||||||
|
throw new Error('No template found in the source.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历Vue AST解析成符合openInula规范的组件模板 E:\tinyvue\tsconfig.vue3.json
|
||||||
|
function JSXFromAST(node) {
|
||||||
|
let jsx = ''
|
||||||
|
// If和For标记
|
||||||
|
let tagName = ''
|
||||||
|
let tagAttr = ''
|
||||||
|
switch (node.type) {
|
||||||
|
case 1: // Element
|
||||||
|
if (node.props) {
|
||||||
|
node.props.forEach((attr) => {
|
||||||
|
if (attr.name === 'if') {
|
||||||
|
tagName = 'If'
|
||||||
|
tagAttr = ' v-if ={' + attr.exp.content + '}'
|
||||||
|
}
|
||||||
|
if (attr.name === 'for') {
|
||||||
|
tagName = 'For'
|
||||||
|
let list = attr.exp.content
|
||||||
|
list = list.slice(list.indexOf('in ') + 3)
|
||||||
|
tagAttr = ' list ={' + list + '}'
|
||||||
|
}
|
||||||
|
// class转换成className
|
||||||
|
if (attr.name === 'class') {
|
||||||
|
jsx += ' className="' + attr.value.content + '"'
|
||||||
|
}
|
||||||
|
// 含有:的动态属性添加大括号{},其中:class需要转换成className
|
||||||
|
if (attr.name === 'bind') {
|
||||||
|
if (attr.arg) {
|
||||||
|
if (attr.arg.content === 'class') {
|
||||||
|
jsx += ' className={vc(' + attr.exp.content + ')}'
|
||||||
|
} else {
|
||||||
|
jsx += ' ' + attr.arg.content + '={' + attr.exp.content + '}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 含有@的动态方法转添加on前缀
|
||||||
|
if (attr.name === 'on') {
|
||||||
|
// 有修饰符的exp可能为undefiend,修饰符待处理
|
||||||
|
if (attr.exp) {
|
||||||
|
jsx += ' on' + capitalizeKebabCase(attr.arg.content) + '={' + attr.exp.content + '}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 含有model的处理
|
||||||
|
// if (attr.name === 'model') {
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
jsx = '<' + node.tag + jsx + '>'
|
||||||
|
|
||||||
|
// 添加If和For外层包裹,前后添加换行符更好看
|
||||||
|
if (tagName) {
|
||||||
|
jsx = '\n<' + tagName + tagAttr + '>\n' + jsx
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
node.children.forEach((child) => {
|
||||||
|
jsx += JSXFromAST(child)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
jsx += '</' + node.tag + '>'
|
||||||
|
// 添加If和For外层包裹
|
||||||
|
if (tagName) {
|
||||||
|
jsx += '\n</' + tagName + '>'
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 2: // Text,此处多数是换行符
|
||||||
|
jsx += node.content
|
||||||
|
break
|
||||||
|
case 3: // 注释
|
||||||
|
jsx += node.loc.source
|
||||||
|
break
|
||||||
|
|
||||||
|
case 5: // 插值或组件内变量
|
||||||
|
jsx += `{${node.content.content}}`
|
||||||
|
break
|
||||||
|
|
||||||
|
// 其他节点类型...
|
||||||
|
default:
|
||||||
|
// 处理其他节点类型或抛出错误
|
||||||
|
logYellow(`======================node.type=${node.type}====================`)
|
||||||
|
logYellow(node)
|
||||||
|
// throw new Error('Unsupported node type: ' + node.type);
|
||||||
|
}
|
||||||
|
// 单独处理slot
|
||||||
|
jsx = jsx.replace('<slot', '<Slot slots={props.slots}').replace('</slot>', '</Slot>')
|
||||||
|
// 单独处理transition
|
||||||
|
jsx = jsx.replace('transition', 'Transition')
|
||||||
|
// 单独处理component组件
|
||||||
|
jsx = jsx.replace('component', 'Component')
|
||||||
|
|
||||||
|
// 最外层template标签替换成div并添加ref
|
||||||
|
jsx = jsx.replace('<template>', '<div ref={ref}>').replace('</template>', '</div>')
|
||||||
|
// windows下调整换行符
|
||||||
|
jsx = jsx.replace('\n', '\r\n')
|
||||||
|
return jsx
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createUi(names = []) {
|
||||||
|
const templateDir = path.join(templatePath, './openinula')
|
||||||
|
const componetDir = pathJoinFromCLI('../../packages/openinula/src')
|
||||||
|
const vueComponetDir = pathJoinFromCLI('../../packages/vue/src')
|
||||||
|
|
||||||
|
const { version } = fs.readJSONSync(pathJoinFromCLI('../../packages/openinula/package.json'))
|
||||||
|
|
||||||
|
names.forEach((componentName) => {
|
||||||
|
let componentPath = path.join(componetDir, componentName)
|
||||||
|
let vueComponentPath = path.join(vueComponetDir, componentName, 'src')
|
||||||
|
|
||||||
|
if (fs.existsSync(componentPath)) {
|
||||||
|
logYellow(`The component name : ${componentName} is exist , please enter other name.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 获取API
|
||||||
|
let api = getApi(componentName)
|
||||||
|
// 定义不同模板内容
|
||||||
|
let pcTemplate = ``
|
||||||
|
let mobileTemplate = ``
|
||||||
|
let mobileFirstTemplate = ``
|
||||||
|
|
||||||
|
walkFileTree({
|
||||||
|
isDeep: true,
|
||||||
|
dirPath: vueComponentPath,
|
||||||
|
callback({ file, subPath }) {
|
||||||
|
// 判断是否是.vue文件,否则会出错
|
||||||
|
if (file.split('.')[1] === 'vue') {
|
||||||
|
let fileContent = fs.readFileSync(subPath, { encoding: 'utf8' })
|
||||||
|
let content = vueToJSX(fileContent)
|
||||||
|
|
||||||
|
if (file === 'pc.vue') {
|
||||||
|
pcTemplate = content
|
||||||
|
}
|
||||||
|
if (file === 'mobile.vue') {
|
||||||
|
mobileTemplate = content
|
||||||
|
}
|
||||||
|
if (file === 'mobile-first.vue') {
|
||||||
|
mobileFirstTemplate = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
walkFileTree({
|
||||||
|
isDeep: true,
|
||||||
|
dirPath: templateDir,
|
||||||
|
callback({ file, subPath }) {
|
||||||
|
let fileName = file
|
||||||
|
// 每次遍历需要重置组件目录,否则会报错
|
||||||
|
componentPath = path.join(componetDir, componentName)
|
||||||
|
const isSrcDir = path.basename(path.dirname(subPath)) === 'src'
|
||||||
|
if (isSrcDir) {
|
||||||
|
componentPath = path.join(componentPath, 'src')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(componentPath)) {
|
||||||
|
fs.mkdirSync(componentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
let template = ''
|
||||||
|
if (file === 'pc.jsx') {
|
||||||
|
template = pcTemplate
|
||||||
|
}
|
||||||
|
if (file === 'mobile.jsx') {
|
||||||
|
template = mobileTemplate
|
||||||
|
}
|
||||||
|
if (file === 'mobile-first.jsx') {
|
||||||
|
template = mobileFirstTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileContent = fs.readFileSync(subPath, { encoding: 'utf8' })
|
||||||
|
const upperComponentName = capitalizeKebabCase(componentName)
|
||||||
|
|
||||||
|
// 编译模板
|
||||||
|
fileContent = handlebarsRender({
|
||||||
|
template: fileContent,
|
||||||
|
data: {
|
||||||
|
NAME: componentName,
|
||||||
|
UNAME: upperComponentName,
|
||||||
|
MINOR: semver.minor(version),
|
||||||
|
SUFFIX: '',
|
||||||
|
THEME: 'theme',
|
||||||
|
TEMPLATE: template,
|
||||||
|
API: api
|
||||||
|
},
|
||||||
|
delimiter: ['\\[\\[', '\\]\\]'],
|
||||||
|
options: { noEscape: true }
|
||||||
|
})
|
||||||
|
componentPath = path.join(componentPath, fileName)
|
||||||
|
fs.writeFileSync(componentPath, fileContent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
logGreen('Create ui openinula done for ' + componentName)
|
||||||
|
})
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
export * from './create-icon-saas'
|
export * from './create-icon-saas'
|
||||||
|
export * from './create-ui-openinula'
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import { Command, Option } from 'commander'
|
import { Command, Option } from 'commander'
|
||||||
import { createIconSaas } from './commands/create/index.js'
|
import { createIconSaas } from './commands/create/index.js'
|
||||||
import { buildUi, buildEntry, buildRuntime, buildReact, buildEntryReact, chartTheme } from './commands/build'
|
import {
|
||||||
|
buildUi,
|
||||||
|
buildEntry,
|
||||||
|
buildRuntime,
|
||||||
|
buildReact,
|
||||||
|
buildEntryReact,
|
||||||
|
buildOpenInula,
|
||||||
|
buildEntryOpenInula
|
||||||
|
} from './commands/build'
|
||||||
import { releaseAurora } from './commands/release/releaseAurora'
|
import { releaseAurora } from './commands/release/releaseAurora'
|
||||||
|
|
||||||
|
import { createUi } from './commands/create'
|
||||||
|
|
||||||
const program = new Command()
|
const program = new Command()
|
||||||
|
|
||||||
program.command('release:aurora').description('转换为aurora的包').action(releaseAurora)
|
program.command('release:aurora').description('转换为aurora的包').action(releaseAurora)
|
||||||
|
@ -12,9 +22,9 @@ program.command('create:icon-saas').description('同步生成 icon-saas').action
|
||||||
|
|
||||||
program.command('build:entry-react').description('生成 react 组件库入口').action(buildEntryReact)
|
program.command('build:entry-react').description('生成 react 组件库入口').action(buildEntryReact)
|
||||||
|
|
||||||
program.command('build:entry').description('生成组件库入口').action(buildEntry)
|
program.command('build:entry-openinula').description('生成 openinula 组件库入口').action(buildEntryOpenInula)
|
||||||
|
|
||||||
program.command('build:chartTheme').description('切换chart主题').action(chartTheme)
|
program.command('build:entry').description('生成组件库入口').action(buildEntry)
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('build:ui')
|
.command('build:ui')
|
||||||
|
@ -23,7 +33,6 @@ program
|
||||||
.addOption(new Option('-v --vue-versions <vueVersions...>', '目标框架,默认所有').choices(['2', '2.7', '3']))
|
.addOption(new Option('-v --vue-versions <vueVersions...>', '目标框架,默认所有').choices(['2', '2.7', '3']))
|
||||||
.addOption(new Option('-f --formats <formats...>', '目标格式,默认 ["es"]').choices(['es', 'cjs']))
|
.addOption(new Option('-f --formats <formats...>', '目标格式,默认 ["es"]').choices(['es', 'cjs']))
|
||||||
.addOption(new Option('-t --build-target <buildTarget>', '组件的目标版本'))
|
.addOption(new Option('-t --build-target <buildTarget>', '组件的目标版本'))
|
||||||
.addOption(new Option('-d --design <design>', '构建的目标设计规范'))
|
|
||||||
.option('-s, --scope <scope>', 'npm scope,默认是 opentiny,会以 @opentiny 发布到 npm')
|
.option('-s, --scope <scope>', 'npm scope,默认是 opentiny,会以 @opentiny 发布到 npm')
|
||||||
.option('-c, --clean', '清空构建目录')
|
.option('-c, --clean', '清空构建目录')
|
||||||
.option('--no-dts', '不生成 dts')
|
.option('--no-dts', '不生成 dts')
|
||||||
|
@ -48,4 +57,21 @@ program
|
||||||
.option('--no-dts', '不生成 dts')
|
.option('--no-dts', '不生成 dts')
|
||||||
.action(buildReact)
|
.action(buildReact)
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('build:openinula')
|
||||||
|
.description('打包 openinula 组件库')
|
||||||
|
.argument('[names...]', '构建指定组件,如 button alert;不指定则构建全量组件')
|
||||||
|
.addOption(new Option('-f --formats <formats...>', '目标格式,默认 ["es"]').choices(['es', 'cjs']))
|
||||||
|
.addOption(new Option('-t --build-target <buildTarget>', '组件的目标版本'))
|
||||||
|
.option('-s, --scope <scope>', 'npm scope,默认是 opentiny,会以 @opentiny 发布到 npm')
|
||||||
|
.option('-c, --clean', '清空构建目录')
|
||||||
|
.option('--no-dts', '不生成 dts')
|
||||||
|
.action(buildOpenInula)
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('create:ui-openinula')
|
||||||
|
.description(' openinula 组件库代码生成')
|
||||||
|
.argument('[names...]', '构建指定组件,如 button alert;不指定则构建全量组件')
|
||||||
|
.action(createUi)
|
||||||
|
|
||||||
program.parse()
|
program.parse()
|
||||||
|
|
32
package.json
32
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "opentiny-vue",
|
"name": "opentiny-vue",
|
||||||
"version": "3.14.0",
|
"version": "3.7.12",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@8.3.1",
|
"packageManager": "pnpm@8.3.1",
|
||||||
"description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.",
|
"description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.",
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"main": "packages/index.js",
|
"main": "packages/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16",
|
"node": ">=16",
|
||||||
"pnpm": ">=6.35"
|
"pnpm": ">=7"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
|
@ -59,28 +59,19 @@
|
||||||
"build:runtime": "pnpm -C internals/cli build:runtime",
|
"build:runtime": "pnpm -C internals/cli build:runtime",
|
||||||
"// ---------- 构建相关脚本 ----------": "",
|
"// ---------- 构建相关脚本 ----------": "",
|
||||||
"build:ui": "pnpm create:icon-saas && pnpm create:mapping && pnpm build:entry && gulp themeConcat && pnpm -C internals/cli build:ui",
|
"build:ui": "pnpm create:icon-saas && pnpm create:mapping && pnpm build:entry && gulp themeConcat && pnpm -C internals/cli build:ui",
|
||||||
"build:chartTheme": "pnpm -C internals/cli build:chartTheme",
|
|
||||||
"build:renderless": "pnpm -C packages/renderless build:fast",
|
"build:renderless": "pnpm -C packages/renderless build:fast",
|
||||||
"build:theme": "gulp themeConcat && pnpm -C packages/theme build:fast",
|
"build:theme": "gulp themeConcat && pnpm -C packages/theme build:fast",
|
||||||
"build:themeSaas": "pnpm -C packages/theme-saas build:fast",
|
"build:themeSaas": "pnpm -C packages/theme-saas build:fast",
|
||||||
"build:themeMobile": "pnpm -C packages/theme-mobile build:fast",
|
"build:themeMobile": "pnpm -C packages/theme-mobile build:fast",
|
||||||
"build:themejson": "gulp themeJson",
|
"build:themejson": "gulp themeJson",
|
||||||
"build:internals": "pnpm \"--filter=./internals/*\" build",
|
"build:internals": "pnpm \"--filter=./internals/*\" build",
|
||||||
"build:vite-import": "pnpm --filter @opentiny/vue-vite-import build",
|
"build:site": "gulp themeConcat && pnpm -C examples/sites build",
|
||||||
"build:virtual-template": "pnpm --filter @opentiny-internal/unplugin-virtual-template build",
|
|
||||||
"build:site": "gulp themeConcat && pnpm i -g pnpm && pnpm build:vite-import && pnpm build:virtual-template && pnpm -C examples/sites build",
|
|
||||||
"release:aurora": "pnpm -C internals/cli release:aurora",
|
"release:aurora": "pnpm -C internals/cli release:aurora",
|
||||||
"// ---------- 使用pnpm批量发布npm包 ----------": "",
|
"// ---------- 使用pnpm批量发布npm包 ----------": "",
|
||||||
"pub2": "pnpm --filter=\"./packages/dist2/**\" publish --tag v2-latest --no-git-checks --access=public",
|
"pub2": "pnpm --filter=\"./packages/dist2/**\" publish --tag v2-latest --no-git-checks --access=public",
|
||||||
"pub3": "pnpm --filter=\"./packages/dist3/**\" publish --no-git-checks --access=public",
|
"pub3": "pnpm --filter=\"./packages/dist3/**\" publish --no-git-checks --access=public",
|
||||||
"pub2.7": "pnpm --filter=\"./packages/dist2.7/**\" publish --tag v2.7-latest --no-git-checks --access=public",
|
"pub2.7": "pnpm --filter=\"./packages/dist2.7/**\" publish --tag v2.7-latest --no-git-checks --access=public",
|
||||||
"pub:aurora": "pnpm --filter=\"./packages/dist2/@aurora/**\" publish --no-git-checks --access=public",
|
"pub:aurora": "pnpm --filter=\"./packages/dist2/@aurora/**\" publish --no-git-checks --access=public",
|
||||||
"pub:theme": "pnpm --filter=\"./packages/theme/dist\" publish --no-git-checks --access=public",
|
|
||||||
"pub:themeMobile": "pnpm --filter=\"./packages/theme-mobile/dist/\" publish --no-git-checks --access=public",
|
|
||||||
"pub:themeSaas": "pnpm --filter=\"./packages/theme-saas/dist\" publish --no-git-checks --access=public",
|
|
||||||
"pub:renderless": "pnpm --filter=\"./packages/renderless/dist\" publish --no-git-checks --access=public",
|
|
||||||
"pub:all": "pnpm pub2 && pnpm pub3 && pnpm pub:theme && pnpm pub:themeMobile && pnpm pub:themeSaas && pnpm pub:renderless",
|
|
||||||
"pub:site": "pnpm -C examples/sites pub",
|
|
||||||
"// ---------- unit单元测试 ----------": "",
|
"// ---------- unit单元测试 ----------": "",
|
||||||
"test:unit2": "pnpm -C examples/vue2 test:unit",
|
"test:unit2": "pnpm -C examples/vue2 test:unit",
|
||||||
"test:unit2.7": "pnpm -C examples/vue2.7 test:unit",
|
"test:unit2.7": "pnpm -C examples/vue2.7 test:unit",
|
||||||
|
@ -89,18 +80,14 @@
|
||||||
"test:e2e2": "pnpm -C examples/vue2 test:e2e --project=chromium",
|
"test:e2e2": "pnpm -C examples/vue2 test:e2e --project=chromium",
|
||||||
"test:e2e2.7": "pnpm -C examples/vue2.7 test:e2e --project=chromium",
|
"test:e2e2.7": "pnpm -C examples/vue2.7 test:e2e --project=chromium",
|
||||||
"test:e2e3": "pnpm -C examples/vue3 test:e2e --project=chromium",
|
"test:e2e3": "pnpm -C examples/vue3 test:e2e --project=chromium",
|
||||||
"test:e2e2:mobile": "pnpm -C examples/vue2 test:e2e --project=android",
|
|
||||||
"test:e2e2.7:mobile": "pnpm -C examples/vue2.7 test:e2e --project=android",
|
|
||||||
"test:e2e3:mobile": "pnpm -C examples/vue3 test:e2e --project=android",
|
|
||||||
"// ---------- playwright下载chromium、firefox等浏览器内核 ----------": "",
|
"// ---------- playwright下载chromium、firefox等浏览器内核 ----------": "",
|
||||||
"install:browser": "pnpm -C examples/vue3 install:browser",
|
"install:browser": "pnpm -C examples/vue3 install:browser",
|
||||||
"// ---------- e2e测试代码生成器 ----------": "",
|
"// ---------- e2e测试代码生成器 ----------": "",
|
||||||
"codegen": "pnpm -C examples/vue3 codegen",
|
"codegen": "pnpm -C examples/vue3 codegen",
|
||||||
"codegen:mobile": "pnpm -C examples/vue3 codegen --device=\"Pixel 5\" ",
|
|
||||||
"format": "prettier --write --cache packages/**/{*.vue,*.js,*.ts,*.jsx,*.tsx,*.less} examples/**/{*.vue,*.js,*.ts,*.jsx,*.tsx} internals/**/{*.js,*.ts}",
|
"format": "prettier --write --cache packages/**/{*.vue,*.js,*.ts,*.jsx,*.tsx,*.less} examples/**/{*.vue,*.js,*.ts,*.jsx,*.tsx} internals/**/{*.js,*.ts}",
|
||||||
"lint": "eslint \"packages/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
"lint": "eslint \"packages/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
||||||
"lint:doc": "eslint \"examples/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
"lint:doc": "eslint \"examples/**/{*.vue,*.js,*.ts}\" --quiet --fix",
|
||||||
"clean:build": "rimraf packages/dist2 packages/dist3 packages/dist2.7 packages/renderless/dist packages/theme/dist packages/theme-saas/dist packages/theme-mobile/dist",
|
"clean:build": "rimraf packages/dist2 packages/dist3 packages/renderless/dist packages/dist2.7",
|
||||||
"clean:dependencies": "rm -rf node_modules /**/node_modules",
|
"clean:dependencies": "rm -rf node_modules /**/node_modules",
|
||||||
"// ---------- 构建【mf】版本 ----------": "",
|
"// ---------- 构建【mf】版本 ----------": "",
|
||||||
"preci:deployMfPatch": "pnpm clean:build && lerna version prepatch --conventional-prerelease --include-merged-tags --preid mf --no-push --yes",
|
"preci:deployMfPatch": "pnpm clean:build && lerna version prepatch --conventional-prerelease --include-merged-tags --preid mf --no-push --yes",
|
||||||
|
@ -138,17 +125,22 @@
|
||||||
"build:react-site": "pnpm --filter @opentiny/react-site build",
|
"build:react-site": "pnpm --filter @opentiny/react-site build",
|
||||||
"prettier": "prettier --config .prettierrc --write .",
|
"prettier": "prettier --config .prettierrc --write .",
|
||||||
"// ---------- openinula 相关脚本命令 ----------": "",
|
"// ---------- openinula 相关脚本命令 ----------": "",
|
||||||
"dev:openinula": "pnpm -C examples/openinula-docs run dev",
|
"dev:openinula": "pnpm create:mapping-openinula && pnpm build:entry-openinula && pnpm -C examples/openinula-docs run dev",
|
||||||
|
"build:entry-openinula": "pnpm -C internals/cli build:entry-openinula",
|
||||||
|
"create:mapping-openinula": "pnpm -C internals/cli create:mapping-openinula",
|
||||||
|
"build:openinula": "pnpm -C internals/cli build:openinula",
|
||||||
|
"build:ui-openinula": "pnpm create:mapping-openinula && pnpm build:entry-openinula && pnpm build:openinula",
|
||||||
|
"create:ui-openinula": "pnpm -C internals/cli create:ui-openinula",
|
||||||
"// ---------- solid 相关脚本命令 ----------": "",
|
"// ---------- solid 相关脚本命令 ----------": "",
|
||||||
"dev:solid": "pnpm -C examples/solid-docs run dev"
|
"dev:solid": "pnpm -C examples/solid-docs run dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/composition-api": "1.7.2",
|
"@vue/composition-api": "1.2.2",
|
||||||
"color": "^4.2.3",
|
"color": "^4.2.3",
|
||||||
"cropperjs": "1.5.12",
|
"cropperjs": "1.5.12",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"echarts": "5.4.1",
|
"echarts": "5.4.1",
|
||||||
"echarts-liquidfill": "3.1.0",
|
"echarts-liquidfill": "3.0.0",
|
||||||
"echarts-wordcloud": "2.0.0",
|
"echarts-wordcloud": "2.0.0",
|
||||||
"fastdom": "1.0.11",
|
"fastdom": "1.0.11",
|
||||||
"shepherd.js": "11.0.1",
|
"shepherd.js": "11.0.1",
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
# 开发文档
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
#### 2024-04-07
|
||||||
|
|
||||||
|
1. 修复pnpm build:openinula构建命令
|
||||||
|
2. 增加pnpm create:ui-openinula创建openInula组件命令
|
||||||
|
该命令主要使用了/internals/cli/src/create/create-ui-openinula的转换脚本,通过解析vue模板遍历ast,生成inula组件。目前该脚本已经实现了api和template的转换,能减少95%的人工转换工作量。
|
||||||
|
3. 修复事件点击报错。该错误来自适配层dom和vnode(对应react的fiber)的关联。
|
||||||
|
|
||||||
|
## 如何将已经开发好的Vue组件快速转换为openInula组件
|
||||||
|
|
||||||
|
执行 `pnpm create:ui-openinula +组件名称` 可在`/packages/openinula/src/`目录下生成对应组件,然后进行测试,根据需要进行少量修改即可。
|
||||||
|
|
||||||
|
## 开发成果
|
||||||
|
|
||||||
|
1. 基本工具的开发
|
||||||
|
|
||||||
|
- create-ui-openinula(已完成,通过该转换工具,可以将Vue模板转换成openInula组件模板)
|
||||||
|
- create-mapping-openinula(已完成,通过该工具,可以生成openinula组件的map文件。新增组件后需要调用)
|
||||||
|
- build-entry-openinula(已完成,通过该工具,可以生成openinula组件的入口文件。新增组件后需要调用)
|
||||||
|
- build-ui-openinula(已完成,通过该工具,可以打包openInula组件)
|
||||||
|
- common-mapping-openinula.json(map文件的公共部分)
|
||||||
|
|
||||||
|
调用顺序为create-ui-openinula(转换组件)=>create-mapping-openinula(创建map)=>build-ui-openinula(创建入口文件)
|
||||||
|
|
||||||
|
实际开发过程中通过命令行,首先执行`pnpm create:ui-openinula+组件名称`,然后执行`pnpm dev:openinula`
|
||||||
|
其中,`pnpm dev:openinula`命令已经包含了map文件生成和入口文件生成
|
||||||
|
|
||||||
|
2. openInula-common 适配层的开发,已完成全部API的转换
|
||||||
|
|
||||||
|
## 下载依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm i
|
||||||
|
```
|
||||||
|
|
||||||
|
## 将Vue组件转换位 openinula 组件
|
||||||
|
|
||||||
|
转换后,还需检查转换的代码,将缺少的props和constants补进代码,然后进行测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm create:ui-openinula +组件名称
|
||||||
|
for example:
|
||||||
|
pnpm create:ui-openinula button
|
||||||
|
```
|
||||||
|
|
||||||
|
## 生成 openinula 组件入口,每次生成新组件后需更新组件入口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build:entry-openinula
|
||||||
|
```
|
||||||
|
|
||||||
|
## 本地启动 openinula 调试项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev:openinula
|
||||||
|
```
|
||||||
|
|
||||||
|
## 本地启动 openinula 文档项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev:openinula-site
|
||||||
|
```
|
||||||
|
|
||||||
|
## 打包 openinula 组件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build:ui-openinula
|
||||||
|
```
|
||||||
|
|
||||||
|
运行此命令后,会在 pacakges-openinula 产生打包产物
|
||||||
|
一般是
|
||||||
|
packages/dist-openinula/@opention/button
|
||||||
|
... 单个组件产物
|
||||||
|
packages/dist-openinula/@opention/openinula-common
|
||||||
|
packages/dist-openinula/@opention/openinula
|
||||||
|
packages/dist-openinula/@opention/openinula-icon
|
||||||
|
|
||||||
|
命令参数:传入字符串参数列表可以指定只打包单个组件或多个特定组件,比如
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build:ui-openinula button
|
||||||
|
```
|
||||||
|
|
||||||
|
默认不传的话,会打包所有组件,以及公共任务,比如 openinula-common、openinula-icon
|
||||||
|
|
||||||
|
可以通过 -f 指定目标格式,默认 es,可选 es、cjs
|
||||||
|
可以通过 -t 指定目标版本,默认 18,现在 openinula 只支持 18
|
||||||
|
可以通过 -s 指定发布 npm scope,默认是 opentiny
|
||||||
|
可以通过 -c 指定是否清空构建目录
|
||||||
|
可以通过 --no-dts 指定不生成类型定义文件
|
||||||
|
|
||||||
|
## 发包 openinula 组件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm pub:openinula
|
||||||
|
```
|
||||||
|
|
||||||
|
# 目录结构
|
||||||
|
|
||||||
|
## 打包 openinula 相关
|
||||||
|
|
||||||
|
```b
|
||||||
|
internals/cli
|
||||||
|
/build
|
||||||
|
/build-entry-openinula.ts (packages/openinula 目录下生成入口)
|
||||||
|
/build-ui-openinula.ts (packages/dist-openinula 下生成打包产物)
|
||||||
|
/create
|
||||||
|
/create-mapping-openinula.ts (packages 下生成构建任务列表 modules.json)
|
||||||
|
/common-mapping-openinula.json (定义一些公共的打包任务,如 openinula-common)
|
||||||
|
/create-ui-openinula.ts (vue到openInula的转换脚本)
|
||||||
|
|
||||||
|
internals/cli
|
||||||
|
/public/template/openinula(转换脚本使用的模板文件)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发 openinula 模版文件相关
|
||||||
|
|
||||||
|
packages/openinula/src/[compName] 目录
|
||||||
|
|
||||||
|
一个组件模版的目录结构如下
|
||||||
|
|
||||||
|
```b
|
||||||
|
alert
|
||||||
|
/node_modules
|
||||||
|
/src
|
||||||
|
/index.ts
|
||||||
|
/pc.tsx
|
||||||
|
/mobile.tsx
|
||||||
|
/mobile-first.ts
|
||||||
|
/index.ts
|
||||||
|
/package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
alert/index 是组件入口
|
||||||
|
pc、mobile、mobile-first 是三套模版
|
||||||
|
|
||||||
|
## 开发 openinula-icon 相关
|
||||||
|
|
||||||
|
packages/openinula-icon/src/[svgName] 目录
|
||||||
|
|
||||||
|
一个 svg 直接用一个 index.ts 创建
|
||||||
|
|
||||||
|
如:packages/openinula-icon/src/add/index.ts
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Svg } from '@opentiny/openinula-common'
|
||||||
|
import { openinulaComponent as AddLoading } from '@opentiny/vue-theme/svgs/add.svg'
|
||||||
|
|
||||||
|
export default Svg({ name: 'AddLoading', component: AddLoading })
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发 openinula-common openinula 适配层相关
|
||||||
|
|
||||||
|
openinula-common 的目录如下,主要是适配层的文件
|
||||||
|
|
||||||
|
```b
|
||||||
|
packages/openinula-common
|
||||||
|
/src
|
||||||
|
/csscls.ts 操作样式类名的一些方法
|
||||||
|
/event.ts 模拟 vue 事件系统
|
||||||
|
/fiber.ts 对 fiber 的一些读取操作
|
||||||
|
/openinulaive.ts 实现数据响应式
|
||||||
|
/resolveProps.js 从 openinula 的 props 上解析事件或属性
|
||||||
|
/svg-render.jsx 渲染 svg 组件的公共函数
|
||||||
|
/utils.ts 工具函数
|
||||||
|
/virtual-comp.jsx 虚拟组件,用于实现 vue 的指令系统
|
||||||
|
/vm.js 用户模拟 vue 的 vm 对象
|
||||||
|
/vue-hooks.js 用户模拟 vue 的钩子函数
|
||||||
|
```
|
||||||
|
|
||||||
|
# 用户使用文档
|
||||||
|
|
||||||
|
## 在项目中使用所有组件
|
||||||
|
|
||||||
|
- 1.下载整个组件库
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i @pe-3/openinula
|
||||||
|
```
|
||||||
|
|
||||||
|
- 2. 导入组件
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { Button as TinyButton, openinula as Tiny openinula } from '@pe-3/openinula'
|
||||||
|
```
|
||||||
|
|
||||||
|
- 3. 使用组件(查看 api 文档)
|
||||||
|
|
||||||
|
```js
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TinyButton type="primary">主要按钮</TinyButton>
|
||||||
|
<TinyAlert description="提示组件" closeable={false} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在项目中使用单个组件
|
||||||
|
|
||||||
|
- 1. 下载单个组件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i @pe-3/openinula-button
|
||||||
|
npm i @pe-3/openinula-alert
|
||||||
|
```
|
||||||
|
|
||||||
|
- 2. 导入单个组件
|
||||||
|
|
||||||
|
```js
|
||||||
|
import TintButton from '@pe-3/openinula-button'
|
||||||
|
import TintAlert from '@pe-3/openinula-alert'
|
||||||
|
```
|
||||||
|
|
||||||
|
- 3. 使用单个组件
|
||||||
|
|
||||||
|
```js
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TinyButton type="primary">主要按钮</TinyButton>
|
||||||
|
<TinyAlert description="提示组件" closeable={false} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 组件 api 文档地址:
|
||||||
|
|
||||||
|
https://opentiny.design/
|
||||||
|
|
||||||
|
## codesandbox
|
||||||
|
|
||||||
|
https://codesandbox.io/s/hungry-bash-tlch6l?file=/src/App.js
|
|
@ -1,9 +1,19 @@
|
||||||
import Alert from '@opentiny/openinula-alert'
|
import Alert from '@opentiny/openinula-alert'
|
||||||
|
import Badge from '@opentiny/openinula-badge'
|
||||||
|
import Button from '@opentiny/openinula-button'
|
||||||
|
import Icon from '@opentiny/openinula-icon'
|
||||||
|
import Switch from '@opentiny/openinula-switch'
|
||||||
|
|
||||||
|
const components = [Alert, Badge, Button, Icon, Switch]
|
||||||
|
|
||||||
export const version = '1.0.0'
|
export const version = '1.0.0'
|
||||||
|
|
||||||
export { Alert }
|
export { Alert, Badge, Button, Icon, Switch }
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Alert
|
Alert,
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
Switch
|
||||||
} as any
|
} as any
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import Alert from '@opentiny/openinula-alert/src/mobile-first'
|
||||||
|
import Button from '@opentiny/openinula-button/src/mobile-first'
|
||||||
|
import Icon from '@opentiny/openinula-icon/src/mobile-first'
|
||||||
|
import Switch from '@opentiny/openinula-switch/src/mobile-first'
|
||||||
|
|
||||||
|
const components = [Alert, Button, Icon, Switch]
|
||||||
|
|
||||||
|
export const version = '1.0.0'
|
||||||
|
|
||||||
|
export { Alert, Button, Icon, Switch }
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
Switch
|
||||||
|
} as any
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Alert from '@opentiny/openinula-alert/src/mobile'
|
||||||
|
import Badge from '@opentiny/openinula-badge/src/mobile'
|
||||||
|
import Button from '@opentiny/openinula-button/src/mobile'
|
||||||
|
import Icon from '@opentiny/openinula-icon/src/mobile'
|
||||||
|
import Switch from '@opentiny/openinula-switch/src/mobile'
|
||||||
|
|
||||||
|
const components = [Alert, Badge, Button, Icon, Switch]
|
||||||
|
|
||||||
|
export const version = '1.0.0'
|
||||||
|
|
||||||
|
export { Alert, Badge, Button, Icon, Switch }
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Alert,
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
Switch
|
||||||
|
} as any
|
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
"Alert": {
|
||||||
|
"path": "openinula/src/alert/index.ts",
|
||||||
|
"type": "component",
|
||||||
|
"exclude": false,
|
||||||
|
"mode": [
|
||||||
|
"mobile-first",
|
||||||
|
"mobile",
|
||||||
|
"pc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AlertMobile": {
|
||||||
|
"path": "openinula/src/alert/src/mobile.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"AlertMobileFirst": {
|
||||||
|
"path": "openinula/src/alert/src/mobile-first.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"AlertPc": {
|
||||||
|
"path": "openinula/src/alert/src/pc.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"Badge": {
|
||||||
|
"path": "openinula/src/badge/index.ts",
|
||||||
|
"type": "component",
|
||||||
|
"exclude": false,
|
||||||
|
"mode": [
|
||||||
|
"mobile",
|
||||||
|
"pc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"BadgeMobile": {
|
||||||
|
"path": "openinula/src/badge/src/mobile.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"BadgePc": {
|
||||||
|
"path": "openinula/src/badge/src/pc.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"Button": {
|
||||||
|
"path": "openinula/src/button/index.ts",
|
||||||
|
"type": "component",
|
||||||
|
"exclude": false,
|
||||||
|
"mode": [
|
||||||
|
"mobile-first",
|
||||||
|
"mobile",
|
||||||
|
"pc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ButtonMobile": {
|
||||||
|
"path": "openinula/src/button/src/mobile.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"ButtonMobileFirst": {
|
||||||
|
"path": "openinula/src/button/src/mobile-first.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"ButtonPc": {
|
||||||
|
"path": "openinula/src/button/src/pc.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"Common": {
|
||||||
|
"path": "openinula/common/src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"Icon": {
|
||||||
|
"path": "openinula/src/icon/index.ts",
|
||||||
|
"type": "component",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"Switch": {
|
||||||
|
"path": "openinula/src/switch/index.ts",
|
||||||
|
"type": "component",
|
||||||
|
"exclude": false,
|
||||||
|
"mode": [
|
||||||
|
"mobile-first",
|
||||||
|
"mobile",
|
||||||
|
"pc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"SwitchMobile": {
|
||||||
|
"path": "openinula/src/switch/src/mobile.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"SwitchMobileFirst": {
|
||||||
|
"path": "openinula/src/switch/src/mobile-first.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
},
|
||||||
|
"SwitchPc": {
|
||||||
|
"path": "openinula/src/switch/src/pc.jsx",
|
||||||
|
"type": "template",
|
||||||
|
"exclude": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,10 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentiny/openinula-common": "workspace:~",
|
"@opentiny/openinula-common": "workspace:~",
|
||||||
"@opentiny/openinula-alert": "workspace:~"
|
"@opentiny/openinula-alert": "workspace:~",
|
||||||
|
"@opentiny/openinula-badge": "workspace:~",
|
||||||
|
"@opentiny/openinula-button": "workspace:~",
|
||||||
|
"@opentiny/openinula-icon": "workspace:~",
|
||||||
|
"@opentiny/openinula-switch": "workspace:~"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Alert from '@opentiny/openinula-alert/src/pc'
|
||||||
|
import Badge from '@opentiny/openinula-badge/src/pc'
|
||||||
|
import Button from '@opentiny/openinula-button/src/pc'
|
||||||
|
import Icon from '@opentiny/openinula-icon/src/pc'
|
||||||
|
import Switch from '@opentiny/openinula-switch/src/pc'
|
||||||
|
|
||||||
|
const components = [Alert, Badge, Button, Icon, Switch]
|
||||||
|
|
||||||
|
export const version = '1.0.0'
|
||||||
|
|
||||||
|
export { Alert, Badge, Button, Icon, Switch }
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Alert,
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
Switch
|
||||||
|
} as any
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Badge from './src'
|
||||||
|
|
||||||
|
export default Badge
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/openinula-badge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
|
"@opentiny/openinula-common": "workspace:~",
|
||||||
|
"@opentiny/openinula-icon": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~",
|
||||||
|
"@opentiny/vue-theme-mobile": "workspace:~"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import pc from './pc'
|
||||||
|
import mobile from './mobile'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const { tiny_mode = 'pc' } = props
|
||||||
|
|
||||||
|
const S = {
|
||||||
|
pc,
|
||||||
|
mobile
|
||||||
|
}[tiny_mode]
|
||||||
|
|
||||||
|
return S(props)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { useSetup, useVm, vc, If, Slot } from '@opentiny/openinula-common'
|
||||||
|
import { api, renderless } from '@opentiny/vue-renderless/badge/vue'
|
||||||
|
import '@opentiny/vue-theme-mobile/badge/index.less'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const {
|
||||||
|
isDot = false,
|
||||||
|
isFixed = true,
|
||||||
|
isMini = false,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
modelValue,
|
||||||
|
href,
|
||||||
|
target,
|
||||||
|
hidden = false,
|
||||||
|
type,
|
||||||
|
badgeClass,
|
||||||
|
offset = [0, 0]
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
isDot,
|
||||||
|
isFixed,
|
||||||
|
isMini,
|
||||||
|
hidden,
|
||||||
|
offset
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
const { ref, parent, current: vm } = useVm()
|
||||||
|
|
||||||
|
const { state } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
api,
|
||||||
|
renderless,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className="tiny-mobile-badge">
|
||||||
|
<Slot parent_children={props.children}></Slot>
|
||||||
|
<If v-if={!hidden && (value > 0 || isDot)}>
|
||||||
|
<div
|
||||||
|
className={vc([
|
||||||
|
'tiny-mobile-badge__content',
|
||||||
|
{
|
||||||
|
'is-dot': isDot,
|
||||||
|
'is-fixed': isFixed,
|
||||||
|
'is-mini': isMini
|
||||||
|
},
|
||||||
|
value < 10 ? 'is-circle' : '',
|
||||||
|
type ? 'tiny-mobile-badge--' + type : ''
|
||||||
|
])}>
|
||||||
|
<If v-if={!isDot}>
|
||||||
|
<span>
|
||||||
|
<Slot name="content" slots={props.slots}>
|
||||||
|
<a href={state.href} target={target} rel="noopener noreferrer" className="tiny-mobile-badge__link">
|
||||||
|
{state.content}
|
||||||
|
</a>
|
||||||
|
</Slot>
|
||||||
|
</span>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { useSetup, useVm, vc, If, Slot } from '@opentiny/openinula-common'
|
||||||
|
import { api, renderless } from '@opentiny/vue-renderless/badge/vue'
|
||||||
|
import '@opentiny/vue-theme/badge/index.less'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const {
|
||||||
|
isDot = false,
|
||||||
|
isFixed = true,
|
||||||
|
isMini = false,
|
||||||
|
max,
|
||||||
|
value,
|
||||||
|
modelValue,
|
||||||
|
href,
|
||||||
|
target,
|
||||||
|
hidden = false,
|
||||||
|
type,
|
||||||
|
badgeClass,
|
||||||
|
offset = [0, 0],
|
||||||
|
data
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
isDot,
|
||||||
|
isFixed,
|
||||||
|
isMini,
|
||||||
|
hidden,
|
||||||
|
offset
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
const { ref, parent, current: vm } = useVm()
|
||||||
|
|
||||||
|
const { state } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
api,
|
||||||
|
renderless,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className="tiny-badge__wrapper">
|
||||||
|
<If v-if={data}>
|
||||||
|
<span>{data}</span>
|
||||||
|
</If>
|
||||||
|
<Slot v-if={!data} parent_children={props.children}></Slot>
|
||||||
|
<If v-if={!hidden && (state.content || state.content === 0 || isDot)}>
|
||||||
|
<div
|
||||||
|
className={vc([
|
||||||
|
'tiny-badge',
|
||||||
|
isDot ? 'tiny-badge--default' : '',
|
||||||
|
state.isOverstep ? 'tiny-badge--max' : '',
|
||||||
|
type ? 'tiny-badge--' + type : '',
|
||||||
|
badgeClass || ''
|
||||||
|
])}
|
||||||
|
style={{
|
||||||
|
transform: `translate(
|
||||||
|
${offset[0]}${typeof offset[0] === 'number' ? 'px' : ''},
|
||||||
|
${offset[1]}${typeof offset[1] === 'number' ? 'px' : ''}
|
||||||
|
)`
|
||||||
|
}}>
|
||||||
|
<Slot name="content" slots={props.slots}>
|
||||||
|
<If v-if={state.href}>
|
||||||
|
<a href={state.href} target={target} rel="noopener noreferrer">
|
||||||
|
{state.content}
|
||||||
|
</a>
|
||||||
|
</If>
|
||||||
|
<If v-if={!state.href}>
|
||||||
|
<span className="tiny-badge__content-text">{state.content}</span>
|
||||||
|
</If>
|
||||||
|
</Slot>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Button from './src'
|
||||||
|
|
||||||
|
export default Button
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/openinula-button",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
|
"@opentiny/openinula-common": "workspace:~",
|
||||||
|
"@opentiny/openinula-icon": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~",
|
||||||
|
"@opentiny/vue-theme-mobile": "workspace:~"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import pc from './pc'
|
||||||
|
import mobile from './mobile'
|
||||||
|
import mobileFirst from './mobile-first'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const { tiny_mode = 'pc' } = props
|
||||||
|
|
||||||
|
const S = {
|
||||||
|
pc,
|
||||||
|
mobile,
|
||||||
|
'mobile-first': mobileFirst
|
||||||
|
}[tiny_mode]
|
||||||
|
|
||||||
|
return S(props)
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/button/vue'
|
||||||
|
import { useSetup, vc, If, Component, Slot, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconLoading } from '@opentiny/openinula-icon'
|
||||||
|
import { classes } from './token'
|
||||||
|
|
||||||
|
const define_props = [
|
||||||
|
'children',
|
||||||
|
'text',
|
||||||
|
'loading',
|
||||||
|
'autofocus',
|
||||||
|
'plain',
|
||||||
|
'round',
|
||||||
|
'circle',
|
||||||
|
'icon',
|
||||||
|
'size',
|
||||||
|
'type',
|
||||||
|
'nativeType',
|
||||||
|
'resetTime',
|
||||||
|
/^on/
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Button(props) {
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
autofocus,
|
||||||
|
size,
|
||||||
|
icon,
|
||||||
|
round,
|
||||||
|
circle,
|
||||||
|
href,
|
||||||
|
buttonClass,
|
||||||
|
tabindex,
|
||||||
|
text,
|
||||||
|
type,
|
||||||
|
nativeType = 'button',
|
||||||
|
resetTime = 1000
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign({
|
||||||
|
nativeType,
|
||||||
|
resetTime,
|
||||||
|
}, props)
|
||||||
|
|
||||||
|
const {
|
||||||
|
ref,
|
||||||
|
parent,
|
||||||
|
current: vm
|
||||||
|
} = useVm()
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleClick,
|
||||||
|
state,
|
||||||
|
a,
|
||||||
|
m,
|
||||||
|
gcls,
|
||||||
|
} = useSetup({
|
||||||
|
api,
|
||||||
|
renderless,
|
||||||
|
props: defaultProps,
|
||||||
|
classes,
|
||||||
|
ref,
|
||||||
|
parent,
|
||||||
|
vm
|
||||||
|
})
|
||||||
|
|
||||||
|
const $attrs = a(props, define_props, false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
data-tag="tiny-button"
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={state.buttonDisabled || loading}
|
||||||
|
autoFocus={autofocus}
|
||||||
|
type={nativeType}
|
||||||
|
className={m(
|
||||||
|
gcls('button'),
|
||||||
|
gcls(`size-${size || 'default'}`),
|
||||||
|
gcls(
|
||||||
|
`type-${type || 'default'}${icon ? '-icon' : state.plain ? '-plain' : ''}
|
||||||
|
${state.buttonDisabled ? '-disabled' : ''}`
|
||||||
|
),
|
||||||
|
gcls(round ? 'is-round' : 'no-round'),
|
||||||
|
gcls(circle ? 'is-circle' : 'no-circle'),
|
||||||
|
gcls({ 'is-border': circle || !(type === 'text' || icon) }),
|
||||||
|
gcls({ 'button-link': href }),
|
||||||
|
buttonClass
|
||||||
|
)}
|
||||||
|
tabIndex={tabindex}
|
||||||
|
{...a($attrs, ['class', 'style'], true)}
|
||||||
|
>
|
||||||
|
<If v-if={loading}>
|
||||||
|
<IconLoading className={gcls('loading-svg')} />
|
||||||
|
</If>
|
||||||
|
<Component
|
||||||
|
v-if={icon && !loading}
|
||||||
|
is={icon}
|
||||||
|
className={vc([
|
||||||
|
gcls('button-icon'),
|
||||||
|
gcls(`button-icon-${state.buttonDisabled ? 'disabled' : 'default'}`)
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
<Slot slots={props.slots} parent_children={props.children}>
|
||||||
|
<span>{text}</span>
|
||||||
|
</Slot>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/button/vue'
|
||||||
|
import { useSetup, vc, If, Component, Slot, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconLoading } from '@opentiny/openinula-icon'
|
||||||
|
import '@opentiny/vue-theme-mobile/button/index.less'
|
||||||
|
|
||||||
|
const define_props = [
|
||||||
|
'children',
|
||||||
|
'text',
|
||||||
|
'loading',
|
||||||
|
'autofocus',
|
||||||
|
'plain',
|
||||||
|
'round',
|
||||||
|
'circle',
|
||||||
|
'icon',
|
||||||
|
'size',
|
||||||
|
'type',
|
||||||
|
'nativeType',
|
||||||
|
'resetTime',
|
||||||
|
/^on/
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Button(props) {
|
||||||
|
const {
|
||||||
|
text,
|
||||||
|
loading,
|
||||||
|
round,
|
||||||
|
icon,
|
||||||
|
size,
|
||||||
|
type = 'default',
|
||||||
|
nativeType = 'button',
|
||||||
|
resetTime = 1000
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign({
|
||||||
|
type,
|
||||||
|
nativeType,
|
||||||
|
resetTime
|
||||||
|
}, props)
|
||||||
|
|
||||||
|
const {
|
||||||
|
ref,
|
||||||
|
parent,
|
||||||
|
current: vm
|
||||||
|
} = useVm()
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleClick,
|
||||||
|
state,
|
||||||
|
a
|
||||||
|
} = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
parent,
|
||||||
|
vm
|
||||||
|
})
|
||||||
|
|
||||||
|
const $attrs = a(props, define_props, false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={state.buttonDisabled || loading}
|
||||||
|
type={nativeType}
|
||||||
|
className={vc([
|
||||||
|
'tiny-mobile-button',
|
||||||
|
type ? 'tiny-mobile-button--' + type : '',
|
||||||
|
size ? 'tiny-mobile-button--' + size : '',
|
||||||
|
{
|
||||||
|
'is-disabled': state.buttonDisabled,
|
||||||
|
'is-loading': loading,
|
||||||
|
'is-plain': state.plain,
|
||||||
|
'is-round': round
|
||||||
|
}
|
||||||
|
])}
|
||||||
|
{...a($attrs, ['class', 'style'], true)}
|
||||||
|
>
|
||||||
|
<If v-if={loading}>
|
||||||
|
<IconLoading class='tiny-icon-loading' />
|
||||||
|
</If>
|
||||||
|
<Component v-if={icon && !loading} is={icon} class='tiny-icon is-icon' />
|
||||||
|
<Slot slots={props.slots} parent_children={props.children}>
|
||||||
|
<span style={{ marginLeft: text && (icon || loading) ? '4px' : 0 }}>{text}</span>
|
||||||
|
</Slot>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/button/vue'
|
||||||
|
import { useSetup, If, Component, vc, useVm } from '@opentiny/openinula-common'
|
||||||
|
import { IconLoading } from '@opentiny/openinula-icon'
|
||||||
|
import '@opentiny/vue-theme/button/index.less'
|
||||||
|
|
||||||
|
const define_props = [
|
||||||
|
'children',
|
||||||
|
'text',
|
||||||
|
'loading',
|
||||||
|
'autofocus',
|
||||||
|
'plain',
|
||||||
|
'round',
|
||||||
|
'circle',
|
||||||
|
'icon',
|
||||||
|
'size',
|
||||||
|
'type',
|
||||||
|
'nativeType',
|
||||||
|
'resetTime',
|
||||||
|
/^on/
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Button(props) {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
text,
|
||||||
|
loading,
|
||||||
|
autofocus,
|
||||||
|
round,
|
||||||
|
circle,
|
||||||
|
icon,
|
||||||
|
size,
|
||||||
|
tabindex,
|
||||||
|
type = 'default',
|
||||||
|
nativeType = 'button',
|
||||||
|
resetTime = 1000
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign({
|
||||||
|
type,
|
||||||
|
nativeType,
|
||||||
|
resetTime
|
||||||
|
}, props)
|
||||||
|
|
||||||
|
const {
|
||||||
|
ref,
|
||||||
|
parent,
|
||||||
|
current: vm
|
||||||
|
} = useVm()
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleClick,
|
||||||
|
state,
|
||||||
|
a
|
||||||
|
} = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
const $attrs = a(props, define_props, false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
ref={ref}
|
||||||
|
className={vc([
|
||||||
|
'tiny-button',
|
||||||
|
type ? 'tiny-button--' + type : '',
|
||||||
|
size ? 'tiny-button--' + size : '',
|
||||||
|
{
|
||||||
|
'is-disabled': state.buttonDisabled,
|
||||||
|
'is-loading': loading,
|
||||||
|
'is-plain': state.plain,
|
||||||
|
'is-round': round,
|
||||||
|
'is-circle': circle,
|
||||||
|
'is-icon': icon && !loading && (text || $slots.default),
|
||||||
|
'is-only-icon': icon && !loading && !(text || $slots.default)
|
||||||
|
}
|
||||||
|
])}
|
||||||
|
onClick={handleClick}
|
||||||
|
disabled={state.buttonDisabled || loading}
|
||||||
|
autoFocus={autofocus}
|
||||||
|
type={nativeType}
|
||||||
|
tabIndex={tabindex}
|
||||||
|
{...a($attrs, ['class', 'style'], true)}
|
||||||
|
>
|
||||||
|
<If v-if={loading}>
|
||||||
|
<IconLoading className="tiny-icon-loading tiny-svg-size" />
|
||||||
|
</If>
|
||||||
|
<Component
|
||||||
|
v-if={icon && !loading}
|
||||||
|
is={icon}
|
||||||
|
className={(text || children) ? 'is-text' : ''}
|
||||||
|
/>
|
||||||
|
<span>{children || text}</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
export const classes = {
|
||||||
|
'button':
|
||||||
|
'inline-block sm:max-w-[9rem] text-center overflow-hidden overflow-ellipsis whitespace-nowrap transition-button duration-300 delay-[0ms]',
|
||||||
|
'size-default': 'h-10 text-sm sm:h-7 sm:text-xs',
|
||||||
|
'size-medium': 'h-10 text-sm sm:h-8 sm:text-xs',
|
||||||
|
'size-small': 'h-8 text-sm sm:h-7 sm:text-xs',
|
||||||
|
'size-mini': 'h-7 sm:h-6 sm:text-xs',
|
||||||
|
'type-default':
|
||||||
|
'text-black border-color-border hover:border-color-border-hover active:border-color-border-active sm:cursor-pointer',
|
||||||
|
'type-primary':
|
||||||
|
'text-white border-color-brand bg-color-brand hover:border-color-brand-hover hover:bg-color-brand-hover active:border-color-brand-active active:bg-color-brand-active sm:cursor-pointer',
|
||||||
|
'type-success':
|
||||||
|
'text-white border-color-success bg-color-success hover:border-color-success-hover hover:bg-color-success-hover active:border-color-success-active active:bg-color-success-active sm:cursor-pointer',
|
||||||
|
'type-info':
|
||||||
|
'text-white border-color-info-secondary bg-color-info-secondary hover:border-color-info-secondary-hover hover:bg-color-info-secondary-hover active:border-color-info-secondary-active active:bg-color-info-secondary-active sm:cursor-pointer',
|
||||||
|
'type-warning':
|
||||||
|
'text-white border-color-warning bg-color-warning hover:border-color-warning-hover hover:bg-color-warning-hover active:border-color-warning-active active:bg-color-warning-active sm:cursor-pointer',
|
||||||
|
'type-danger':
|
||||||
|
'text-white border-color-error bg-color-error hover:border-color-error-hover hover:bg-color-error-hover active:border-color-error-active active:bg-color-error-active sm:cursor-pointer',
|
||||||
|
'type-text':
|
||||||
|
'border-none bg-transparent cursor-pointer text-color-text-placeholder active:text-color-text-primary sm:hover:text-color-text-primary sm:active:!text-color-brand-active',
|
||||||
|
'type-default-disabled': 'text-color-text-disabled bg-color-bg-3 border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-primary-disabled': 'text-white bg-color-brand-disabled border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-success-disabled': 'text-white bg-color-success-disabled border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-info-disabled': 'text-white bg-color-info-secondary-disabled border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-warning-disabled': 'text-white bg-color-alert-disabled border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-danger-disabled': 'text-white bg-color-error-disabled border-transparent hover:cursor-not-allowed',
|
||||||
|
'type-text-disabled': 'text-color-text-disabled hover:cursor-not-allowed',
|
||||||
|
'type-default-plain':
|
||||||
|
'text-black border-color-border hover:border-color-border-hover active:border-color-border-active sm:cursor-pointer',
|
||||||
|
'type-primary-plain':
|
||||||
|
'text-color-brand border-color-brand hover:text-color-brand-hover hover:border-color-brand-hover active:text-color-brand-active active:border-color-brand-active bg-white sm:cursor-pointer',
|
||||||
|
'type-success-plain':
|
||||||
|
'text-color-success border-color-success hover:text-color-success-hover hover:border-color-success-hover active:text-color-success-active active:border-color-success-active bg-white sm:cursor-pointer',
|
||||||
|
'type-info-plain':
|
||||||
|
'text-color-info-secondary border-color-info-secondary hover:text-color-info-secondary-hover hover:border-color-info-secondary-hover active:text-color-info-secondary-active active:border-color-info-secondary-active bg-white sm:cursor-pointer',
|
||||||
|
'type-warning-plain':
|
||||||
|
'text-color-warning border-color-warning hover:text-color-warning-hover hover:border-color-warning-hover active:text-color-warning-active active:border-color-warning-active bg-white sm:cursor-pointer',
|
||||||
|
'type-danger-plain':
|
||||||
|
'text-color-error border-color-error hover:text-color-error-hover hover:border-color-error-hover active:text-color-error-active active:border-color-error-active bg-white sm:cursor-pointer',
|
||||||
|
'type-text-plain': 'text-color-brand hover:text-color-brand-hover active:text-color-brand-active',
|
||||||
|
'type-default-plain-disabled':
|
||||||
|
'text-color-text-disabled bg-white border-color-text-disabled hover:cursor-not-allowed',
|
||||||
|
'type-primary-plain-disabled':
|
||||||
|
'text-color-brand-disabled bg-white border-color-brand-disabled hover:cursor-not-allowed',
|
||||||
|
'type-success-plain-disabled':
|
||||||
|
'text-color-success-disabled bg-white border-color-success-disabled hover:cursor-not-allowed',
|
||||||
|
'type-info-plain-disabled':
|
||||||
|
'text-color-info-secondary-disabled bg-white border-color-info-secondary-disabled hover:cursor-not-allowed',
|
||||||
|
'type-warning-plain-disabled':
|
||||||
|
'text-color-alert-disabled bg-white border-color-alert-disabled hover:cursor-not-allowed',
|
||||||
|
'type-danger-plain-disabled':
|
||||||
|
'text-color-error-disabled bg-white border-color-error-disabled hover:cursor-not-allowed',
|
||||||
|
'type-text-plain-disabled': 'text-color-text-disabled hover:cursor-not-allowed',
|
||||||
|
'no-round': 'rounded-sm',
|
||||||
|
'is-round': 'rounded-full',
|
||||||
|
'is-border': 'border-0.5 sm:border',
|
||||||
|
'no-circle': 'sm:min-w-[4.5rem] pl-3 pr-3',
|
||||||
|
'is-circle': 'sm:min-w-[0.4375rem] sm:rounded-full sm:pl-2 sm:pr-2',
|
||||||
|
'button-icon': '-mt-0.5 sm:text-base fill-current',
|
||||||
|
'button-icon-default': 'text-color-icon-primary hover:text-color-icon-hover active:text-color-icon-active',
|
||||||
|
'button-icon-disabled': 'text-color-icon-disabled hover:cursor-not-allowed',
|
||||||
|
'loading-svg': 'animate-spin-2 mr-1 fill-current -inset-0.5',
|
||||||
|
'button-link':
|
||||||
|
'text-color-link hover:text-color-link-hover active:color-link-hover active:hover:text-color-link-hover sm:hover:text-color-link-hover'
|
||||||
|
}
|
|
@ -3,10 +3,7 @@ import { compWhiteList } from './virtual-comp'
|
||||||
|
|
||||||
export function getFiberByDom(dom) {
|
export function getFiberByDom(dom) {
|
||||||
const key = Object.keys(dom).find((key) => {
|
const key = Object.keys(dom).find((key) => {
|
||||||
return (
|
return key.startsWith('_inula_VNode_')
|
||||||
key.startsWith('__openinulaFiber$') || // openinula 17+
|
|
||||||
key.startsWith('__openinulaInternalInstance$')
|
|
||||||
) // openinula <17
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return dom[key]
|
return dom[key]
|
||||||
|
@ -46,13 +43,13 @@ export function creatFiberCombine(fiber) {
|
||||||
const children = []
|
const children = []
|
||||||
|
|
||||||
traverseFiber(fiber.child, [
|
traverseFiber(fiber.child, [
|
||||||
(fiber) => {
|
// (fiber) => {
|
||||||
if (typeof fiber.type === 'string' && fiber.stateNode.getAttribute('v_ref')) {
|
// if (typeof fiber.type === 'string' && fiber.stateNode.getAttribute('v_ref')) {
|
||||||
refs[fiber.stateNode.getAttribute('v_ref')] = fiber.stateNode
|
// refs[fiber.stateNode.getAttribute('v_ref')] = fiber.stateNode
|
||||||
} else if (fiber.memoizedProps.v_ref) {
|
// } else if (fiber.memoizedProps.v_ref) {
|
||||||
refs[fiber.memoizedProps.v_ref] = fiber
|
// refs[fiber.memoizedProps.v_ref] = fiber
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
(fiber) => {
|
(fiber) => {
|
||||||
if (fiber.type && typeof fiber.type !== 'string') {
|
if (fiber.type && typeof fiber.type !== 'string') {
|
||||||
children.push(fiber)
|
children.push(fiber)
|
||||||
|
@ -74,8 +71,9 @@ export function useFiber() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
const current_fiber = getFiberByDom(ref.current)
|
const current_fiber = getFiberByDom(ref.current)
|
||||||
setParent(getParentFiber(current_fiber.return))
|
|
||||||
setCurrent(current_fiber.return)
|
setParent(getParentFiber(current_fiber))
|
||||||
|
setCurrent(current_fiber)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
|
@ -99,10 +99,10 @@ export const vc = VueClassName
|
||||||
|
|
||||||
export const getElementCssClass = (classes = {}, key) => {
|
export const getElementCssClass = (classes = {}, key) => {
|
||||||
if (typeof key === 'object') {
|
if (typeof key === 'object') {
|
||||||
const keys = Array.isArray(key) ? key : Object.keys(key).filter((k) => key[k])
|
const keys = Object.keys(key)
|
||||||
let cls = ''
|
let cls = ''
|
||||||
keys.forEach((k) => {
|
keys.forEach((k) => {
|
||||||
if (classes[k]) cls += `${classes[k]} `
|
if (key[k] && classes[k]) cls += `${classes[k]} `
|
||||||
})
|
})
|
||||||
return cls
|
return cls
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -100,7 +100,7 @@ export function useVm() {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
ref,
|
ref,
|
||||||
current: current.fiber && createVmProxy(current),
|
current: current && createVmProxy(current),
|
||||||
parent: parent.fiber && createVmProxy(parent)
|
parent: parent && createVmProxy(parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@ const collectRefs = (rootEl, $children) => {
|
||||||
const rootFiber = getFiberByDom(rootEl)
|
const rootFiber = getFiberByDom(rootEl)
|
||||||
// 收集普通元素 ref
|
// 收集普通元素 ref
|
||||||
traverseFiber(rootFiber, (fiber) => {
|
traverseFiber(rootFiber, (fiber) => {
|
||||||
if (typeof fiber.type === 'string' && fiber.stateNode.getAttribute('v-ref')) {
|
// if (typeof fiber.type === 'string' && fiber.stateNode.getAttribute('v-ref')) {
|
||||||
refs[fiber.stateNode.getAttribute('v-ref')] = fiber.stateNode
|
// refs[fiber.stateNode.getAttribute('v-ref')] = fiber.stateNode
|
||||||
}
|
// }
|
||||||
})
|
})
|
||||||
// 收集组件元素 ref
|
// 收集组件元素 ref
|
||||||
$children.forEach((child) => {
|
$children.forEach((child) => {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Switch from './src/index'
|
||||||
|
import '@opentiny/vue-theme/switch/index.less'
|
||||||
|
import { version } from './package.json'
|
||||||
|
|
||||||
|
Switch.version = version
|
||||||
|
|
||||||
|
export default Switch
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "@opentiny/openinula-switch",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"module": "index.ts",
|
||||||
|
"sideEffects": false,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm -w build:ui-openinula $npm_package_name",
|
||||||
|
"//postversion": "pnpm build"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@opentiny-internal/vue-test-utils": "workspace:*",
|
||||||
|
"vitest": "^0.31.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@opentiny/vue-renderless": "workspace:~",
|
||||||
|
"@opentiny/openinula-common": "workspace:~",
|
||||||
|
"@opentiny/openinula-icon": "workspace:~",
|
||||||
|
"@opentiny/vue-theme": "workspace:~",
|
||||||
|
"@opentiny/vue-theme-mobile": "workspace:~"
|
||||||
|
},
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import pc from './pc'
|
||||||
|
import mobile from './mobile'
|
||||||
|
|
||||||
|
// import mobileFirst from './mobile-first'
|
||||||
|
|
||||||
|
const $constants = {
|
||||||
|
PC_PREFIXCLS: 'tiny-switch',
|
||||||
|
MOBILE_PREFIXCLS: 'tiny-mobile-switch',
|
||||||
|
Mode: 'pc',
|
||||||
|
prefixcls(mode) {
|
||||||
|
return mode === this.Mode ? this.PC_PREFIXCLS : this.MOBILE_PREFIXCLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const { tiny_mode = 'pc', _constants = $constants } = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
_constants,
|
||||||
|
tiny_mode
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
const S = {
|
||||||
|
pc,
|
||||||
|
mobile
|
||||||
|
}[tiny_mode]
|
||||||
|
|
||||||
|
return S && S(defaultProps)
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { renderless, api } from '@opentiny/vue-renderless/switch/vue'
|
||||||
|
import { vc, useSetup, useVm } from '@opentiny/openinula-common'
|
||||||
|
import '@opentiny/vue-theme-mobile/switch/index.less'
|
||||||
|
|
||||||
|
const $constants = {}
|
||||||
|
|
||||||
|
export default function Switch(props) {
|
||||||
|
const {
|
||||||
|
_constants,
|
||||||
|
disabled = false,
|
||||||
|
showText,
|
||||||
|
falseColor,
|
||||||
|
falseValue = false,
|
||||||
|
mini = false,
|
||||||
|
modelValue = false,
|
||||||
|
size,
|
||||||
|
tabindex = '1',
|
||||||
|
trueColor,
|
||||||
|
trueValue = true,
|
||||||
|
beforeChange,
|
||||||
|
displayOnly = false
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
disabled,
|
||||||
|
falseValue,
|
||||||
|
mini,
|
||||||
|
modelValue,
|
||||||
|
tabindex,
|
||||||
|
trueValue,
|
||||||
|
displayOnly
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
const { ref, current: vm, parent } = useVm()
|
||||||
|
|
||||||
|
const { toggle, state } = useSetup({
|
||||||
|
props,
|
||||||
|
renderless,
|
||||||
|
api,
|
||||||
|
constants: _constants,
|
||||||
|
vm,
|
||||||
|
parent
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref}>
|
||||||
|
{' '}
|
||||||
|
<span className={vc(state.wrapClasses)} disabled={disabled} onClick={toggle} onKeydown={toggle}>
|
||||||
|
<div className="tiny-mobile-switch-loading">
|
||||||
|
{' '}
|
||||||
|
<div className="tiny-mobile-switch-loading-inner"></div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { useSetup, useVm, vc, If, Slot } from '@opentiny/openinula-common'
|
||||||
|
import { api, renderless } from '@opentiny/vue-renderless/switch/vue'
|
||||||
|
import '@opentiny/vue-theme/switch/index.less'
|
||||||
|
|
||||||
|
export default function (props) {
|
||||||
|
const {
|
||||||
|
_constants,
|
||||||
|
disabled = false,
|
||||||
|
showText,
|
||||||
|
falseColor,
|
||||||
|
falseValue = false,
|
||||||
|
mini = false,
|
||||||
|
modelValue = false,
|
||||||
|
size,
|
||||||
|
tabindex = '1',
|
||||||
|
trueColor,
|
||||||
|
trueValue = true,
|
||||||
|
beforeChange,
|
||||||
|
displayOnly = false
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const defaultProps = Object.assign(
|
||||||
|
{
|
||||||
|
disabled,
|
||||||
|
falseValue,
|
||||||
|
mini,
|
||||||
|
modelValue,
|
||||||
|
tabindex,
|
||||||
|
trueValue,
|
||||||
|
displayOnly
|
||||||
|
},
|
||||||
|
props
|
||||||
|
)
|
||||||
|
|
||||||
|
const { ref, parent, current: vm } = useVm()
|
||||||
|
|
||||||
|
const { state, toggle } = useSetup({
|
||||||
|
props: defaultProps,
|
||||||
|
api,
|
||||||
|
renderless,
|
||||||
|
vm,
|
||||||
|
parent,
|
||||||
|
constants: _constants
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
ref={ref}
|
||||||
|
className={vc([state.wrapClasses, state.showText ? 'tiny-switch__text' : ''])}
|
||||||
|
tabIndex={tabindex}
|
||||||
|
onClick={toggle}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
e.key === 'Enter' && toggle(e)
|
||||||
|
}}>
|
||||||
|
<span className={state.innerClasses}>
|
||||||
|
<If v-if={!mini && state.showText}>
|
||||||
|
<Slot v-if={state.currentValue === trueValue} name="open" slots={props.slots}>
|
||||||
|
ON
|
||||||
|
</Slot>
|
||||||
|
<Slot v-if={state.currentValue === falseValue} name="close" slots={props.slots}>
|
||||||
|
OFF
|
||||||
|
</Slot>
|
||||||
|
</If>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue