apptify 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THIRD_PARTY_LICENSES.txt +23 -0
- package/dist/apptify.css +1 -1
- package/dist/apptify.js +2395 -2451
- package/dist/components/AppleCard.vue.d.ts +1 -1
- package/dist/components/content.d.ts +81 -20
- package/dist/components/forms.d.ts +2 -2
- package/dist/components/foundation.d.ts +133 -11
- package/dist/components/mobile-image-viewer.d.ts +35 -12
- package/dist/components/navibar.d.ts +2 -2
- package/dist/components/overlays.d.ts +10 -59
- package/dist/components/scrollbar.d.ts +88 -0
- package/dist/components/speed-dial.d.ts +157 -0
- package/dist/core/context.d.ts +11 -0
- package/dist/core/device.d.ts +2 -0
- package/dist/core/image-gallery-motion.d.ts +2 -1
- package/dist/core/image-motion.d.ts +1 -1
- package/dist/core/layers.d.ts +1 -0
- package/dist/core/motion-driver.d.ts +40 -0
- package/dist/core/motion.d.ts +1 -1
- package/dist/core/ripple.d.ts +16 -0
- package/dist/core/scroll-motion.d.ts +6 -0
- package/dist/core/scrollbar.d.ts +14 -0
- package/dist/core/speed-dial.d.ts +21 -0
- package/dist/index.d.ts +237 -26
- package/docs/COMPONENTS.md +10 -6
- package/package.json +5 -7
- package/templates/playground/README.md +1 -1
- package/templates/playground/package.json +1 -2
- package/templates/playground/source-snapshot.json +299 -66
- package/templates/playground/src/App.vue +1 -0
- package/templates/playground/src/ComponentDemo.vue +30 -21
- package/templates/playground/src/catalog.ts +70 -65
- package/templates/playground/src/components/ComponentIndex.vue +10 -14
- package/templates/playground/src/components/MasonryFeed.vue +52 -0
- package/templates/playground/src/components/UserMenu.vue +1 -1
- package/templates/playground/src/components/forbidden-paper-scene.vue +367 -0
- package/templates/playground/src/components/forbidden-paper-story.ts +233 -0
- package/templates/playground/src/editor/ComponentPlayground.vue +89 -0
- package/templates/playground/src/editor/ConfigurableEditor.vue +1 -1
- package/templates/playground/src/editor/apple-input-source.ts +1 -1
- package/templates/playground/src/editor/component-metadata.ts +72 -0
- package/templates/playground/src/editor/component-playground.ts +162 -0
- package/templates/playground/src/editor/contract.ts +1 -1
- package/templates/playground/src/editor/documents.ts +11 -2
- package/templates/playground/src/editor/handbook.ts +22 -0
- package/templates/playground/src/editor/handbooks/apple-accordion.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-action-sheet.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-alert.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-aside.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-auto-size.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-autocomplete.ts +114 -0
- package/templates/playground/src/editor/handbooks/apple-avatar-group.ts +50 -0
- package/templates/playground/src/editor/handbooks/apple-avatar.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-back-top.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-badge.ts +62 -0
- package/templates/playground/src/editor/handbooks/apple-breadcrumbs.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-button.ts +92 -0
- package/templates/playground/src/editor/handbooks/apple-card.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-cascader.ts +102 -0
- package/templates/playground/src/editor/handbooks/apple-checkbox.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-color-picker.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-container.ts +26 -0
- package/templates/playground/src/editor/handbooks/apple-date-picker.ts +104 -0
- package/templates/playground/src/editor/handbooks/apple-dialog.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-divider.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-drawer.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-empty.ts +43 -0
- package/templates/playground/src/editor/handbooks/apple-floating-group.ts +51 -0
- package/templates/playground/src/editor/handbooks/apple-form-field.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-form.ts +76 -0
- package/templates/playground/src/editor/handbooks/apple-grid.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-image.ts +122 -0
- package/templates/playground/src/editor/handbooks/apple-infinite-scroll.ts +73 -0
- package/templates/playground/src/editor/handbooks/apple-link.ts +68 -0
- package/templates/playground/src/editor/handbooks/apple-list.ts +67 -0
- package/templates/playground/src/editor/handbooks/apple-menu.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-navibar.ts +100 -0
- package/templates/playground/src/editor/handbooks/apple-otp-input.ts +108 -0
- package/templates/playground/src/editor/handbooks/apple-overlay-host.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-pagination.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-popover.ts +112 -0
- package/templates/playground/src/editor/handbooks/apple-progress.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-provider.ts +32 -0
- package/templates/playground/src/editor/handbooks/apple-pull-refresh.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-radio-group.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-rate.ts +97 -0
- package/templates/playground/src/editor/handbooks/apple-scroll-bar.ts +15 -0
- package/templates/playground/src/editor/handbooks/apple-search.ts +49 -0
- package/templates/playground/src/editor/handbooks/apple-segmented-control.ts +85 -0
- package/templates/playground/src/editor/handbooks/apple-select.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-sheet.ts +151 -0
- package/templates/playground/src/editor/handbooks/apple-skeleton.ts +74 -0
- package/templates/playground/src/editor/handbooks/apple-slider.ts +103 -0
- package/templates/playground/src/editor/handbooks/apple-snackbar.ts +89 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial-item.ts +16 -0
- package/templates/playground/src/editor/handbooks/apple-speed-dial.ts +13 -0
- package/templates/playground/src/editor/handbooks/apple-spinner.ts +38 -0
- package/templates/playground/src/editor/handbooks/apple-stack.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-statistic.ts +69 -0
- package/templates/playground/src/editor/handbooks/apple-stepper.ts +91 -0
- package/templates/playground/src/editor/handbooks/apple-steps.ts +61 -0
- package/templates/playground/src/editor/handbooks/apple-switch.ts +79 -0
- package/templates/playground/src/editor/handbooks/apple-tab-bar.ts +66 -0
- package/templates/playground/src/editor/handbooks/apple-table.ts +176 -0
- package/templates/playground/src/editor/handbooks/apple-tabs.ts +72 -0
- package/templates/playground/src/editor/handbooks/apple-tag.ts +56 -0
- package/templates/playground/src/editor/handbooks/apple-textarea.ts +109 -0
- package/templates/playground/src/editor/handbooks/apple-timeline.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tooltip.ts +45 -0
- package/templates/playground/src/editor/handbooks/apple-transition.ts +44 -0
- package/templates/playground/src/editor/handbooks/apple-tree.ts +84 -0
- package/templates/playground/src/editor/handbooks/apple-upload.ts +126 -0
- package/templates/playground/src/editor/playground-examples.ts +105 -0
- package/templates/playground/src/images.ts +3 -0
- package/templates/playground/src/router/index.ts +19 -2
- package/templates/playground/src/style.css +4 -4
- package/templates/playground/src/views/403.vue +65 -0
- package/templates/playground/src/views/component-docs/[component].vue +11 -14
- package/templates/playground/src/views/components.vue +11 -41
- package/templates/playground/src/views/index.vue +3 -1
- package/templates/playground/src/views/settings.vue +5 -2
- package/tooling/create-playground.mjs +2 -2
- package/tooling/prepare-playground.mjs +3 -3
- package/tooling/test-create-apptify.mjs +3 -1
- package/tooling/test-playground-package.mjs +10 -0
- package/dist/components/gallery-scrollbar.d.ts +0 -46
- package/dist/core/desktop-image-enter.d.ts +0 -6
- package/dist/core/desktop-image-return.d.ts +0 -1
- package/templates/playground/src/components/DirectoryDialog.vue +0 -20
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
</apple-stack>
|
|
29
29
|
</apple-provider>
|
|
30
30
|
</apple-stack>
|
|
31
|
+
<apple-button v-else-if="name === 'AppleOverlayHost'" @click="notify('由 Provider 内的 OverlayHost 显示')">显示通知</apple-button>
|
|
31
32
|
<apple-stack v-else-if="name === 'AppleContainer'">
|
|
32
33
|
<apple-slider v-model="containerWidth" label="内容宽度" :min="240" :max="640" :step="40" />
|
|
33
34
|
<apple-container :width="containerWidth">
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
</apple-grid>
|
|
48
49
|
</apple-stack>
|
|
49
50
|
<apple-card v-else-if="name === 'AppleCard'" class="demo-product" title="AirPods Max" subtitle="每个音符,都更动听。" eyebrow="声音,出类拔萃。">
|
|
50
|
-
<apple-image :gallery="{ src: '
|
|
51
|
+
<apple-image :gallery="{ src: imageUrl('airpods-max-orange.jpg'), alt: '橙色 AirPods Max 耳机' }" aspect-ratio="1" fit="contain" />
|
|
51
52
|
<template #actions><apple-button @click="notify('已加入购物袋')">加入购物袋</apple-button><apple-link as="button" :icon="icons.Heart" icon-only label="收藏" @click="notify('已收藏')" /></template>
|
|
52
53
|
</apple-card>
|
|
53
54
|
<apple-stack v-else-if="name === 'AppleImage'" class="demo-image">
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
<apple-stack v-else-if="name === 'AppleBadge'" direction="row" align="center"><apple-badge :value="unread"><apple-link variant="secondary" :icon="icons.Bell" @click="unread++">新消息</apple-link></apple-badge><apple-link as="button" :disabled="!unread" @click="unread = 0">全部已读</apple-link></apple-stack>
|
|
62
63
|
<apple-stack v-else-if="name === 'AppleTag'" class="demo-tag-row" direction="row" align="center" :gap="10"><apple-tag>默认标签</apple-tag><apple-tag tone="success">已完成</apple-tag><apple-tag tone="info">进行中</apple-tag><apple-tag tone="warning">待确认</apple-tag><apple-tag v-if="tag" label="设计系统" closable @close="tag = false">设计系统</apple-tag><apple-link as="button" v-else :icon="icons.Plus" @click="tag = true">添加标签</apple-link></apple-stack>
|
|
63
64
|
<apple-stack v-else-if="name === 'AppleDivider'"><apple-switch v-model="dividerVertical" label="纵向分隔" /><apple-stack :direction="dividerVertical ? 'row' : 'column'" :gap="0" align="center"><span>配送到家</span><apple-divider :vertical="dividerVertical" :label="dividerVertical ? undefined : '更多选择'" /><span>到店取货</span></apple-stack></apple-stack>
|
|
65
|
+
<div v-else-if="name === 'AppleScrollBar'" class="demo-scroll-bar" tabindex="0" aria-label="滚动示例"><div class="demo-scroll-bar__content"><apple-card v-for="n in 12" :key="n" class="demo-layout-tile"><strong>0{{ n }}</strong></apple-card></div></div>
|
|
64
66
|
<apple-input v-else-if="name === 'AppleInput'" v-model="text" label="姓名" placeholder="怎么称呼你" clearable />
|
|
65
67
|
<apple-textarea v-else-if="name === 'AppleTextarea'" v-model="bio" label="个人简介" placeholder="写下你的想法" :maxlength="160" counter />
|
|
66
68
|
<apple-select v-else-if="name === 'AppleSelect'" v-model="city" label="所在城市" :items="cities" />
|
|
@@ -94,7 +96,7 @@
|
|
|
94
96
|
<apple-steps v-else-if="name === 'AppleSteps'" v-model="step" :items="steps" clickable />
|
|
95
97
|
<apple-accordion v-else-if="name === 'AppleAccordion'" :items="questions" />
|
|
96
98
|
<apple-stack v-else-if="name === 'AppleTable'"><apple-switch v-model="virtualTable" label="虚拟滚动 · 5000 行" /><apple-table :columns="columns" :rows="virtualTable ? virtualRows : rows" selectable :virtual="virtualTable" :height="360" :page-size="virtualTable ? 0 : 3" /></apple-stack>
|
|
97
|
-
<apple-tree v-else-if="name === 'AppleTree'" v-model="tree" :items="folders" />
|
|
99
|
+
<apple-tree v-else-if="name === 'AppleTree'" v-model="tree" :items="folders" :mobile-directory="false" />
|
|
98
100
|
<apple-list v-else-if="name === 'AppleList'" v-model="setting" :items="settings" selectable @select="notify('已选择设置项')" />
|
|
99
101
|
<apple-stack v-else-if="name === 'AppleTimeline'"><apple-segmented-control v-model="timelineDirection" :items="[{label:'纵向',value:'vertical'},{label:'横向',value:'horizontal'}]" label="时间线方向" /><apple-timeline :items="events" :orientation="timelineDirection" /><apple-link variant="secondary" @click="updateDelivery">{{ delivered ? '查看配送过程' : '确认签收' }}</apple-link></apple-stack>
|
|
100
102
|
<apple-stack v-else-if="name === 'AppleStatistic'"><apple-segmented-control v-model="period" :items="periods" label="统计周期" /><apple-statistic :label="period === 'day' ? '今日访问' : period === 'week' ? '本周访问' : '本月访问'" :value="period === 'day' ? 428 : period === 'week' ? 2996 : 12840" suffix="次" description="最新访问数据" /></apple-stack>
|
|
@@ -125,6 +127,8 @@
|
|
|
125
127
|
<apple-pull-refresh v-else-if="name === 'ApplePullRefresh'" @refresh="refresh"><apple-list :items="[{label:'最新内容',value:'latest',description:refreshText},{label:'最近更新',value:'recent',description:'所有内容已同步'}]" /></apple-pull-refresh>
|
|
126
128
|
<template v-else-if="name === 'AppleBackTop'"><apple-button :icon="icons.ArrowUp" variant="secondary" @click="toTop">回到顶部</apple-button><apple-back-top :threshold="0" :fixed="false" /></template>
|
|
127
129
|
<template v-else-if="name === 'AppleFloatingGroup'"><apple-button variant="secondary" @click="notify('快捷操作已就绪')">快捷操作</apple-button><apple-card class="demo-floating-preview"><apple-provider><apple-floating-group :threshold="0" /></apple-provider></apple-card></template>
|
|
130
|
+
<apple-card v-else-if="name === 'AppleSpeedDial'" class="demo-floating-preview"><apple-provider><apple-floating-group :back-top="false" label="全局快捷操作预览"><apple-back-top :threshold="0" :fixed="false" /><apple-button :icon="icons.Plus" icon-only label="新增" @click="notify('已新增')" /><apple-button :icon="icons.Menu" icon-only label="目录" :disabled="!$apple.speedDial.mobile.value" @click="$apple.speedDial.open.value = true" /></apple-floating-group></apple-provider></apple-card>
|
|
131
|
+
<apple-button v-else-if="name === 'AppleSpeedDialItem'" :icon="icons.Plus" icon-only label="新增" @click="notify('已新增')" />
|
|
128
132
|
<apple-stack v-else-if="name === 'AppleAutoSize'"><apple-switch v-model="checked" label="显示详情" /><apple-auto-size><apple-list :items="checked ? settings : settings.slice(0,1)" selectable /></apple-auto-size></apple-stack>
|
|
129
133
|
<apple-stack v-else-if="name === 'AppleTransition'">
|
|
130
134
|
<apple-segmented-control v-model="period" :items="periods" label="统计周期" />
|
|
@@ -139,33 +143,34 @@
|
|
|
139
143
|
</template>
|
|
140
144
|
|
|
141
145
|
<script lang="ts">
|
|
146
|
+
import { imageUrl } from './images'
|
|
142
147
|
import { defineComponent, h, markRaw } from 'vue'
|
|
143
|
-
import { Plus, Heart, Bell, ArrowUpRight, ArrowUp, MoreHorizontal, ZoomIn, Share2, Trash2 } from 'lucide-vue-next'
|
|
144
|
-
import { AppleButton } from 'apptify'
|
|
148
|
+
import { Plus, Heart, Bell, ArrowUpRight, ArrowUp, MoreHorizontal, ZoomIn, Share2, Trash2, Menu } from 'lucide-vue-next'
|
|
149
|
+
import { AppleButton, resolveMotion, scrollToWithMotion, type MotionScroll } from 'apptify'
|
|
145
150
|
export default defineComponent({
|
|
146
151
|
name: 'ComponentDemo', props: { name: { type: String, required: true } }, emits: ['navigate'],
|
|
147
152
|
data() { return {
|
|
148
|
-
icons: markRaw({ Plus, Heart, Bell, ArrowUpRight, ArrowUp, MoreHorizontal, ZoomIn }), text: '', bio: '', email: '', city: 'beijing', device: null, checked: true, delivery: 'express', volume: 68, quantity: 1, period: 'month', date: '', time: '10:30', color: '#0071e3', files: [], otp: '', region: [], rating: 4, tab: 'overview', page: 1, step: 1, tree: '', drawer: false, sheet: false, actionSheet: false, tag: true, refreshText: '今天 09:41', itemsCount: 3,
|
|
153
|
+
icons: markRaw({ Plus, Heart, Bell, ArrowUpRight, ArrowUp, MoreHorizontal, ZoomIn, Menu }), text: '', bio: '', email: '', city: 'beijing', device: null, checked: true, delivery: 'express', volume: 68, quantity: 1, period: 'month', date: '', time: '10:30', color: '#0071e3', files: [], otp: '', region: [], rating: 4, tab: 'overview', page: 1, step: 1, tree: '', drawer: false, sheet: false, actionSheet: false, tag: true, refreshText: '今天 09:41', itemsCount: 3,
|
|
149
154
|
searchText: '', setting: 'profile', website: '', previewTheme: 'dark', stackDirection: 'row' as 'row' | 'column', containerWidth: 480, gridMin: 160, avatarSize: 48, avatarMax: 3, unread: 8, dividerVertical: false, delivered: false, alertVisible: true, demoLoading: true, imageLayout: 'compact' as 'compact' | 'tiled' | 'tiled-wrap', imageSquare: false, imageCarousel: false,
|
|
150
155
|
virtualTable: false, transitionBackward: false,
|
|
151
156
|
timelineDirection:'vertical' as 'vertical'|'horizontal', skeletonVariant:'text' as 'text'|'avatar'|'card'|'list'|'table'|'image', skeletonVariants:[{label:'文本',value:'text'},{label:'头像',value:'avatar'},{label:'卡片',value:'card'},{label:'列表',value:'list'},{label:'表格',value:'table'},{label:'图片',value:'image'}],
|
|
152
157
|
dateFormat:'YYYY/MM/DD HH:mm', dateFormats:[{label:'年 / 月',value:'YYYY/MM'},{label:'年 / 月 / 日',value:'YYYY/MM/DD'},{label:'日期与时分',value:'YYYY/MM/DD HH:mm'},{label:'日期与时分秒',value:'YYYY/MM/DD HH:mm:ss'},{label:'时分',value:'HH:mm'},{label:'时分秒',value:'HH:mm:ss'}],
|
|
153
158
|
previewThemes: [{label:'浅色',value:'light'},{label:'深色',value:'dark'},{label:'石墨',value:'graphite'},{label:'玫瑰',value:'rose'}], layoutItems: ['概览', '设计', '支持'],
|
|
154
159
|
galleryImages: [
|
|
155
|
-
{ src: '
|
|
156
|
-
{ src: '
|
|
157
|
-
{ src: '
|
|
158
|
-
{ src: '
|
|
159
|
-
{ src: '
|
|
160
|
-
{ src: '
|
|
161
|
-
{ src: '
|
|
162
|
-
{ src: '
|
|
163
|
-
{ src: '
|
|
164
|
-
{ src: '
|
|
165
|
-
{ src: '
|
|
166
|
-
{ src: '
|
|
160
|
+
{ src: imageUrl('lake.jpg'), alt: '山间湖泊与小屋' },
|
|
161
|
+
{ src: imageUrl('airpods-max-orange.jpg'), alt: '橙色 AirPods Max 耳机' },
|
|
162
|
+
{ src: imageUrl('city.jpg'), alt: '城市天际线' },
|
|
163
|
+
{ src: imageUrl('mountains.jpg'), alt: '群山与山峰' },
|
|
164
|
+
{ src: imageUrl('forest.jpg'), alt: '阳光下的森林' },
|
|
165
|
+
{ src: imageUrl('architecture.jpg'), alt: '建筑细节' },
|
|
166
|
+
{ src: imageUrl('coast.jpg'), alt: '海岸风景' },
|
|
167
|
+
{ src: imageUrl('food.jpg'), alt: '餐桌上的美食' },
|
|
168
|
+
{ src: imageUrl('desert.jpg'), alt: '沙漠沙丘' },
|
|
169
|
+
{ src: imageUrl('waterfall.jpg'), alt: '山间瀑布' },
|
|
170
|
+
{ src: imageUrl('sunset.jpg'), alt: '夕阳风景' },
|
|
171
|
+
{ src: imageUrl('snow.jpg'), alt: '雪地风景' },
|
|
167
172
|
],
|
|
168
|
-
images: ['
|
|
173
|
+
images: [imageUrl('lake.jpg'), imageUrl('airpods-max-orange.jpg')], people: [{name:'林初'},{name:'Alex'},{name:'Taylor'},{name:'Quinn'},{name:'Sam'}],
|
|
169
174
|
cities: [{label:'北京',value:'beijing'},{label:'上海',value:'shanghai'},{label:'杭州',value:'hangzhou'}], devices: [{label:'MacBook Air',value:'mac'},{label:'iPad Pro',value:'ipad'},{label:'AirPods Max',value:'airpods'}],
|
|
170
175
|
deliveryItems: [{label:'快递送达',value:'express'},{label:'到店取货',value:'pickup'}], periods: [{label:'日',value:'day'},{label:'周',value:'week'},{label:'月',value:'month'}],
|
|
171
176
|
regions: [{label:'北京市',value:'beijing',children:[{label:'朝阳区',value:'chaoyang'},{label:'海淀区',value:'haidian'}]},{label:'浙江省',value:'zhejiang',children:[{label:'杭州市',value:'hangzhou',children:[{label:'西湖区',value:'xihu'}]}]}],
|
|
@@ -175,8 +180,9 @@ export default defineComponent({
|
|
|
175
180
|
folders: [{label:'设计资源',value:'design',children:[{label:'组件',value:'components',children:[{label:'按钮',value:'buttons'},{label:'输入框',value:'inputs'}]},{label:'图标',value:'icons'}]},{label:'项目文件',value:'project'}],
|
|
176
181
|
settings: [{label:'个人资料',value:'profile',description:'姓名、头像和联系方式'},{label:'通知',value:'notifications',description:'推送和邮件偏好'},{label:'隐私与安全',value:'security',description:'管理账户的安全设置'}],
|
|
177
182
|
events: [{label:'订单已确认',value:'confirmed',description:'我们正在准备你的订单',time:'09:41'},{label:'正在配送',value:'shipping',description:'你的包裹已出发',time:'12:00'},{label:'即将送达',value:'arrival',description:'留意配送通知',time:'预计明天'}],
|
|
178
|
-
actions: [{label:'收藏',value:'favorite'},{label:'分享',value:'share'},{label:'删除',value:'delete',danger:true}], slides: [{label:'山间清晨',value:0,src:'
|
|
183
|
+
actions: [{label:'收藏',value:'favorite'},{label:'分享',value:'share'},{label:'删除',value:'delete',danger:true}], slides: [{label:'山间清晨',value:0,src:imageUrl('lake.jpg')},{label:'一场聆听',value:1,src:imageUrl('airpods-max-orange.jpg')}],
|
|
179
184
|
photoActions: [{label:'加入收藏',value:'favorite',description:'将这张照片保存到你的灵感收藏',icon:markRaw(Heart),danger:false},{label:'分享照片',value:'share',description:'与朋友分享山间的这一刻',icon:markRaw(Share2),danger:false},{label:'删除照片',value:'delete',description:'从当前相册中移除',icon:markRaw(Trash2),danger:true}],
|
|
185
|
+
topScroll: undefined as MotionScroll | undefined,
|
|
180
186
|
timers: [] as ReturnType<typeof setTimeout>[],
|
|
181
187
|
} },
|
|
182
188
|
computed: {
|
|
@@ -193,8 +199,9 @@ export default defineComponent({
|
|
|
193
199
|
this.transitionBackward = this.periods.findIndex(item => item.value === next) < this.periods.findIndex(item => item.value === previous)
|
|
194
200
|
},
|
|
195
201
|
},
|
|
196
|
-
beforeUnmount() { this.timers.forEach(clearTimeout) },
|
|
202
|
+
beforeUnmount() { this.topScroll?.cancel(); this.timers.forEach(clearTimeout) },
|
|
197
203
|
methods: {
|
|
204
|
+
imageUrl,
|
|
198
205
|
notify(message: string) { this.$apple.notify(message, {tone:'success'}) },
|
|
199
206
|
validate(data: FormData) { return String(data.get('name') ?? '').trim().length > 0 || '请输入姓名' },
|
|
200
207
|
updateDelivery() { this.delivered = !this.delivered; this.events[2] = this.delivered ? { label: '已签收', value: 'arrival', description: '包裹已送达,祝你使用愉快', time: new Date().toLocaleTimeString('zh-CN') } : { label: '即将送达', value: 'arrival', description: '留意配送通知', time: '预计明天' } },
|
|
@@ -206,7 +213,7 @@ export default defineComponent({
|
|
|
206
213
|
},
|
|
207
214
|
refresh(done: () => void) { this.timers.push(setTimeout(() => { this.refreshText = `更新于 ${new Date().toLocaleTimeString('zh-CN')}`; done() }, 700)) },
|
|
208
215
|
loadMore(done: () => void) { this.timers.push(setTimeout(() => { this.itemsCount = Math.min(12, this.itemsCount + 3); done() }, 500)) },
|
|
209
|
-
toTop() {
|
|
216
|
+
toTop() { this.topScroll = scrollToWithMotion(window, 0, resolveMotion('inherit', this.$apple.motion.value.mode, this.$apple.motion.value.reduced) === 'full') },
|
|
210
217
|
},
|
|
211
218
|
})
|
|
212
219
|
</script>
|
|
@@ -220,6 +227,8 @@ export default defineComponent({
|
|
|
220
227
|
.component-demo > .demo-tag-row > :deep(.apple-button) { align-self: center; max-width: 100%; }
|
|
221
228
|
|
|
222
229
|
.demo-transition-stage { display: grid; min-width: 0; overflow: hidden; --demo-slide-distance: 24px; }
|
|
230
|
+
.demo-scroll-bar { height: 240px; overflow: auto; border-radius: 12px; }
|
|
231
|
+
.demo-scroll-bar__content { width: 720px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; padding: 16px; }
|
|
223
232
|
.demo-transition-stage.is-backward { --demo-slide-distance: -24px; }
|
|
224
233
|
.demo-transition-stage :deep(.apple-statistic) { grid-area: 1 / 1; min-width: 0; }
|
|
225
234
|
.demo-transition-stage :deep(.apple-slide-x-enter-active),
|
|
@@ -1,70 +1,75 @@
|
|
|
1
|
+
import { componentMetadata } from './editor/component-metadata'
|
|
1
2
|
export interface CatalogItem { name: string; label: string; group: string; description: string; api: string; code: string }
|
|
2
|
-
const item = (name: string,
|
|
3
|
+
const item = (name: string, api: string, code: string): CatalogItem => ({ name, ...componentMetadata[name], api, code })
|
|
3
4
|
export const catalog: CatalogItem[] = [
|
|
4
|
-
item('AppleButton', '
|
|
5
|
-
item('AppleLink', '
|
|
6
|
-
item('AppleSearch', '
|
|
7
|
-
item('AppleProvider', '
|
|
8
|
-
item('
|
|
9
|
-
item('
|
|
10
|
-
item('
|
|
11
|
-
item('
|
|
12
|
-
item('
|
|
13
|
-
item('
|
|
14
|
-
item('
|
|
15
|
-
item('
|
|
16
|
-
item('
|
|
17
|
-
item('
|
|
18
|
-
item('
|
|
19
|
-
item('
|
|
20
|
-
item('
|
|
21
|
-
item('
|
|
22
|
-
item('
|
|
23
|
-
item('
|
|
24
|
-
item('
|
|
25
|
-
item('
|
|
26
|
-
item('
|
|
27
|
-
item('
|
|
28
|
-
item('
|
|
29
|
-
item('
|
|
30
|
-
item('
|
|
31
|
-
item('
|
|
32
|
-
item('
|
|
33
|
-
item('
|
|
34
|
-
item('
|
|
35
|
-
item('
|
|
36
|
-
item('
|
|
37
|
-
item('
|
|
38
|
-
item('
|
|
39
|
-
item('
|
|
40
|
-
item('
|
|
41
|
-
item('
|
|
42
|
-
item('
|
|
43
|
-
item('
|
|
44
|
-
item('
|
|
45
|
-
item('
|
|
46
|
-
item('
|
|
47
|
-
item('
|
|
48
|
-
item('
|
|
49
|
-
item('
|
|
50
|
-
item('
|
|
51
|
-
item('
|
|
52
|
-
item('
|
|
53
|
-
item('
|
|
54
|
-
item('
|
|
55
|
-
item('
|
|
56
|
-
item('
|
|
57
|
-
item('
|
|
58
|
-
item('
|
|
59
|
-
item('
|
|
60
|
-
item('
|
|
61
|
-
item('
|
|
62
|
-
item('
|
|
63
|
-
item('
|
|
64
|
-
item('
|
|
65
|
-
item('
|
|
66
|
-
item('
|
|
67
|
-
item('
|
|
5
|
+
item('AppleButton', 'variant · size · icon · iconOnly · label · loading · disabled · ripple · motion', '<apple-button variant="primary" :loading="saving" @click="save">继续</apple-button>'),
|
|
6
|
+
item('AppleLink', 'href · external · disabled | slot: default', '<apple-link href="https://www.apple.com.cn/" external>Apple 官网</apple-link>'),
|
|
7
|
+
item('AppleSearch', 'v-model(string) · label · placeholder · disabled | search(query)', '<apple-search v-model="query" label="搜索设置" placeholder="搜索设置" @search="search" />'),
|
|
8
|
+
item('AppleProvider', 'theme · motion(auto|full|reduced|none) | slot: default', '<apple-provider theme="dark" motion="auto">\n <apple-button>继续</apple-button>\n</apple-provider>'),
|
|
9
|
+
item('AppleOverlayHost', '由 AppleProvider 自动包含;无公开 props、事件或插槽', '<apple-overlay-host />'),
|
|
10
|
+
item('AppleContainer', 'width(number|string) | slot: default', '<apple-container :width="960">\n <apple-stack>内容</apple-stack>\n</apple-container>'),
|
|
11
|
+
item('AppleStack', 'direction(row|column) · gap · align · wrap | slot: default', '<apple-stack direction="row" :gap="16" align="center">\n <apple-button>继续</apple-button>\n <apple-button variant="secondary">取消</apple-button>\n</apple-stack>'),
|
|
12
|
+
item('AppleGrid', 'min(number) · gap(number) | slot: default', '<apple-grid :min="240" :gap="20">\n <div v-for="item in items" :key="item.id">{{ item.label }}</div>\n</apple-grid>'),
|
|
13
|
+
item('AppleCard', 'title · subtitle · text · image · zoom · shadow · motion | slots: media, title, icon, actions', '<apple-card title="AirPods Max" subtitle="声音,出类拔萃。" motion="auto">\n <template #actions><apple-button>进一步了解</apple-button></template>\n</apple-card>'),
|
|
14
|
+
item('AppleImage', 'gallery(object|array) · index · preview · carousel · autoplay · interval(5000ms) · disabled · label · galleryLayout(compact|tiled|tiled-wrap) · v-model:index | item({item,index,active}) · squared · aspectRatio · fit', '<apple-image :gallery="images" gallery-layout="compact" :index="0" />'),
|
|
15
|
+
item('AppleAvatar', 'src · name · size', '<apple-avatar name="林初" :size="48" />'),
|
|
16
|
+
item('AppleAvatarGroup', 'items[{name,src,value}] · max · size · label | slot: default', '<apple-avatar-group :items="members" :max="4" :size="40" label="项目成员" />'),
|
|
17
|
+
item('AppleBadge', 'value · max · dot · tone', '<apple-badge :value="8"><apple-button>消息</apple-button></apple-badge>'),
|
|
18
|
+
item('AppleTag', 'tone · closable | close', '<apple-tag tone="success" closable @close="remove">已完成</apple-tag>'),
|
|
19
|
+
item('AppleDivider', 'label · vertical', '<apple-divider label="更多选择" />'),
|
|
20
|
+
item('AppleInput', 'v-model · label · placeholder · type · error · hint · clearable · disabled', '<apple-input v-model="name" label="姓名" placeholder="怎么称呼你" clearable />'),
|
|
21
|
+
item('AppleTextarea', 'v-model · label · rows · maxlength · error · hint', '<apple-textarea v-model="bio" label="个人简介" :maxlength="160" />'),
|
|
22
|
+
item('AppleSelect', 'v-model · label · items[{label,value,disabled}] · placeholder · disabled', '<apple-select v-model="city" label="所在城市" :items="cities" />'),
|
|
23
|
+
item('AppleAutocomplete', 'v-model · label · items · placeholder · disabled', '<apple-autocomplete v-model="device" label="搜索设备" :items="devices" />'),
|
|
24
|
+
item('AppleCheckbox', 'v-model · label · disabled · indeterminate', '<apple-checkbox v-model="agreed" label="我已阅读并同意条款" />'),
|
|
25
|
+
item('AppleRadioGroup', 'v-model · label · items · disabled', '<apple-radio-group v-model="delivery" label="配送方式" :items="methods" />'),
|
|
26
|
+
item('AppleSwitch', 'v-model · label · disabled', '<apple-switch v-model="notifications" label="接收通知" />'),
|
|
27
|
+
item('AppleSlider', 'v-model · label · min · max · step · disabled', '<apple-slider v-model="volume" label="音量" :min="0" :max="100" />'),
|
|
28
|
+
item('AppleStepper', 'v-model · label · min · max · step', '<apple-stepper v-model="quantity" label="数量" :min="1" :max="10" />'),
|
|
29
|
+
item('AppleSegmentedControl', 'v-model · items · label · disabled', '<apple-segmented-control v-model="period" :items="periods" label="统计周期" />'),
|
|
30
|
+
item('AppleDatePicker', 'v-model · format · granularity · label · min · max · required · disabled | change(value)', '<apple-date-picker v-model="date" label="到店日期" format="YYYY/MM/DD HH:mm" />'),
|
|
31
|
+
item('AppleColorPicker', 'v-model(#hex) · label · disabled', '<apple-color-picker v-model="color" label="强调色" />'),
|
|
32
|
+
item('AppleUpload', 'v-model(File[]) · label · accept · multiple · maxSize(bytes) · maxFiles · capture | reject([{file,reason}]), remove(file)', '<apple-upload v-model="files" label="上传图片" accept="image/*" multiple :max-files="5" />'),
|
|
33
|
+
item('AppleOtpInput', 'v-model · length · label | complete', '<apple-otp-input v-model="code" :length="6" label="验证码" />'),
|
|
34
|
+
item('AppleCascader', 'v-model(value[]) · items[{label,value,children}] · label', '<apple-cascader v-model="region" label="所在地区" :items="regions" />'),
|
|
35
|
+
item('AppleRate', 'v-model · max · label · readonly · disabled', '<apple-rate v-model="rating" label="体验评分" />'),
|
|
36
|
+
item('AppleForm', 'validator(FormData) => boolean|string|Promise · loading · disabled | submit(FormData), invalid, reset | methods: validate(), submit(), reset()', '<apple-form :validator="validate" @submit="submit">\n <apple-input v-model="name" name="name" label="姓名" required />\n <apple-button type="submit">提交</apple-button>\n</apple-form>'),
|
|
37
|
+
item('AppleFormField', 'label · for · error · hint · required · disabled | default({id,disabled,required,aria-invalid,aria-describedby})', '<apple-form-field label="个人网站" :error="error">\n <template #default="field">\n <input v-bind="field" v-model="website" class="apple-control" type="url" />\n </template>\n</apple-form-field>'),
|
|
38
|
+
item('AppleNavibar', 'v-model · items · brand · brandHref · fixed · breakpoint=640 · hideOnPreview · label · motion | change(value,item), toggle(open) | slots: brand, item, actions', '<apple-navibar v-model="page" brand="Apptify" :items="pages" />'),
|
|
39
|
+
item('AppleAside', 'hideOnPreview=true · motion | slot: default | 布局由调用方提供', '<apple-aside :hide-on-preview="false">\n <apple-list :items="items" />\n</apple-aside>'),
|
|
40
|
+
item('AppleTabs', 'v-model · items[{label,value,disabled}] | slot: default({value})', '<apple-tabs v-model="tab" :items="tabs">\n <template #default="{ value }">{{ value }}</template>\n</apple-tabs>'),
|
|
41
|
+
item('AppleTabBar', 'v-model · items · label · disabled · motion | slot: default({value})', '<apple-tab-bar v-model="tab" :items="tabs">\n <template #default="{value}">{{ value }}</template>\n</apple-tab-bar>'),
|
|
42
|
+
item('AppleBreadcrumbs', 'items[{label,value,href,disabled}] · label', '<apple-breadcrumbs :items="[{label:\'首页\',value:\'home\',href:\'/\'},{label:\'设置\',value:\'settings\'}]" />'),
|
|
43
|
+
item('ApplePagination', 'v-model(page) · total · pageSize', '<apple-pagination v-model="page" :total="120" :page-size="10" />'),
|
|
44
|
+
item('AppleSteps', 'v-model(index) · items[{label,value,description,disabled}] · clickable · disabled', '<apple-steps v-model="step" :items="steps" clickable />'),
|
|
45
|
+
item('AppleAccordion', 'v-model · items[{label,value,content}] · multiple', '<apple-accordion :items="questions" />'),
|
|
46
|
+
item('AppleTable', 'columns[{key,label,sortable,width,minWidth,maxWidth}] · rows · rowKey · selectable · pageSize · virtual · height · rowHeight · overscan · resizable | update:selected, column-resize', '<apple-table :columns="columns" :rows="rows" row-key="id" virtual :height="360" :row-height="48" />'),
|
|
47
|
+
item('AppleTree', 'v-model · items[{label,value,children}] | select', '<apple-tree v-model="selected" :items="folders" />'),
|
|
48
|
+
item('AppleList', 'v-model · items[{label,value,description,href,disabled}] · selectable | select(item)', '<apple-list v-model="setting" :items="settings" selectable @select="openSetting" />'),
|
|
49
|
+
item('AppleTimeline', 'items[{label,value,description,time,tone}] · orientation(horizontal|vertical) · label', '<apple-timeline :items="events" orientation="horizontal" />'),
|
|
50
|
+
item('AppleStatistic', 'label · value · prefix · suffix · precision', '<apple-statistic label="本月访问" :value="12840" suffix="次" />'),
|
|
51
|
+
item('AppleDialog', 'v-model · title · persistent · confirmText · cancelText | confirm, cancel', '<apple-dialog v-model="open" title="保存更改?" @confirm="save">\n 你的更改会同步到所有设备。\n</apple-dialog>'),
|
|
52
|
+
item('AppleDrawer', 'v-model · title · placement(left|right|bottom) · width · persistent · loading', '<apple-drawer v-model="open" title="偏好设置" placement="right">内容</apple-drawer>'),
|
|
53
|
+
item('AppleSheet', 'v-model · title · persistent', '<apple-sheet v-model="open" title="分享">内容</apple-sheet>'),
|
|
54
|
+
item('AppleSnackbar', 'v-model · message · title · tone · duration(ms) · action · closable | close, action', "const notice = apple.notify('已保存', { tone: 'success', duration: 4000 })"),
|
|
55
|
+
item('AppleAlert', 'v-model · title · message · tone · closable | close', '<apple-alert v-model="visible" title="一切已就绪" tone="success" closable>内容已同步。</apple-alert>'),
|
|
56
|
+
item('AppleProgress', 'modelValue · max · label · indeterminate · showValue · tone', '<apple-progress :model-value="68" label="正在上传" show-value />'),
|
|
57
|
+
item('AppleSpinner', 'size · label', '<apple-spinner label="正在加载" />'),
|
|
58
|
+
item('AppleSkeleton', 'variant(text|avatar|card|list|table|image) · lines · rows · columns · avatar · width · height · animated · label · motion', '<apple-skeleton variant="list" :rows="3" avatar />'),
|
|
59
|
+
item('AppleEmpty', 'title · description | slot: default', '<apple-empty title="还没有收藏" description="喜欢的内容,会出现在这里。" />'),
|
|
60
|
+
item('ApplePopover', 'v-model · label · placement · align · width · disabled | activator({props,open,close,isOpen}), default({close})', '<apple-popover label="更多选项">\n <template #activator><apple-button variant="secondary">更多</apple-button></template>\n <apple-switch v-model="sync" label="自动同步" />\n</apple-popover>'),
|
|
61
|
+
item('AppleTooltip', 'text | slot: default', '<apple-tooltip text="添加到收藏"><apple-button>收藏</apple-button></apple-tooltip>'),
|
|
62
|
+
item('AppleMenu', 'v-model · items[{label,value,disabled,danger,icon,description}] · label · selected | select(value,item)', '<apple-menu :items="actions" label="更多操作" @select="onAction" />'),
|
|
63
|
+
item('AppleActionSheet', 'v-model · title · message · items[{label,value,disabled,danger}] · cancelText | default slot {select(item),close(value)} | select(value,item)', '<apple-action-sheet v-model="open" title="选择操作" @select="onAction">\n <template #default="{ select }">\n <apple-button @click="select({ label: \'收藏\', value: \'favorite\' })">加入收藏</apple-button>\n </template>\n</apple-action-sheet>'),
|
|
64
|
+
item('ApplePullRefresh', 'v-model(refreshing) · threshold · label · disabled | refresh(done)', '<apple-pull-refresh @refresh="async done => { await reload(); done() }">内容</apple-pull-refresh>'),
|
|
65
|
+
item('AppleBackTop', 'threshold · label', '<apple-back-top :threshold="400" />'),
|
|
66
|
+
item('AppleFloatingGroup', 'backTop · threshold · target · label · motion | default', '<apple-floating-group :threshold="300" />'),
|
|
67
|
+
item('AppleSpeedDial', 'backTop · threshold · target · label', '<!-- App.vue -->\n<apple-speed-dial />'),
|
|
68
|
+
item('AppleSpeedDialItem', 'label · icon · visible · disabled · variant | click', '<apple-speed-dial-item label="新增" :icon="Plus" @click="create" />'),
|
|
69
|
+
item('AppleAutoSize', 'axis(height|both) · motion | default', '<apple-auto-size><div v-if="expanded">详情内容</div></apple-auto-size>'),
|
|
70
|
+
item('AppleTransition', 'name(page|slide-x|slide-y|fade) · mode · appear · motion | default', '<apple-transition name="page"><router-view /></apple-transition>'),
|
|
71
|
+
item('AppleInfiniteScroll', 'loading · finished · error · disabled · distance | load(done), retry(done)', '<apple-infinite-scroll :finished="finished" @load="async done => { await loadMore(); done() }">内容</apple-infinite-scroll>'),
|
|
72
|
+
item('AppleScrollBar', 'target · axis(both|horizontal|vertical) · label | interaction · refresh()', '<apple-scroll-bar target="#scroll-area" />'),
|
|
68
73
|
]
|
|
69
74
|
export const groups = ['全部组件', '基础', '表单', '导航', '数据展示', '反馈', '移动交互']
|
|
70
75
|
|
|
@@ -1,43 +1,39 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<nav class="component-index" aria-label="组件定位">
|
|
3
|
-
<AppleTree :items="treeItems" :model-value="activeId || activeGroup" :expanded="
|
|
4
|
-
<p v-if="!treeItems.length" class="index-empty" role="status">未找到组件,请尝试其他关键词。</p>
|
|
3
|
+
<AppleTree :items="treeItems" :model-value="activeId || activeGroup" :expanded="expanded" label="组件目录树" @update:expanded="expanded = $event" @select="navigate" />
|
|
5
4
|
</nav>
|
|
6
5
|
</template>
|
|
7
6
|
|
|
8
7
|
<script setup lang="ts">
|
|
9
|
-
import { computed } from 'vue'
|
|
10
8
|
import { useRoute, useRouter } from 'vue-router'
|
|
9
|
+
import { scrollToWithMotion } from 'apptify'
|
|
11
10
|
import { AppleTree } from 'apptify'
|
|
12
11
|
import type { AppleTreeItem, AppleValue } from 'apptify'
|
|
13
12
|
import { sections, componentId } from '../catalog'
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
defineProps<{ activeGroup: string; activeId: string }>()
|
|
16
15
|
const emit = defineEmits<{ navigate: [] }>()
|
|
17
16
|
const router = useRouter()
|
|
18
17
|
const route = useRoute()
|
|
19
18
|
const expanded = defineModel<AppleValue[]>('expanded', { default: () => sections.map(section => section.id) })
|
|
20
|
-
const
|
|
21
|
-
const treeItems = computed<AppleTreeItem[]>(() => sections.flatMap(section => {
|
|
22
|
-
const groupMatches = section.label.toLocaleLowerCase().includes(query.value)
|
|
23
|
-
const items = section.items.filter(item => groupMatches || `${item.label} ${item.name} ${componentId(item.name)} ${item.description}`.toLocaleLowerCase().includes(query.value))
|
|
24
|
-
return items.length ? [{ label: section.label, value: section.id, children: items.map(item => ({ label: item.label, value: componentId(item.name) })) }] : []
|
|
25
|
-
}))
|
|
26
|
-
const expandedGroups = computed(() => query.value ? treeItems.value.map(item => item.value) : expanded.value)
|
|
19
|
+
const treeItems: AppleTreeItem[] = sections.map(section => ({ label: section.label, value: section.id, children: section.items.map(item => ({ label: item.label, value: componentId(item.name), description: `${item.name} ${item.description}` })) }))
|
|
27
20
|
function navigate(item: AppleTreeItem) {
|
|
28
21
|
if (item.children?.length) return
|
|
29
22
|
const hash = `#${item.value}`
|
|
30
23
|
if (route.hash === hash) {
|
|
31
24
|
const target = document.getElementById(String(item.value))
|
|
32
|
-
if (target)
|
|
25
|
+
if (target) {
|
|
26
|
+
const motion = document.querySelector('#app > .apple-provider')?.getAttribute('data-apple-motion')
|
|
27
|
+
const top = () => Math.min(target.getBoundingClientRect().top + window.scrollY - 96, Math.max(0, document.documentElement.scrollHeight - window.innerHeight))
|
|
28
|
+
scrollToWithMotion(window, top, motion === 'full')
|
|
29
|
+
}
|
|
33
30
|
} else void router.push(`/components${hash}`)
|
|
34
31
|
emit('navigate')
|
|
35
32
|
}
|
|
36
33
|
</script>
|
|
37
34
|
|
|
38
35
|
<style scoped>
|
|
39
|
-
.component-index { min-width: 0; }
|
|
36
|
+
.component-index { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
40
37
|
.component-index :deep(.apple-tree__row) { min-height: 34px; }
|
|
41
38
|
.component-index :deep(.apple-tree__row > span:last-child) { min-width: 0; overflow-wrap: anywhere; }
|
|
42
|
-
.index-empty { margin: 12px 8px; font-size: 12px; line-height: 1.7; color: var(--apple-secondary); }
|
|
43
39
|
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="root" class="masonry-feed"><slot /></div>
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
7
|
+
|
|
8
|
+
const emit = defineEmits<{ layout: [] }>()
|
|
9
|
+
const root = ref<HTMLElement>()
|
|
10
|
+
let observer: ResizeObserver | undefined
|
|
11
|
+
let frame = 0
|
|
12
|
+
const gap = 20
|
|
13
|
+
|
|
14
|
+
function layout() {
|
|
15
|
+
frame = 0
|
|
16
|
+
const container = root.value
|
|
17
|
+
if (!container || !container.clientWidth) return
|
|
18
|
+
const cards = Array.from(container.children) as HTMLElement[]
|
|
19
|
+
const columns = Math.max(1, Math.floor((container.clientWidth + gap) / (400 + gap)))
|
|
20
|
+
const width = (container.clientWidth - gap * (columns - 1)) / columns
|
|
21
|
+
const heights = Array<number>(columns).fill(0)
|
|
22
|
+
for (const card of cards) card.style.width = `${width}px`
|
|
23
|
+
for (const card of cards) {
|
|
24
|
+
const column = heights.indexOf(Math.min(...heights))
|
|
25
|
+
card.style.position = 'absolute'
|
|
26
|
+
card.style.left = `${column * (width + gap)}px`
|
|
27
|
+
card.style.top = `${heights[column]}px`
|
|
28
|
+
heights[column] += card.offsetHeight + gap
|
|
29
|
+
}
|
|
30
|
+
container.style.height = `${Math.max(...heights)}px`
|
|
31
|
+
emit('layout')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function scheduleLayout() {
|
|
35
|
+
if (!frame) frame = requestAnimationFrame(layout)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onMounted(() => {
|
|
39
|
+
observer = new ResizeObserver(scheduleLayout)
|
|
40
|
+
observer.observe(root.value!)
|
|
41
|
+
for (const card of root.value!.children) observer.observe(card)
|
|
42
|
+
layout()
|
|
43
|
+
})
|
|
44
|
+
onBeforeUnmount(() => {
|
|
45
|
+
observer?.disconnect()
|
|
46
|
+
cancelAnimationFrame(frame)
|
|
47
|
+
})
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<style scoped>
|
|
51
|
+
.masonry-feed { position: relative; }
|
|
52
|
+
</style>
|
|
@@ -101,7 +101,7 @@ function showProfile() {
|
|
|
101
101
|
.user-popup.apple-popover { border-radius: 24px; background: var(--apple-surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
|
|
102
102
|
.user-popup .user-menu-list > .apple-link > .apple-link__content { width: 100%; justify-content: flex-start; gap: 11px; }
|
|
103
103
|
.user-popup .user-menu-list > .apple-link { min-height: 44px; font-weight: 400; line-height: inherit; }
|
|
104
|
-
.user-popup .user-menu-list > .apple-link:hover:
|
|
104
|
+
.user-popup .user-menu-list > .apple-link:hover:not(:disabled) { background: var(--apple-hover); }
|
|
105
105
|
.user-popup .user-menu-list > .apple-link:active:not(:disabled) { background: var(--apple-pressed); }
|
|
106
106
|
.user-trigger.user-account-trigger { width: 44px; height: 44px; border: 0; background: transparent; }
|
|
107
107
|
.user-trigger.user-account-trigger:focus-visible { outline: 3px solid var(--apple-accent); outline-offset: 2px; }
|