feat(sites): add mobile-first playground (#864)

This commit is contained in:
gimmyhehe 2023-11-19 18:07:58 -08:00 committed by GitHub
parent 45acef6cb2
commit 4ccc2ba1eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 55 deletions

View File

@ -14,29 +14,38 @@ import Share from './icons/Share.vue'
const versions = ['3.11', '3.10', '3.9', '3.8']
const latestVersion = versions[0]
const cdnHost = window.localStorage.getItem('setting-cdn')
const getRuntime = (version) => `${cdnHost}/@opentiny/vue@${version}/runtime/`
const searchObj = new URLSearchParams(location.search)
const tinyMode = searchObj.get('mode')
const tinyTheme = tinyMode === 'saas' ? 'aurora' : searchObj.get('theme')
const isMobileFirst = tinyMode === 'mobile-first'
const createImportMap = (version) => {
const imports = {
'@opentiny/vue': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue.mjs`,
'@opentiny/vue-icon': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-icon.mjs`,
'@opentiny/vue-locale': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-locale.mjs`,
'@opentiny/vue-common': `${cdnHost}/@opentiny/vue@${version}/runtime/tiny-vue-common.mjs`,
'@opentiny/vue': `${getRuntime(version)}tiny-vue.mjs`,
'@opentiny/vue-icon': `${getRuntime(version)}tiny-vue-icon.mjs`,
'@opentiny/vue-locale': `${getRuntime(version)}tiny-vue-locale.mjs`,
'@opentiny/vue-common': `${getRuntime(version)}tiny-vue-common.mjs`,
'@opentiny/vue-theme/': `${cdnHost}/@opentiny/vue-theme@${version}/`,
'sortablejs': `${cdnHost}/sortablejs@1.15.0/modular/sortable.esm.js`
}
if (['aurora', 'smb'].includes(tinyTheme)) {
imports[`@opentiny/vue-design-${tinyTheme}`] = `${cdnHost}/@opentiny/vue-design-${tinyTheme}@${version}/index.js`
}
if (isMobileFirst) {
imports['@opentiny/vue'] = `${getRuntime(version)}tiny-vue-mobile-first.mjs`
imports['@opentiny/vue-icon'] = `${getRuntime(version)}tiny-vue-icon-saas.mjs`
}
return {
imports
}
}
const getTinyTheme = (version) => {
if (isMobileFirst) {
return `${getRuntime(version)}tailwind.css`
}
let theme = tinyMode === 'saas' ? 'saas' : tinyTheme
if (!['smb', 'default', 'aurora', 'saas'].includes(theme)) {
theme = 'default'
@ -81,7 +90,7 @@ const state = reactive({
const designThemeMap = {
aurora: 'tinyAuroraTheme',
infinity: 'tinyInfinityTheme',
smb: 'tinySmbTheme',
smb: 'tinySmbTheme'
}
function setTinyDesign() {
@ -90,14 +99,14 @@ function setTinyDesign() {
// aurorasmbdesign
if (['aurora', 'smb'].includes(tinyTheme)) {
importCode += `import designConfig from '@opentiny/vue-design-${tinyTheme}';
import { design } from '@opentiny/vue-common';\n`,
import { design } from '@opentiny/vue-common';\n`
useCode += 'app.provide(design.configKey, designConfig)\n'
}
if (['aurora', 'infinity', 'smb'].includes(tinyTheme)) {
const designTheme = designThemeMap[tinyTheme]
importCode += `import TinyThemeTool from '@opentiny/vue-theme/theme-tool';
import { ${designTheme} } from '@opentiny/vue-theme/theme';\n`,
import { ${designTheme} } from '@opentiny/vue-theme/theme';\n`
useCode += `const theme = new TinyThemeTool(${designTheme})\n`
}

View File

@ -2,13 +2,18 @@
<div class="hp100 of-hidden">
<tiny-config-provider :design="designConfig">
<router-view />
<tiny-modal v-show="visible" :show-header="false" :show-footer="false" v-model="modalSHow" fullscreen>
<tiny-icon-close class="close-icon" @click="visible = !visible"></tiny-icon-close>
<iframe width="100%" height="100%" :src="previewUrl" frameborder="0"></iframe>
</tiny-modal>
</tiny-config-provider>
</div>
</template>
<script>
import { defineComponent, reactive, computed, onMounted, toRefs } from 'vue'
import { ConfigProvider } from '@opentiny/vue'
import { defineComponent, onMounted, provide, ref } from 'vue'
import { ConfigProvider, Modal } from '@opentiny/vue'
import { iconClose } from '@opentiny/vue-icon'
import { appData } from './tools'
import useTheme from './tools/useTheme'
@ -16,9 +21,14 @@ export default defineComponent({
name: 'AppVue',
props: [],
components: {
TinyConfigProvider: ConfigProvider
TinyConfigProvider: ConfigProvider,
TinyModal: Modal,
TinyIconClose: iconClose()
},
setup() {
const visible = ref(false)
const previewUrl = ref(import.meta.env.VITE_PLAYGROUND_URL)
const modalSHow = ref(false)
onMounted(() => {
// header
const common = new window.TDCommon(['#header'], {
@ -31,12 +41,35 @@ export default defineComponent({
}
})
common.renderHeader()
modalSHow.value = true
})
const { designConfig } = useTheme()
provide('showPreview', (url) => {
previewUrl.value = url
visible.value = true
})
return {
appData,
designConfig
designConfig,
visible,
previewUrl,
modalSHow
}
}
})
</script>
<style scoped lang="less">
.close-icon.tiny-svg {
position: absolute;
right: 10px;
top: 10px;
font-size: 16px;
}
.tiny-modal {
:deep(.tiny-modal__body) {
padding: 34px 0 0;
}
}
</style>

View File

@ -4,7 +4,7 @@
<!-- DEMO 的标题 + 说明desc+ 示例wcTag -->
<div class="ti-f-r ti-f-pos-between ti-f-box-end ti-pb20">
<div class="ti-f18 ti-cur-hand">{{ demo.name[langKey] }}</div>
<div v-if="!isMobileFirst">
<div>
<tiny-tooltip placement="top" :append-to-body="false" :content="copyTip">
<i
:class="copyIcon"
@ -32,7 +32,7 @@
<component :is="getDescMd(demo)" class="ti-mb16 ti-f14" />
<div v-if="isMobileFirst" class="pc-demo-container">
<tiny-button @click="openPlayground(demo)">多端预览</tiny-button>
<tiny-button @click="openPlayground(demo, false)">多端预览</tiny-button>
</div>
<div v-else-if="demoConfig.isMobile" class="phone-container">
<div class="mobile-view-container">
@ -64,7 +64,7 @@
</template>
<script lang="jsx">
import { defineComponent, reactive, computed, toRefs, shallowRef, onMounted, watch, nextTick } from 'vue'
import { defineComponent, reactive, computed, toRefs, shallowRef, onMounted, watch, nextTick, inject } from 'vue'
import { $t, $t2 } from '@/i18n'
import { $split, appData, fetchDemosFile, $pub } from '@/tools'
import { Tooltip as TinyTooltip, Tabs as TinyTabs, TabItem as TinyTabItem, Button as TinyButton } from '@opentiny/vue'
@ -77,33 +77,6 @@ import AsyncHighlight from './async-highlight.vue'
const { apiModeState, apiModeFn } = useApiMode()
const getDemoCodeFn = async (demo, forceUpdate) => {
// code
if (!demo.files || forceUpdate) {
const cmpId = router.currentRoute.value.params.cmpId
const promises = demo.codeFiles.map(async (fileName) => {
// option-apicomposition-api
const demoName = apiModeFn.getDemoName(`${getWebdocPath(cmpId)}/${fileName}`)
let code = ''
const path = `${staticDemoPath}/${demoName}`
code = await fetchDemosFile(path)
.then((code) => {
return code
})
.catch((error) => {
return `${demoName}示例资源不存在,请检查文件名是否正确?`
})
const ext = $split(fileName, '.', -1)
const language = languageMap[ext] || ''
return { code, fileName, language }
})
demo.files = await Promise.all(promises)
return demo.files
}
return demo.files
}
export default defineComponent({
name: 'Demo',
props: ['demo'],
@ -120,6 +93,34 @@ export default defineComponent({
const isMobileFirst = computed(() => {
return templateModeState.mode === 'mobile-first'
})
const getDemoCodeFn = async (demo, forceUpdate) => {
// code
if (!demo.files || forceUpdate) {
const cmpId = router.currentRoute.value.params.cmpId
const promises = demo.codeFiles.map(async (fileName) => {
// option-apicomposition-api
const demoName = apiModeFn.getDemoName(`${getWebdocPath(cmpId)}/${fileName}`)
let code = ''
// const path = `${staticDemoPath}/${demoName}`
const path = isMobileFirst ? `@demos/mobile-first/app/${demoName}` : `${staticDemoPath}/${demoName}`
code = await fetchDemosFile(path)
.then((code) => {
return code
})
.catch((error) => {
return `${demoName}示例资源不存在,请检查文件名是否正确?`
})
const ext = $split(fileName, '.', -1)
const language = languageMap[ext] || ''
return { code, fileName, language }
})
demo.files = await Promise.all(promises)
return demo.files
}
return demo.files
}
const state = reactive({
tabValue: 'tab0',
cmpId: router.currentRoute.value.params.cmpId,
@ -139,6 +140,8 @@ export default defineComponent({
const cmp = shallowRef(null)
const showPreview = inject('showPreview')
const fn = {
getDescMd(demo) {
// descmd,html mdhtml
@ -180,14 +183,18 @@ export default defineComponent({
// code
return getDemoCodeFn(demo)
},
openPlayground(demo) {
openPlayground(demo, open = true) {
const cmpId = router.currentRoute.value.params.cmpId
const tinyTheme = currThemeLabel.value.split('-')[1]
window.open(
`${import.meta.env.VITE_PLAYGROUND_URL}?cmpId=${cmpId}&fileName=${demo.codeFiles[0]}&apiMode=${
apiModeState.apiMode
}&mode=${templateModeState.mode}&theme=${tinyTheme}`
)
const url = `${import.meta.env.VITE_PLAYGROUND_URL}?cmpId=${cmpId}&fileName=${demo.codeFiles[0]}&apiMode=${
apiModeState.apiMode
}&mode=${templateModeState.mode}&theme=${tinyTheme}`
if (open) {
window.open(url)
} else {
showPreview(url)
}
}
}

View File

@ -22,7 +22,8 @@
"rollup": "^3.7.3",
"semver": "^7.3.8",
"tsup": "7.2.0",
"vite": "^4.3.8"
"vite": "^4.3.8",
"tailwindcss": "^3.2.4"
},
"bin": {
"cli": "./dist/cjs/index.js"

View File

@ -7,6 +7,7 @@ import babel from '@rollup/plugin-babel'
import { logGreen } from '../../shared/utils'
import type { BuildUiOption, BaseConfig } from './build-ui'
import { pathFromPackages, getBaseConfig, requireModules } from './build-ui'
import { createProcessor } from 'tailwindcss/src/cli/build/plugin'
const argv = minimist(process.argv.slice(2))
const { tiny_mode = 'pc' } = argv
@ -123,11 +124,7 @@ async function batchBuildAll({ vueVersion, tasks, message, emptyOutDir, npmScope
}
function getEntryTasks() {
return [
{
path: 'vue-icon/index.ts',
libPath: 'tiny-vue-icon'
},
const entry = [
{
path: 'vue-locale/src/index.ts',
libPath: 'tiny-vue-locale'
@ -141,6 +138,18 @@ function getEntryTasks() {
libPath: tiny_mode === 'mobile-first' ? 'tiny-vue-mobile-first' : 'tiny-vue'
}
]
if (tiny_mode === 'mobile-first') {
entry.push({
path: 'vue-icon-saas/index.ts',
libPath: 'tiny-vue-icon-saas'
})
} else {
entry.push({
path: 'vue-icon/index.ts',
libPath: 'tiny-vue-icon'
})
}
return entry
}
export async function buildRuntime({
@ -163,6 +172,19 @@ export async function buildRuntime({
for (let i = 0; i < tasks.length; i++) {
await batchBuildAll({ vueVersion, tasks: [tasks[i]], message, emptyOutDir, npmScope: scope, min })
}
if (tiny_mode === 'mobile-first') {
const rootDir = pathFromPackages('')
const runtimeDir = `dist${vueVersion}/@opentiny/vue/runtime`
const outDir = path.resolve(rootDir, runtimeDir)
const processor = await createProcessor(
{
'--output': path.join(outDir, 'tailwind.css'),
'--content': path.join(outDir, 'tiny-vue-mobile-first.mjs')
},
path.resolve(rootDir, 'theme-saas/tailwind.config.js')
)
await processor.build()
}
// 确保只运行一次
emptyOutDir = false
}

View File

@ -58,7 +58,7 @@
"create:mapping": "pnpm -C internals/cli create:mapping",
"sync-icons": "pnpm -C internals/cli sync-icons",
"// ---------- 打包运行时组件库 ----------": "",
"build:runtime": "pnpm -C internals/cli build:runtime",
"build:runtime": "pnpm -C internals/cli build:runtime && pnpm -C internals/cli build:runtime-mf",
"// ---------- 构建相关脚本 ----------": "",
"build:ui": "pnpm create:icon-saas && pnpm create:mapping && pnpm build:entry && gulp themeConcat && pnpm -C internals/cli build:ui",
"build:renderless": "pnpm -C packages/renderless build:fast",
@ -256,4 +256,4 @@
"> 1%",
"last 2 versions"
]
}
}