Skip to content

Commit

Permalink
feat: support react
Browse files Browse the repository at this point in the history
  • Loading branch information
surunzi committed Jun 30, 2023
1 parent d56e613 commit 49accaa
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 27 deletions.
10 changes: 10 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ module.exports = {
},
},
],
babel: async (options) => {
options.presets.push([
'@babel/preset-react',
{
runtime: 'automatic',
},
])

return options
},
webpackFinal: (config) => {
const rules = config.module.rules

Expand Down
1 change: 1 addition & 0 deletions index.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"install": false
},
"modal": {
"react": true,
"icon": true,
"version": "1.0.0",
"style": true,
Expand Down
10 changes: 10 additions & 0 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ module.exports = wrap(async function (component) {
if (!isEmpty(peerDependencies)) {
pkg.peerDependencies = peerDependencies
}
pkg.exports = {
'.': `esm/${component}/index.js`,
'./css': `./luna-${component}.css`,
[`./luna-${component}.css`]: `./luna-${component}.css`,
[`./luna-${component}.js`]: `./luna-${component}.js`,
'./package.json': './package.json',
}
if (config.react) {
pkg.exports['./react'] = `./esm/${component}/react.js`
}
}

await fs.writeFile(
Expand Down
4 changes: 3 additions & 1 deletion lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ exports.readComponentConfig = function (component) {
icon: false,
test: true,
install: false,
react: false,
dependencies: [],
})

Expand All @@ -97,6 +98,7 @@ const config = defaults(pkg.luna || {}, {
icon: false,
test: true,
install: false,
react: false,
dependencies: []
})`

Expand Down Expand Up @@ -129,7 +131,7 @@ exports.createKarmaConf = async function (component) {

exports.createTsConfig = async function (component, esm) {
let files = await fs.readdir(resolve(`../src/${component}`))
files = filter(files, (file) => endWith(file, '.ts'))
files = filter(files, (file) => endWith(file, '.ts') || endWith(file, '.tsx'))

const tsConfig = {
extends: '../../tsconfig.json',
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,16 @@
},
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/preset-react": "^7.22.5",
"@metahub/karma-postcss-preprocessor": "^4.0.1",
"@storybook/addon-knobs": "^6.2.9",
"@storybook/addon-storysource": "^6.4.14",
"@storybook/addons": "^6.4.14",
"@storybook/html": "^6.4.14",
"@storybook/theming": "^6.4.14",
"@types/node": "^17.0.21",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"autoprefixer": "^9.7.4",
Expand All @@ -68,6 +71,8 @@
"postcss-clean": "^1.1.0",
"postcss-loader": "^3.0.0",
"postcss-prefixer": "^2.1.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sass": "^1.62.1",
"sass-loader": "^10.2.0",
"shelljs": "^0.8.3",
Expand Down
4 changes: 2 additions & 2 deletions src/modal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class Modal extends Component<IOptions> {
this.$container.rmClass(this.c('hidden'))
}
/** Hide the modal. */
hide = () => {
hide() {
this.$container.addClass(this.c('hidden'))
}
destroy() {
Expand Down Expand Up @@ -189,7 +189,7 @@ export default class Modal extends Component<IOptions> {
globalContainer = container
}
private bindEvent() {
this.$body.on('click', this.c('.icon-close'), this.hide)
this.$body.on('click', this.c('.icon-close'), () => this.hide())
this.on('optionChange', this.render)
}
private render = () => {
Expand Down
1 change: 1 addition & 0 deletions src/modal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "Create modal dialogs",
"luna": {
"react": true,
"icon": true
}
}
66 changes: 66 additions & 0 deletions src/modal/react.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { FC, PropsWithChildren, useEffect, useRef } from 'react'
import { createPortal } from 'react-dom'
import h from 'licia/h'
import types from 'licia/types'
import Modal from './index'

interface IModalProps {
title: string
visible: boolean
width?: number
onClose?: () => void
}

const LunaModal: FC<PropsWithChildren<IModalProps>> = (props) => {
const modalRef = useRef<HTMLDivElement>(null)
const modal = useRef<Modal>()
const content = useRef<HTMLDivElement>(h('div') as HTMLDivElement)
const doHide = useRef<types.AnyFn>()

useEffect(() => {
modal.current = new Modal(modalRef.current!, {
title: props.title,
content: content.current,
})
doHide.current = modal.current.hide
modal.current.hide = function () {
props.onClose && props.onClose()
}
if (props.visible) {
modal.current.show()
}
if (props.width) {
modal.current.setOption('width', props.width)
}

return () => modal.current?.destroy()
}, [])

useEffect(() => {
if (modal.current) {
modal.current.setOption('title', props.title)
}
}, [props.title])

useEffect(() => {
if (modal.current) {
if (props.visible) {
modal.current.show()
} else {
doHide.current && doHide.current.call(modal.current)
}
}
}, [props.visible])

useEffect(() => {
if (modal.current) {
modal.current.setOption('width', props.width)
}
}, [props.width])

return <div ref={modalRef}>
{createPortal(<>{props.children}</>, content.current)}
</div>
}

export default LunaModal
60 changes: 52 additions & 8 deletions src/modal/story.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import 'luna-modal.css'
import Modal from 'luna-modal.js'
import LunaModal from './react'
import readme from './README.md'
import changelog from './CHANGELOG.md'
import story from '../share/story'
import { text, button } from '@storybook/addon-knobs'
import { text, button, number } from '@storybook/addon-knobs'
import { useState } from 'react'

const def = story(
'modal',
(container) => {
const title = text('Modal Title', 'This is the Title')
const content = text('Modal Content', 'This is the modal content.')
const { title, content, width } = createKnobs()

const modal = new Modal(container, {
title,
content,
width,
})
modal.show()

Expand All @@ -22,13 +24,13 @@ const def = story(
return false
})

button('hide', () => {
button('Hide', () => {
modal.hide()
return false
})

const alertContent = text('Alert Content', 'This is the alert content.')
button('alert', () => {
button('Alert', () => {
modal.hide()
Modal.alert(alertContent)

Expand All @@ -39,7 +41,7 @@ const def = story(
'Confirm Content',
'This is the confirm content.'
)
button('confirm', () => {
button('Confirm', () => {
modal.hide()
Modal.confirm(confirmContent).then((result) => {
console.log('Confirm result:', result)
Expand All @@ -49,7 +51,7 @@ const def = story(

const promptTitle = text('Prompt Title', 'This is the prompt title.')
const promptDefault = text('Prompt Default', 'This is the default text.')
button('prompt', () => {
button('Prompt', () => {
modal.hide()
Modal.prompt(promptTitle, promptDefault).then((result) => {
console.log('Prompt result:', result)
Expand All @@ -63,9 +65,51 @@ const def = story(
readme,
changelog,
source: __STORY__,
ReactComponent() {
const { title, content, width } = createKnobs()
const [visible, setVisible] = useState(true)

button('Show', () => {
setVisible(true)
return false
})

button('Hide', () => {
setVisible(false)
return false
})

return (
<LunaModal
title={title}
visible={visible}
width={width}
onClose={() => setVisible(false)}
>
{content}
</LunaModal>
)
},
}
)

function createKnobs() {
const title = text('Modal Title', 'This is the Title')
const content = text('Modal Content', 'This is the modal content.')
const width = number('Modal Width', 500, {
range: true,
min: 250,
max: 1000,
stp: 1,
})

return {
title,
content,
width,
}
}

export default def

export const { modal } = def
export const { modal: html, react } = def
60 changes: 45 additions & 15 deletions src/share/story.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,29 @@ import { addReadme } from 'storybook-readme/html'
import each from 'licia/each'
import addons from '@storybook/addons'
import now from 'licia/now'
import ReactDOM from 'react-dom'
import * as registerKnobs from '@storybook/addon-knobs/dist/registerKnobs'
import { optionsKnob } from '@storybook/addon-knobs'

export default function story(
name,
storyFn,
{ readme, changelog = '', source, layout = 'padded', themes = {} } = {}
{
readme,
changelog = '',
source,
layout = 'padded',
themes = {},
ReactComponent = false,
} = {}
) {
const container = h('div')

if (changelog) {
readme += `\n## Changelog\n${changelog.replace(/## /g, '### ')}`
}

return {
const ret = {
title: map(spaceCase(name).split(' '), upperFirst).join(' '),
decorators: [withKnobs, addReadme],
parameters: {
Expand All @@ -42,19 +50,7 @@ export default function story(
layout,
},
[camelCase(name)]: () => {
if (window.components) {
const lastComponentName = window.componentName
if (upperFirst(camelCase(name)) !== lastComponentName) {
// Fix knobs not reset when story changed.
const knobStore = registerKnobs.manager.knobStore
knobStore.reset()
addons.getChannel().emit('storybookjs/knobs/set', {
knobs: knobStore.getAll(),
timestamp: now(),
})
}
each(window.components, (component) => component.destroy())
}
fixKnobs(name)

waitUntil(() => container.parentElement).then(() => {
const theme = optionsKnob(
Expand Down Expand Up @@ -92,4 +88,38 @@ export default function story(
return container
},
}

if (ReactComponent) {
const container = h('div')

ret.react = function () {
fixKnobs(`react-${name}`)

window.components = []
delete window.component
window.componentName = upperFirst(camelCase(`react-${name}`))

ReactDOM.render(<ReactComponent />, container)

return container
}
}

return ret
}

function fixKnobs(name) {
if (window.components) {
const lastComponentName = window.componentName
if (upperFirst(camelCase(name)) !== lastComponentName) {
// Fix knobs not reset when story changed.
const knobStore = registerKnobs.manager.knobStore
knobStore.reset()
addons.getChannel().emit('storybookjs/knobs/set', {
knobs: knobStore.getAll(),
timestamp: now(),
})
}
each(window.components, (component) => component.destroy())
}
}
1 change: 0 additions & 1 deletion src/share/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ module.exports = function (
if (hasStyle) {
entry.unshift(`./src/${name}/style.scss`)
}

if (useIcon) {
entry.unshift(`./src/${name}/icon.css`)
}
Expand Down
Loading

0 comments on commit 49accaa

Please sign in to comment.