refactor(avatar, base icon, base close): ts

This commit is contained in:
07akioni 2021-01-16 15:25:28 +08:00
parent 05690562fd
commit 37dff9426e
25 changed files with 310 additions and 235 deletions

View File

@ -1,31 +1,51 @@
module.exports = {
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript/recommended',
'plugin:markdown/recommended'
],
extends: ['plugin:markdown/recommended'],
rules: {
'vue/max-attributes-per-line': [
2,
{
singleline: 20,
multiline: {
max: 1,
allowFirstLine: false
}
}
],
'vue/no-multiple-template-root': 0,
'vue/no-lone-template': 0,
'vue/no-v-model-argument': 0,
'no-void': 0
},
overrides: [
{
files: 'src/**/*.vue',
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript/recommended'
],
rules: {
'vue/max-attributes-per-line': [
2,
{
singleline: 20,
multiline: {
max: 1,
allowFirstLine: false
}
}
],
'vue/no-multiple-template-root': 0,
'vue/no-lone-template': 0,
'vue/no-v-model-argument': 0
}
},
{
files: ['*.ts', '*.js', '*.tsx'],
extends: ['standard-with-typescript'],
parserOptions: {
project: './tsconfig.json',
ecmaFeatures: {
jsx: true
}
},
rules: {
'@typescript-eslint/strict-boolean-expressions': 0,
'@typescript-eslint/prefer-nullish-coalescing': 0
}
},
{
files: ['light.ts'],
rules: {
'@typescript-eslint/explicit-module-boundary-types': 0
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/explicit-function-return-type': 0
}
},
{

View File

@ -49,6 +49,10 @@
"prettier --write",
"eslint --fix"
],
"*.tsx": [
"prettier --write",
"eslint --fix"
],
"*.vue": [
"prettier --parser=vue --write",
"eslint --fix"
@ -83,12 +87,14 @@
"babel-jest": "^26.6.2",
"cross-env": "^5.2.1",
"cssnano": "^4.1.10",
"eslint": "^7.0.0",
"eslint": "^7.18.0",
"eslint-config-standard": "^16.0.2",
"eslint-config-standard-with-typescript": "^19.0.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-markdown": "^2.0.0-rc.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.2",
"eslint-plugin-standard": "^4.1.0",
"eslint-plugin-vue": "^7.0.0",
"fs-extra": "^9.0.1",
"husky": "^4.3.5",

View File

@ -1 +0,0 @@
export { default } from './src/Close.vue'

1
src/_base/close/index.ts Normal file
View File

@ -0,0 +1 @@
export { default } from './src/Close'

View File

@ -1,16 +1,4 @@
<template>
<n-base-icon
class="n-base-close"
:class="{
'n-base-close--disabled': disabled
}"
>
<close-icon />
</n-base-icon>
</template>
<script>
import { defineComponent } from 'vue'
import { h, defineComponent } from 'vue'
import { useStyle } from '../../../_mixins'
import NBaseIcon from '../../icon'
import { CloseIcon } from '../../icons'
@ -18,10 +6,6 @@ import style from './styles/index.cssr.js'
export default defineComponent({
name: 'BaseClose',
components: {
NBaseIcon,
CloseIcon
},
props: {
disabled: {
type: Boolean,
@ -30,6 +14,17 @@ export default defineComponent({
},
setup (props) {
useStyle('BaseClose', style)
return (
<NBaseIcon
class={[
'n-base-close',
{
'n-base-close--disabled': props.disabled
}
]}
>
<CloseIcon />
</NBaseIcon>
)
}
})
</script>

View File

@ -1,22 +0,0 @@
import { cB, cM, c } from '../../../../_utils/cssr'
// vars:
// --close-color
// --close-color-hover
// --close-color-pressed
// --close-color-disabled
export default cB('base-close', `
cursor: pointer;
color: var(--close-color);
`, [
c('&:hover', {
color: 'var(--close-color-hover)'
}),
c('&:active', {
color: 'var(--close-color-pressed)'
}),
cM('disabled', {
cursor: 'not-allowed!important',
color: 'var(--close-color-disabled)'
})
])

View File

@ -0,0 +1,26 @@
import { cB, cM, c } from '../../../../_utils/cssr'
// vars:
// --close-color
// --close-color-hover
// --close-color-pressed
// --close-color-disabled
export default cB(
'base-close',
`
cursor: pointer;
color: var(--close-color);
`,
[
c('&:hover', {
color: 'var(--close-color-hover)'
}),
c('&:active', {
color: 'var(--close-color-pressed)'
}),
cM('disabled', {
cursor: 'not-allowed!important',
color: 'var(--close-color-disabled)'
})
]
)

View File

@ -1 +0,0 @@
export { default } from './src/Icon.vue'

1
src/_base/icon/index.ts Normal file
View File

@ -0,0 +1 @@
export { default } from './src/Icon'

View File

@ -1,9 +1,4 @@
<template>
<i class="n-base-icon"><slot /></i>
</template>
<script>
import { defineComponent } from 'vue'
import { h, defineComponent, renderSlot } from 'vue'
import { useStyle } from '../../../_mixins'
import style from './styles/index.cssr.js'
@ -11,6 +6,8 @@ export default defineComponent({
name: 'BaseIcon',
setup () {
useStyle('BaseIcon', style)
},
render () {
return <i class="n-base-icon">{renderSlot(this.$slots, 'default')}</i>
}
})
</script>

View File

@ -1,6 +1,8 @@
import { c, cB } from '../../../../_utils/cssr'
export default cB('base-icon', `
export default cB(
'base-icon',
`
height: 1em;
width: 1em;
line-height: 1em;
@ -9,9 +11,11 @@ export default cB('base-icon', `
position: relative;
fill: currentColor;
transform: translateZ(0);
`, [
c('svg', {
height: '1em',
width: '1em'
})
])
`,
[
c('svg', {
height: '1em',
width: '1em'
})
]
)

12
src/_base/index.ts Normal file
View File

@ -0,0 +1,12 @@
export { default as NIconSwitchTransition } from './icon-switch-transition'
export { default as NFadeInExpandTransition } from './fade-in-expand-transition'
export { default as NBaseClose } from './close'
export { default as NBaseIcon } from './icon'
export { default as NBaseFocusDetector } from './focus-detector'
export { default as NBaseLoading } from './loading'
export { default as NBaseSelectMenu } from './select-menu'
export { default as NBaseWave } from './wave'
export { default as NBaseMenuMask } from './menu-mask'
export { default as NBaseSelection } from './selection'
export { default as NBaseSlotMachine } from './slot-machine'
export { default as NBaseClear } from './clear'

View File

@ -1,2 +0,0 @@
/* istanbul ignore file */
export { default as NAvatar } from './src/Avatar.vue'

2
src/avatar/index.ts Normal file
View File

@ -0,0 +1,2 @@
/* istanbul ignore file */
export { default as NAvatar } from './src/Avatar'

118
src/avatar/src/Avatar.tsx Normal file
View File

@ -0,0 +1,118 @@
import {
h,
ref,
computed,
onUpdated,
onMounted,
defineComponent,
PropType
} from 'vue'
import { useTheme } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { avatarLight } from '../styles'
import type { AvatarTheme } from '../styles'
import { createKey } from '../../_utils'
import style from './styles/index.cssr'
export default defineComponent({
name: 'Avatar',
props: {
...(useTheme.props as ThemeProps<AvatarTheme>),
size: {
type: [String, Number] as PropType<
number | 'tiny' | 'small' | 'medium' | 'large' | 'huge'
>,
default: 'medium'
},
src: {
type: String,
default: undefined
},
circle: {
type: Boolean,
default: false
},
round: {
type: Boolean,
default: false
},
color: {
type: String,
default: undefined
}
},
setup (props) {
let memoedTextHtml: string | null = null
const textRef = ref<HTMLElement | null>(null)
const selfRef = ref<HTMLElement | null>(null)
const adjustText = (): void => {
const { value: textEl } = textRef
if (textEl) {
if (memoedTextHtml === null || memoedTextHtml !== textEl.innerHTML) {
memoedTextHtml = textEl.innerHTML
const { value: selfEl } = selfRef
if (selfEl) {
const { offsetWidth: elWidth, offsetHeight: elHeight } = selfEl
const { offsetWidth: textWidth, offsetHeight: textHeight } = textEl
const radix = 0.9
const ratio = Math.min(
(elWidth / textWidth) * radix,
(elHeight / textHeight) * radix,
1
)
textEl.style.transform = `translateX(-50%) translateY(-50%) scale(${ratio})`
}
}
}
}
// Not Good Impl
onMounted(() => adjustText())
onUpdated(() => {
console.log('updated')
adjustText()
})
const themeRef = useTheme('Avatar', 'Avatar', style, avatarLight, props)
return {
textRef,
selfRef,
cssVars: computed(() => {
const { size, round, circle } = props
const {
self: { borderRadius, fontSize, color },
common: { cubicBezierEaseInOut }
} = themeRef.value
let height: string
if (typeof size === 'number') {
height = `${size}px`
} else {
height = themeRef.value.self[createKey('height', size)]
}
return {
'--font-size': fontSize,
'--border-radius': round || circle ? '50%' : borderRadius,
'--color': color,
'--bezier': cubicBezierEaseInOut,
'--size': height
}
})
}
},
render () {
const { $slots, src } = this
return (
<span ref="selfRef" class="n-avatar" style={this.cssVars as any}>
{!$slots.default && src ? (
<img src={src} />
) : (
<span
ref="textRef"
class="n-avatar__text"
style={{ background: this.color }}
>
{$slots}
</span>
)}
</span>
)
}
})

View File

@ -1,107 +0,0 @@
<template>
<span ref="selfRef" class="n-avatar" :style="cssVars">
<img v-if="!$slots.default && src" :src="src">
<slot v-else-if="$slots.icon" name="icon" />
<span
v-else
ref="textRef"
class="n-avatar__text"
:style="{
background: color
}"
>
<slot />
</span>
</span>
</template>
<script>
import { ref, computed, onUpdated, onMounted, defineComponent } from 'vue'
import { useTheme } from '../../_mixins'
import { avatarLight } from '../styles'
import { createKey } from '../../_utils'
import { validSize } from './config'
import style from './styles/index.cssr.js'
export default defineComponent({
name: 'Avatar',
props: {
...useTheme.props,
size: {
validator (value) {
return validSize.includes(value) || typeof value === 'number'
},
default: 'medium'
},
src: {
type: String,
default: undefined
},
circle: {
type: Boolean,
default: false
},
round: {
type: Boolean,
default: false
},
color: {
type: String,
default: undefined
}
},
setup (props) {
let memoedTextHtml = null
const textRef = ref(null)
const selfRef = ref(null)
const adjustText = () => {
const { value: textEl } = textRef
if (textEl) {
if (memoedTextHtml === null || memoedTextHtml !== textEl.innerHTML) {
memoedTextHtml = textEl.innerHTML
const { value: selfEl } = selfRef
const { offsetWidth: elWidth, offsetHeight: elHeight } = selfEl
const { offsetWidth: textWidth, offsetHeight: textHeight } = textEl
const radix = 0.9
const ratio = Math.min(
(elWidth / textWidth) * radix,
(elHeight / textHeight) * radix,
1
)
textEl.style.transform = `translateX(-50%) translateY(-50%) scale(${ratio})`
}
}
}
// Not Good Impl
onMounted(() => adjustText())
onUpdated(() => {
console.log('updated')
adjustText()
})
const themeRef = useTheme('Avatar', 'Avatar', style, avatarLight, props)
return {
textRef,
selfRef,
cssVars: computed(() => {
const { size, round, circle } = props
const {
self: {
borderRadius,
fontSize,
color,
[createKey('height', String(size))]: height
},
common: { cubicBezierEaseInOut }
} = themeRef.value
return {
'--font-size': fontSize,
'--border-radius': round || circle ? '50%' : borderRadius,
'--color': color,
'--bezier': cubicBezierEaseInOut,
'--size': height || `${size}px`
}
})
}
}
})
</script>

View File

@ -1 +0,0 @@
export const validSize = ['tiny', 'small', 'medium', 'large', 'huge']

View File

@ -6,7 +6,9 @@ import { c, cE, cB } from '../../../_utils/cssr'
// --color
// --bezier
// --size
export default cB('avatar', `
export default cB(
'avatar',
`
width: var(--size);
height: var(--size);
color: #FFF;
@ -20,23 +22,28 @@ export default cB('avatar', `
transition:
background-color .3s var(--bezier),
color .3s var(--bezier);
`, [
c('img', {
width: '100%',
height: '100%'
}),
cE('text', `
`,
[
c('img', {
width: '100%',
height: '100%'
}),
cE(
'text',
`
white-space: nowrap;
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
`),
cB('icon', {
verticalAlign: 'bottom',
fontSize: 'var(--size)'
}),
cE('text', {
lineHeight: 1.25
})
])
`
),
cB('icon', {
verticalAlign: 'bottom',
fontSize: 'var(--size)'
}),
cE('text', {
lineHeight: 1.25
})
]
)

View File

@ -1,6 +1,7 @@
import { commonDark } from '../../_styles/new-common'
import { AvatarTheme } from './light'
export default {
const avatarDark: AvatarTheme = {
name: 'Avatar',
common: commonDark,
self (vars) {
@ -26,3 +27,5 @@ export default {
}
}
}
export default avatarDark

View File

@ -1,2 +0,0 @@
export { default as avatarDark } from './dark.js'
export { default as avatarLight } from './light.js'

View File

@ -0,0 +1,3 @@
export { default as avatarDark } from './dark'
export { default as avatarLight } from './light'
export type { AvatarThemeVars, AvatarTheme } from './light'

View File

@ -1,28 +0,0 @@
import { commonLight } from '../../_styles/new-common'
export default {
name: 'Avatar',
common: commonLight,
self (vars) {
const {
borderRadius,
avatarColorOverlay,
fontSize,
heightTiny,
heightSmall,
heightMedium,
heightLarge,
heightHuge
} = vars
return {
borderRadius,
fontSize,
heightTiny,
heightSmall,
heightMedium,
heightLarge,
heightHuge,
color: avatarColorOverlay
}
}
}

View File

@ -0,0 +1,37 @@
import { commonLight } from '../../_styles/new-common'
import type { ThemeCommonVars } from '../../_styles/new-common'
import type { Theme } from '../../_mixins'
const self = (vars: ThemeCommonVars) => {
const {
borderRadius,
avatarColorOverlay,
fontSize,
heightTiny,
heightSmall,
heightMedium,
heightLarge,
heightHuge
} = vars
return {
borderRadius,
fontSize,
heightTiny,
heightSmall,
heightMedium,
heightLarge,
heightHuge,
color: avatarColorOverlay
}
}
export type AvatarThemeVars = ReturnType<typeof self>
const avatarLight: Theme<AvatarThemeVars> = {
name: 'Avatar',
common: commonLight,
self
}
export default avatarLight
export type AvatarTheme = typeof avatarLight

View File

@ -6,6 +6,9 @@
"strictNullChecks": true,
"allowJs": true,
"checkJs": true,
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
"module": "ES6",
"moduleResolution": "Node",
"target": "ES6",

View File

@ -36,5 +36,9 @@ module.exports = {
})
]
}
},
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment'
}
}