apptify 0.2.0 → 0.5.1
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 +40 -239
- package/dist/apptify.css +1 -1
- package/dist/apptify.js +4153 -2177
- package/dist/components/button.d.ts +1 -1
- package/dist/components/content.d.ts +73 -362
- package/dist/components/date-picker.d.ts +10 -0
- package/dist/components/forms.d.ts +77 -18
- package/dist/components/foundation.d.ts +2061 -111
- package/dist/components/gallery-scrollbar.d.ts +46 -0
- package/dist/components/mobile-image-viewer.d.ts +166 -0
- package/dist/components/navibar.d.ts +80 -0
- package/dist/components/overlays.d.ts +127 -158
- package/dist/components/tabs.d.ts +117 -0
- package/dist/core/context.d.ts +573 -9
- package/dist/core/desktop-image-enter.d.ts +6 -0
- package/dist/core/desktop-image-return.d.ts +1 -0
- package/dist/core/device.d.ts +5 -0
- package/dist/core/image-gallery-motion.d.ts +2 -0
- package/dist/core/image-geometry.d.ts +28 -0
- package/dist/core/image-motion.d.ts +18 -0
- package/dist/core/layers.d.ts +2 -0
- package/dist/core/motion.d.ts +2 -0
- package/dist/core/popup-motion.d.ts +12 -0
- package/dist/core/popup-placement.d.ts +31 -0
- package/dist/core/preview-navigation.d.ts +5 -0
- package/dist/index.d.ts +1214 -339
- package/docs/COMPONENTS.md +18 -16
- package/docs/PLAYGROUND-TEMPLATE.md +51 -0
- package/docs/VALIDATION.md +25 -1
- package/package.json +35 -7
- package/templates/playground/README.md +21 -0
- package/templates/playground/gitignore +3 -0
- package/templates/playground/index.html +26 -0
- package/templates/playground/package.json +26 -0
- package/templates/playground/playground/App.vue +73 -0
- package/templates/playground/playground/ComponentDemo.vue +234 -0
- package/templates/playground/playground/catalog.ts +80 -0
- package/templates/playground/playground/components/ComponentIndex.vue +62 -0
- package/templates/playground/playground/components/PageFooter.vue +3 -0
- package/templates/playground/playground/components/UserMenu.vue +132 -0
- package/templates/playground/playground/components/not-found-paper-scene.vue +210 -0
- package/templates/playground/playground/components/not-found-paper-story.ts +149 -0
- package/templates/playground/playground/env.d.ts +1 -0
- package/templates/playground/playground/main.ts +8 -0
- package/templates/playground/playground/router/index.ts +24 -0
- package/templates/playground/playground/style.css +228 -0
- package/templates/playground/playground/views/[...all].vue +68 -0
- package/templates/playground/playground/views/components.vue +126 -0
- package/templates/playground/playground/views/index.vue +75 -0
- package/templates/playground/playground/views/settings.vue +98 -0
- package/templates/playground/public/images/SOURCES.md +21 -0
- package/templates/playground/public/images/airpods-max-orange.jpg +0 -0
- package/templates/playground/public/images/architecture.jpg +0 -0
- package/templates/playground/public/images/city.jpg +0 -0
- package/templates/playground/public/images/coast.jpg +0 -0
- package/templates/playground/public/images/desert.jpg +0 -0
- package/templates/playground/public/images/food.jpg +0 -0
- package/templates/playground/public/images/forest.jpg +0 -0
- package/templates/playground/public/images/lake.jpg +0 -0
- package/templates/playground/public/images/mountains.jpg +0 -0
- package/templates/playground/public/images/snow.jpg +0 -0
- package/templates/playground/public/images/sunset.jpg +0 -0
- package/templates/playground/public/images/waterfall.jpg +0 -0
- package/templates/playground/source-snapshot.json +111 -0
- package/templates/playground/tsconfig.json +24 -0
- package/templates/playground/vite.config.ts +9 -0
- package/tooling/create-playground.mjs +35 -0
- package/tooling/prepare-package.mjs +35 -0
- package/tooling/prepare-playground.mjs +103 -0
- package/tooling/test-create-apptify.mjs +76 -0
- package/tooling/test-playground-package.mjs +188 -0
- package/tooling/vite.d.ts +5 -0
- package/tooling/vite.js +10 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
html, body { background-color: var(--app-page-bg, #f5f5f7); }
|
|
2
|
+
body { margin: 0; }
|
|
3
|
+
button, a, input { -webkit-tap-highlight-color: transparent; }
|
|
4
|
+
button { font: inherit; }
|
|
5
|
+
#app > .apple-provider { min-height: 100dvh; }
|
|
6
|
+
.apple-auto-size__inner { position: relative; }
|
|
7
|
+
.route-page { display: contents; }
|
|
8
|
+
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--apple-text); text-decoration: none; font-weight: 650; font-size: 20px; }
|
|
9
|
+
.brand-symbol { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; background: var(--apple-text); color: var(--apple-surface); font-weight: 600; font-size: 24px; line-height: 1; padding-bottom: 4px; font-family: Georgia,serif; }
|
|
10
|
+
.user-trigger { width: 38px; height: 38px; border: 1px solid var(--apple-border); border-radius: 50%; display: grid; place-items: center; background: var(--apple-surface); color: var(--apple-text); transition: background var(--apple-fast); }
|
|
11
|
+
.user-trigger:hover:where(:not([data-apple-touch] *)),.user-trigger[aria-expanded=true] { background: var(--apple-selected); }
|
|
12
|
+
.user-popup.apple-popover { padding: 8px; }
|
|
13
|
+
.user-summary { display: flex; align-items: center; gap: 12px; padding: 14px 10px 20px; }
|
|
14
|
+
.user-summary > div { min-width: 0; }
|
|
15
|
+
.user-summary strong { display: flex; align-items: center; gap: 9px; font-size: 14px; font-weight: 600; }
|
|
16
|
+
.user-summary strong small { padding: 2px 5px; border-radius: 4px; background: var(--apple-surface-alt); font-size: 9px; font-weight: 400; color: var(--apple-secondary); }
|
|
17
|
+
.user-summary > div > span { display: block; color: var(--apple-secondary); font-size: 11px; margin-top: 4px; overflow-wrap: anywhere; }
|
|
18
|
+
.user-menu-list { border-top: 1px solid var(--apple-border); padding: 6px 0; }
|
|
19
|
+
.user-menu-list > :is(button,a) { display: flex; align-items: center; gap: 11px; width: 100%; padding: 12px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--apple-text); font-size: 13px; text-align: left; text-decoration: none; }
|
|
20
|
+
.user-menu-list > :is(button,a):hover:where(:not([data-apple-touch] *)) { background: var(--apple-hover); }
|
|
21
|
+
.user-menu-list > :is(button,a):active { background: var(--apple-pressed); }
|
|
22
|
+
.user-menu-list span { flex: 1; }
|
|
23
|
+
.user-menu-list svg,.user-menu-list small { color: var(--apple-secondary); }
|
|
24
|
+
.user-menu-list small { margin-left: 5px; font-size: 11px; }
|
|
25
|
+
.user-menu-list em { color: var(--apple-secondary); font-size: 10px; font-style: normal; }
|
|
26
|
+
.auth-hint { font-size: 13px; color: var(--apple-secondary); margin: 0 0 24px; }
|
|
27
|
+
.main-content { min-width: 0; width: 100%; max-width: 1280px; margin-inline: auto; padding: 48px 48px 0; min-height: calc(100dvh - 64px); }
|
|
28
|
+
.page-kicker { color: var(--apple-secondary); font-size: 10px; font-weight: 500; margin-bottom: 20px; }
|
|
29
|
+
.page-heading { margin-bottom: 32px; }
|
|
30
|
+
.page-heading h1 { font-size: 38px; line-height: 1.2; font-weight: 650; margin: 0; }
|
|
31
|
+
.page-heading h1 > span { color: var(--apple-secondary); }
|
|
32
|
+
.page-heading p { font-size: 13px; line-height: 1.7; color: var(--apple-secondary); margin: 14px 0 0; }
|
|
33
|
+
.home-intro { padding: 24px 0 58px; }
|
|
34
|
+
.home-intro h1 { font-size: clamp(38px, 4.3vw, 62px); font-weight: 650; line-height: 1.24; margin: 0; }
|
|
35
|
+
.home-intro h1 > span { color: var(--apple-secondary); }
|
|
36
|
+
.home-intro p { font-size: 14px; color: var(--apple-secondary); line-height: 1.9; margin: 22px 0 26px; }
|
|
37
|
+
.home-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 24px; }
|
|
38
|
+
.primary-link,.text-link { display: inline-flex; align-items: center; justify-content: center; gap: 9px; text-decoration: none; font-size: 13px; font-weight: 500; }
|
|
39
|
+
.primary-link { background: var(--apple-text); color: var(--apple-surface); padding: 12px 20px; min-height: 44px; border-radius: 24px; }
|
|
40
|
+
.primary-link:hover { opacity: .85; }
|
|
41
|
+
.text-link { color: var(--apple-text); min-height: 40px; }
|
|
42
|
+
.text-link:hover { color: var(--apple-accent); }
|
|
43
|
+
.mobile-break { display: none; }
|
|
44
|
+
.section-heading { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding-top: 26px; border-top: 1px solid var(--apple-border); margin-bottom: 22px; }
|
|
45
|
+
.section-heading h2 { font-size: 19px; font-weight: 600; margin: 0; }
|
|
46
|
+
.section-heading > span { font-size: 11px; color: var(--apple-secondary); }
|
|
47
|
+
.more-components { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 38px 0; }
|
|
48
|
+
.more-components h2 { margin: 0 0 8px; font-size: 19px; font-weight: 600; }
|
|
49
|
+
.more-components span { font-size: 12px; color: var(--apple-secondary); }
|
|
50
|
+
.page-footer { padding: 24px 0; border-top: 1px solid var(--apple-border); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; font-size: 11px; color: var(--apple-secondary); margin-top: 16px; }
|
|
51
|
+
.specimen-grid { display: grid; grid-template-columns: 1.04fr 1fr 1fr; gap: 18px; }
|
|
52
|
+
.specimen { min-width: 0; padding: 21px 24px 22px; border-radius: 8px; background: var(--apple-surface); box-shadow: 0 2px 10px rgb(0 0 0 / .025); position: relative; display: flex; flex-direction: column; }
|
|
53
|
+
.specimen-caption { display: flex; align-items: center; justify-content: space-between; color: var(--apple-secondary); font-size: 9px; margin-bottom: 22px; }
|
|
54
|
+
.specimen h2 { font-size: 21px; font-weight: 600; line-height: 1.3; margin: 0 0 25px; }
|
|
55
|
+
.button-specimens { display: flex; align-items: center; flex-direction: column; gap: 15px; padding: 9px 0 22px; }
|
|
56
|
+
.button-specimens > .apple-button { min-width: 144px; font-size: 13px; min-height: 40px; }
|
|
57
|
+
.icon-button-row { display: flex; gap: 12px; margin-top: 4px; }
|
|
58
|
+
.icon-button-row .apple-button { width: 38px; height: 38px; min-height: 38px; }
|
|
59
|
+
.specimen-foot { margin-top: auto; padding-top: 15px; display: flex; gap: 10px; justify-content: space-between; align-items: center; color: var(--apple-secondary); font-size: 10px; }
|
|
60
|
+
.specimen-foot code { font-size: 10px; }
|
|
61
|
+
.preference-demo { display: flex; flex-direction: column; gap: 14px; }
|
|
62
|
+
.preference-demo .apple-field__label { display: none; }
|
|
63
|
+
.preference-demo .apple-slider .apple-field__label { display: block; }
|
|
64
|
+
.setting-line { display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid color-mix(in srgb,var(--apple-border) 55%,transparent); padding: 3px 0 13px; }
|
|
65
|
+
.setting-line > div:first-child { min-width: 0; }
|
|
66
|
+
.setting-line strong { display: block; font-size: 12px; font-weight: 500; }
|
|
67
|
+
.setting-line small { display: block; font-size: 10px; color: var(--apple-secondary); margin-top: 4px; }
|
|
68
|
+
.setting-line .apple-field { flex-shrink: 0; }
|
|
69
|
+
.check-row { padding-top: 5px; }
|
|
70
|
+
.check-row .apple-choice__label { font-size: 11px; }
|
|
71
|
+
.form-specimen .apple-form { display: flex; flex-direction: column; gap: 13px; }
|
|
72
|
+
.form-specimen .apple-form .apple-field { margin-bottom: 10px; }
|
|
73
|
+
.form-specimen .apple-form .apple-button { margin-top: 9px; align-self: flex-start; font-size: 12px; min-height: 36px; }
|
|
74
|
+
.specimen .apple-field__label { font-size: 11px; }
|
|
75
|
+
.specimen input { font-size: 12px; }
|
|
76
|
+
.media-specimen .apple-image { flex: 1; min-height: 184px; }
|
|
77
|
+
.feedback-specimen .apple-alert { font-size: 11px; padding: 12px; }
|
|
78
|
+
.progress-demo { margin: 25px 0; }
|
|
79
|
+
.progress-demo > div:first-child { display: flex; justify-content: space-between; font-size: 11px; color: var(--apple-secondary); margin-bottom: 10px; }
|
|
80
|
+
.feedback-actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: 3px; padding-top: 25px; }
|
|
81
|
+
.feedback-actions .apple-button { font-size: 11px; }
|
|
82
|
+
|
|
83
|
+
.specimen > .apple-search { margin-bottom: 12px; }
|
|
84
|
+
.sidebar { width: 232px; position: fixed; top: 64px; bottom: 0; left: 0; z-index: 40; padding: 30px 14px 24px; border-right: 1px solid color-mix(in srgb,var(--apple-border) 55%,transparent); background: var(--apple-surface); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; }
|
|
85
|
+
.sidebar-heading { display: flex; flex-direction: column; gap: 7px; padding: 0 14px; margin-bottom: 24px; }
|
|
86
|
+
.sidebar-heading strong { font-size: 13px; font-weight: 600; }
|
|
87
|
+
.sidebar-heading > span { font-size: 10px; color: var(--apple-secondary); }
|
|
88
|
+
.component-index a { text-decoration: none; color: var(--apple-secondary); font-size: 12px; }
|
|
89
|
+
.index-group { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: 6px; }
|
|
90
|
+
.index-group span { flex: 1; }
|
|
91
|
+
.index-group small { font-size: 10px; font-variant-numeric: tabular-nums; color: var(--apple-secondary); }
|
|
92
|
+
.index-group.active { color: var(--apple-text); font-weight: 600; }
|
|
93
|
+
.index-items { margin: 3px 0 12px 21px; padding-left: 12px; border-left: 1px solid var(--apple-border); }
|
|
94
|
+
.index-items a { display: block; border-radius: 5px; padding: 7px 10px; }
|
|
95
|
+
.component-index a:hover:where(:not([data-apple-touch] *)) { color: var(--apple-text); background: var(--apple-hover); }
|
|
96
|
+
.index-items a.active { background: var(--apple-selected); color: var(--apple-text); font-weight: 500; }
|
|
97
|
+
.components-page { margin-left: 232px; width: calc(100% - 232px); max-width: none; padding: 42px 48px 0; }
|
|
98
|
+
.components-page > * { max-width: 1000px; margin-inline: auto; }
|
|
99
|
+
.feed-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding-bottom: 14px; border-bottom: 1px solid var(--apple-border); }
|
|
100
|
+
.feed-count { font-size: 12px; font-weight: 500; }
|
|
101
|
+
.feed-count small { margin-left: 7px; color: var(--apple-secondary); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
102
|
+
.preview-controls { display: flex; gap: 4px; }
|
|
103
|
+
.mobile-index-toggle { display: none; }
|
|
104
|
+
.feed-group { padding-bottom: 16px; }
|
|
105
|
+
.feed-group-heading { display: flex; align-items: baseline; gap: 12px; padding: 32px 0 20px; }
|
|
106
|
+
.feed-group-heading > span { color: var(--apple-secondary); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
107
|
+
.feed-group-heading h2 { margin: 0; font-size: 20px; font-weight: 600; }
|
|
108
|
+
.feed-group-heading small { margin-left: auto; font-size: 11px; color: var(--apple-secondary); }
|
|
109
|
+
.component-card { min-width: 0; background: var(--apple-surface); border: 1px solid color-mix(in srgb,var(--apple-border) 40%,transparent); border-radius: 10px; margin-bottom: 20px; }
|
|
110
|
+
.component-card-heading { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px; padding: 24px 28px 0; }
|
|
111
|
+
.component-card-heading h3 { font-size: 16px; font-weight: 600; margin: 0; }
|
|
112
|
+
.component-card-heading h3 a { display: inline-flex; align-items: center; gap: 8px; color: inherit; text-decoration: none; }
|
|
113
|
+
.component-card-heading h3 svg { color: var(--apple-secondary); opacity: 0; }
|
|
114
|
+
.component-card-heading h3 a:hover svg,.component-card-heading h3 a:focus-visible svg { opacity: 1; }
|
|
115
|
+
.component-card-heading p { font-size: 12px; color: var(--apple-secondary); margin: 6px 0 0; }
|
|
116
|
+
.component-card-heading > code { font-size: 10px; color: var(--apple-secondary); padding-top: 3px; overflow-wrap: anywhere; }
|
|
117
|
+
.detail-preview { min-width: 0; padding: 34px 28px; min-height: 186px; display: flex; align-items: center; justify-content: center; width: 100%; }
|
|
118
|
+
.detail-preview .component-demo { width: 100%; max-width: 720px; min-width: 0; margin-inline: auto; }
|
|
119
|
+
.detail-preview .component-demo--compact { max-width: 420px; }
|
|
120
|
+
.detail-preview .component-demo--media { max-width: 560px; }
|
|
121
|
+
.detail-preview.mobile-preview { max-width: 390px; margin-inline: auto; }
|
|
122
|
+
.component-card > .component-source { border-radius: 0 0 9px 9px; }
|
|
123
|
+
.component-source { border-top: 1px solid color-mix(in srgb,var(--apple-border) 45%,transparent); }
|
|
124
|
+
.code-view { padding: 4px 28px 20px; min-width: 0; }
|
|
125
|
+
.code-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; color: var(--apple-secondary); font-size: 12px; }
|
|
126
|
+
.code-view h4 { font-size: 13px; margin: 22px 0 0; }
|
|
127
|
+
pre { padding: 18px; overflow: auto; background: var(--apple-surface-alt); border-radius: 6px; line-height: 1.8; font-size: 12px; tab-size: 2; }
|
|
128
|
+
.api-line { overflow-wrap: anywhere; font-size: 12px; line-height: 1.9; color: var(--apple-secondary); }
|
|
129
|
+
.component-demo > .apple-stack > .apple-button,
|
|
130
|
+
.component-demo .apple-form__fields > .apple-button,
|
|
131
|
+
.component-demo > .apple-stack > .apple-provider > .apple-stack > .apple-button { align-self: flex-start; }
|
|
132
|
+
.component-demo .apple-form__fields { gap: 20px; }
|
|
133
|
+
.component-demo .apple-form__fields > .apple-button { justify-self: start; }
|
|
134
|
+
.component-demo .apple-grid { align-items: start; }
|
|
135
|
+
.demo-layout-tile { display: flex; flex-direction: column; justify-content: center; gap: 12px; min-height: 112px; padding: 20px; border: 1px solid var(--apple-border); border-radius: 10px; background: var(--apple-surface-alt); }
|
|
136
|
+
.demo-layout-tile > span { color: var(--apple-secondary); font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
137
|
+
.demo-layout-tile > strong { font-size: 16px; font-weight: 550; }
|
|
138
|
+
.component-demo > .apple-stack > .apple-provider { border-radius: 10px; }
|
|
139
|
+
.component-demo[data-component="AppleSpinner"] .apple-spinner { align-self: flex-start; }
|
|
140
|
+
.component-demo[data-component="AppleDivider"] .apple-divider:not(.apple-divider--vertical) { width: 100%; }
|
|
141
|
+
.component-demo[data-component="AppleSkeleton"] .apple-skeleton { width: 100%; }
|
|
142
|
+
.component-demo[data-component="ApplePopover"] p { margin: 0 0 16px; }
|
|
143
|
+
.component-demo > .apple-field { max-width: 420px; }
|
|
144
|
+
.component-demo > .apple-form { max-width: 420px; }
|
|
145
|
+
.component-demo .apple-form .apple-field { margin-bottom: 0; }
|
|
146
|
+
.component-demo > .apple-image { max-width: 560px; }
|
|
147
|
+
.demo-image { max-width: 560px; }
|
|
148
|
+
.demo-product { max-width: 330px; margin-inline: auto; }
|
|
149
|
+
.demo-product .apple-image { margin-top: 10px; }
|
|
150
|
+
.demo-copy { margin: 0; padding: 8px 0; color: var(--apple-secondary); }
|
|
151
|
+
.demo-slide { display: block; width: 100%; aspect-ratio: 16/10; object-fit: cover; }
|
|
152
|
+
|
|
153
|
+
.demo-floating-preview { padding: 24px; border-radius: 8px; }
|
|
154
|
+
.demo-floating-preview .apple-floating-group { position: relative; inset: auto; flex-direction: row; justify-content: center; padding: 12px 0; z-index: auto; }
|
|
155
|
+
.settings-page { max-width: 980px; }
|
|
156
|
+
.settings-section { background: var(--apple-surface); border: 1px solid color-mix(in srgb,var(--apple-border) 40%,transparent); border-radius: 12px; padding: 28px; margin-bottom: 22px; }
|
|
157
|
+
.settings-section-heading { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 26px; }
|
|
158
|
+
.settings-section-heading > svg { margin-top: 3px; color: var(--apple-secondary); }
|
|
159
|
+
.settings-section h2 { font-size: 19px; font-weight: 600; margin: 0; }
|
|
160
|
+
.settings-section-heading p { font-size: 12px; line-height: 1.7; color: var(--apple-secondary); margin: 6px 0 0; }
|
|
161
|
+
.theme-options { display: grid; grid-template-columns: repeat(5,minmax(0,1fr)); gap: 12px; }
|
|
162
|
+
.theme-options button { display: flex; flex-direction: column; gap: 12px; min-width: 0; border: 1px solid var(--apple-border); border-radius: 8px; padding: 7px; background: transparent; color: var(--apple-text); }
|
|
163
|
+
.theme-options button.selected { border-color: var(--apple-accent); outline: 1px solid var(--apple-accent); }
|
|
164
|
+
.theme-swatch { display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: 7px; width: 100%; height: 78px; border-radius: 5px; padding: 16px; }
|
|
165
|
+
.theme-swatch i { display: block; width: 58%; height: 5px; border-radius: 3px; background: currentColor; opacity: .4; }
|
|
166
|
+
.theme-swatch i:first-child { width: 80%; opacity: .85; }
|
|
167
|
+
.theme-swatch i:last-child { width: 35%; }
|
|
168
|
+
.theme-label { display: flex; align-items: center; justify-content: space-between; gap: 4px; width: 100%; min-height: 20px; font-size: 12px; padding: 0 2px 3px; }
|
|
169
|
+
.theme-label svg { color: var(--apple-accent); flex-shrink: 0; }
|
|
170
|
+
.settings-section > .apple-segmented { max-width: 440px; }
|
|
171
|
+
.settings-note { color: var(--apple-secondary); font-size: 12px; line-height: 1.8; min-height: 22px; margin: 14px 0 24px; }
|
|
172
|
+
.motion-preview { background: var(--apple-surface-alt); border-radius: 8px; padding: 14px 20px 22px; }
|
|
173
|
+
.motion-preview-heading { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 12px; color: var(--apple-secondary); margin-bottom: 14px; }
|
|
174
|
+
.motion-preview-content p { margin: 18px 0 0; font-size: 13px; color: var(--apple-secondary); line-height: 1.8; }
|
|
175
|
+
.settings-reset { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--apple-secondary); font-size: 12px; margin: 26px 0 48px; }
|
|
176
|
+
.not-found-page { max-width: 740px; }
|
|
177
|
+
@media (min-width: 1600px) { .main-content.home-page { max-width: 1420px; } .specimen-grid { gap: 24px; } .specimen { padding: 26px 30px; } }
|
|
178
|
+
@media (max-width: 1100px) { .main-content { padding-inline: 28px; } .components-page { padding-inline: 32px; } .sidebar { width: 210px; } .components-page { margin-left: 210px; width: calc(100% - 210px); } .specimen-grid { grid-template-columns: 1fr 1fr; } }
|
|
179
|
+
@media (max-width: 800px) {
|
|
180
|
+
.sidebar { display: none; }
|
|
181
|
+
.components-page { margin-left: 0; width: 100%; }
|
|
182
|
+
.mobile-index-toggle { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0; border: 0; background: transparent; color: var(--apple-text); font-size: 12px; }
|
|
183
|
+
.feed-count { display: none; }
|
|
184
|
+
.theme-options { gap: 8px; }
|
|
185
|
+
}
|
|
186
|
+
@media (max-width: 640px) {
|
|
187
|
+
.site-nav.is-collapsed .apple-navibar__bar { grid-template-columns: minmax(0,1fr) 40px 38px; gap: 6px; padding-inline: 18px; }
|
|
188
|
+
.site-nav .apple-navibar__brand { order: 1; }
|
|
189
|
+
.site-nav .apple-navibar__toggle { order: 2; width: 40px; }
|
|
190
|
+
.site-nav .apple-navibar__actions { order: 3; }
|
|
191
|
+
.site-nav .apple-navibar__menu { order: 4; }
|
|
192
|
+
.brand { font-size: 19px; }
|
|
193
|
+
.brand-symbol { width: 26px; height: 26px; }
|
|
194
|
+
.main-content { padding: 30px 18px 0; min-height: calc(100dvh - 58px); }
|
|
195
|
+
.home-intro { padding: 16px 0 36px; }
|
|
196
|
+
.home-intro p { font-size: 13px; margin: 20px 0; }
|
|
197
|
+
.home-actions { gap: 12px 20px; }
|
|
198
|
+
.mobile-break { display: block; }
|
|
199
|
+
.section-heading { align-items: flex-start; flex-direction: column; gap: 7px; padding-top: 22px; }
|
|
200
|
+
.specimen-grid { grid-template-columns: 1fr; gap: 16px; }
|
|
201
|
+
.specimen { padding: 24px; }
|
|
202
|
+
.specimen h2 { font-size: 23px; }
|
|
203
|
+
.specimen-caption { font-size: 10px; }
|
|
204
|
+
.button-specimens > .apple-button { min-height: 44px; }
|
|
205
|
+
.icon-button-row .apple-button { width: 44px; height: 44px; }
|
|
206
|
+
.setting-line strong { font-size: 14px; }
|
|
207
|
+
.setting-line small { font-size: 11px; }
|
|
208
|
+
.check-row .apple-choice__label { font-size: 13px; }
|
|
209
|
+
.specimen .apple-field__label { font-size: 12px; }
|
|
210
|
+
.specimen input { font-size: 16px; }
|
|
211
|
+
.more-components { align-items: flex-start; flex-direction: column; gap: 10px; padding-block: 28px; }
|
|
212
|
+
.page-heading h1 { font-size: 32px; }
|
|
213
|
+
.page-heading p { font-size: 12px; }
|
|
214
|
+
.page-kicker { margin-bottom: 16px; }
|
|
215
|
+
.feed-group-heading { padding-top: 26px; }
|
|
216
|
+
.component-card-heading { padding: 20px 18px 0; gap: 8px; }
|
|
217
|
+
.component-card-heading > code { font-size: 9px; }
|
|
218
|
+
.detail-preview { padding: 28px 16px; }
|
|
219
|
+
.code-view { padding-inline: 16px; }
|
|
220
|
+
pre { padding: 14px; font-size: 11px; }
|
|
221
|
+
.settings-section { padding: 22px 18px; }
|
|
222
|
+
.theme-options { grid-template-columns: repeat(3,minmax(0,1fr)); gap: 12px; }
|
|
223
|
+
.theme-swatch { height: 68px; padding: 12px; }
|
|
224
|
+
.theme-label { font-size: 11px; }
|
|
225
|
+
.settings-section-heading p { font-size: 11px; }
|
|
226
|
+
.motion-preview { padding-inline: 14px; }
|
|
227
|
+
.settings-reset { font-size: 11px; }
|
|
228
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<main class="main-content not-found-page paper-404-page">
|
|
3
|
+
<apple-card class="paper-404-card" shadow="none" zoom="none">
|
|
4
|
+
<template #title>
|
|
5
|
+
<div class="paper-404-heading">
|
|
6
|
+
<p class="paper-404-number" aria-hidden="true">404</p>
|
|
7
|
+
<h1>页面不存在</h1>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
<p class="paper-404-copy">有些东西,悄悄溜走了。<br />就像松鼠的橡果,这一页也不知漂去了哪里。</p>
|
|
11
|
+
<div class="paper-404-actions">
|
|
12
|
+
<router-link v-slot="{ href, navigate }" :to="homePath" custom>
|
|
13
|
+
<apple-button :href="href" @click="navigate">返回首页</apple-button>
|
|
14
|
+
</router-link>
|
|
15
|
+
<apple-button variant="ghost" @click="goBack"><ArrowLeft :size="16" aria-hidden="true" /> 返回上一页</apple-button>
|
|
16
|
+
</div>
|
|
17
|
+
</apple-card>
|
|
18
|
+
<NotFoundPaperScene class="paper-404-illustration" />
|
|
19
|
+
</main>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { ArrowLeft } from 'lucide-vue-next'
|
|
24
|
+
import { useRouter } from 'vue-router'
|
|
25
|
+
import NotFoundPaperScene from '../components/not-found-paper-scene.vue'
|
|
26
|
+
|
|
27
|
+
const router = useRouter()
|
|
28
|
+
const homePath = router.getRoutes().some(page => page.path === '/') ? '/' : '/settings'
|
|
29
|
+
function goBack() {
|
|
30
|
+
// A direct visit has no in-app predecessor; keep its escape route useful.
|
|
31
|
+
if (typeof router.options.history.state.back === 'string') router.back()
|
|
32
|
+
else void router.push(homePath)
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<style scoped>
|
|
37
|
+
.paper-404-page { max-width: 1360px; display: grid; grid-template-columns: minmax(260px, .78fr) minmax(0, 1.65fr); align-content: center; align-items: center; column-gap: 16px; padding-top: 38px; padding-bottom: calc(38px + 64px); }
|
|
38
|
+
.paper-404-card { background: transparent; border: 0; overflow: visible; }
|
|
39
|
+
.paper-404-card :deep(.apple-card__body) { padding: 0 0 0 8px; }
|
|
40
|
+
.paper-404-card :deep(.apple-card__heading) { margin: 0; }
|
|
41
|
+
.paper-404-heading { width: 100%; }
|
|
42
|
+
.paper-404-kicker { color: var(--apple-secondary); font-size: 10px; letter-spacing: .1em; margin: 0 0 40px; }
|
|
43
|
+
.paper-404-number { display: flex; align-items: center; gap: 18px; color: var(--apple-text); font-family: Georgia, 'Times New Roman', serif; font-size: clamp(78px, 8.2vw, 118px); line-height: .9; letter-spacing: -.075em; margin: 0 0 28px; font-weight: 400; }
|
|
44
|
+
.paper-404-number span { writing-mode: vertical-rl; font-family: -apple-system, BlinkMacSystemFont, sans-serif; font-size: 10px; letter-spacing: .22em; color: var(--apple-secondary); padding-top: 8px; }
|
|
45
|
+
.paper-404-heading h1 { font-size: clamp(26px, 2.7vw, 35px); line-height: 1.35; letter-spacing: -.035em; font-weight: 600; margin: 0 0 17px; }
|
|
46
|
+
.paper-404-copy { font-size: 13px; line-height: 1.95; color: var(--apple-secondary); margin: 0; text-wrap: pretty; }
|
|
47
|
+
.paper-404-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 27px; }
|
|
48
|
+
.paper-404-actions .apple-button--ghost { font-size: 12px; padding-inline: 8px; color: var(--apple-secondary); }
|
|
49
|
+
.paper-404-illustration { min-width: 0; }
|
|
50
|
+
.paper-404-footnote { grid-column: 1 / -1; text-align: center; font-size: 10px; letter-spacing: .09em; color: var(--apple-secondary); margin: 48px 0 0; }
|
|
51
|
+
@media (max-width: 880px) {
|
|
52
|
+
.paper-404-page { grid-template-columns: minmax(0, 1fr); max-width: 680px; padding-top: 18px; }
|
|
53
|
+
.paper-404-illustration { grid-row: 1; }
|
|
54
|
+
.paper-404-card { margin-top: 26px; }
|
|
55
|
+
.paper-404-card :deep(.apple-card__body) { padding: 0; text-align: center; }
|
|
56
|
+
.paper-404-kicker { display: none; }
|
|
57
|
+
.paper-404-number { justify-content: center; font-size: 70px; margin-bottom: 18px; }
|
|
58
|
+
.paper-404-number span { font-size: 9px; }
|
|
59
|
+
.paper-404-heading h1 { font-size: 26px; margin-bottom: 12px; }
|
|
60
|
+
.paper-404-actions { justify-content: center; margin-top: 18px; }
|
|
61
|
+
.paper-404-footnote { margin-top: 32px; }
|
|
62
|
+
}
|
|
63
|
+
@media (max-width: 420px) {
|
|
64
|
+
.paper-404-page { padding-inline: 18px; padding-top: 12px; }
|
|
65
|
+
.paper-404-copy { font-size: 12px; }
|
|
66
|
+
.paper-404-card { margin-top: 22px; }
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<apple-aside class="sidebar" aria-label="组件目录">
|
|
3
|
+
<div class="sidebar-heading"><strong>组件目录</strong><span>点击定位,直接预览</span></div>
|
|
4
|
+
<div class="directory-search"><apple-search v-model="directorySearch" label="搜索组件目录" placeholder="名称、标签或描述" /></div>
|
|
5
|
+
<ComponentIndex :active-group="activeGroup" :active-id="activeId" :search="directorySearch" />
|
|
6
|
+
</apple-aside>
|
|
7
|
+
<main class="main-content components-page">
|
|
8
|
+
<header class="page-heading"><div><div class="page-kicker">COMPONENTS</div><h1>组件<span>。</span></h1><p>{{ catalog.length }} 个组件,完整呈现。向下浏览,直接体验。</p></div></header>
|
|
9
|
+
<div class="feed-toolbar">
|
|
10
|
+
<apple-button class="mobile-index-toggle" variant="ghost" size="small" :icon="PanelLeft" @click="mobileIndex = true">组件目录</apple-button>
|
|
11
|
+
<span class="feed-count">全部组件 <small>{{ catalog.length }}</small></span>
|
|
12
|
+
<div class="preview-controls" role="group" aria-label="预览尺寸">
|
|
13
|
+
<apple-button variant="ghost" size="small" icon-only :icon="Monitor" :class="{ active: previewMode === 'desktop' }" :aria-pressed="previewMode === 'desktop'" label="桌面预览" @click="previewMode = 'desktop'" />
|
|
14
|
+
<apple-button variant="ghost" size="small" icon-only :icon="Smartphone" :class="{ active: previewMode === 'mobile' }" :aria-pressed="previewMode === 'mobile'" label="手机预览" @click="previewMode = 'mobile'" />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<div ref="feed" class="component-feed">
|
|
18
|
+
<section v-for="(section, index) in sections" :id="section.id" :key="section.id" class="feed-group feed-anchor" :data-group="section.id" :aria-labelledby="`${section.id}-heading`">
|
|
19
|
+
<header class="feed-group-heading"><span>0{{ index + 1 }}</span><h2 :id="`${section.id}-heading`">{{ section.label }}</h2><small>{{ section.items.length }} 个组件</small></header>
|
|
20
|
+
<article v-for="item in section.items" :id="componentId(item.name)" :key="item.name" class="component-card feed-anchor" :data-group="section.id" :aria-labelledby="`${componentId(item.name)}-heading`">
|
|
21
|
+
<header class="component-card-heading"><div><h3 :id="`${componentId(item.name)}-heading`"><router-link :to="`#${componentId(item.name)}`">{{ item.label }}<Hash :size="14" /></router-link></h3><p>{{ item.description }}</p></div><code>{{ componentId(item.name) }}</code></header>
|
|
22
|
+
<section class="detail-preview" :class="{ 'mobile-preview': previewMode === 'mobile' }" :aria-label="`${item.label}预览`"><ComponentDemo :name="item.name" @navigate="navigateGroup" /></section>
|
|
23
|
+
<apple-accordion class="component-source" :items="sourceSections">
|
|
24
|
+
<template #item>
|
|
25
|
+
<div class="code-view"><div class="code-toolbar"><span>使用示例</span><apple-button variant="ghost" size="small" @click="copy(item.code)">复制代码</apple-button></div><pre><code>{{ item.code }}</code></pre><h4>接口</h4><p class="api-line">{{ item.api }}</p></div>
|
|
26
|
+
</template>
|
|
27
|
+
</apple-accordion>
|
|
28
|
+
</article>
|
|
29
|
+
</section>
|
|
30
|
+
</div>
|
|
31
|
+
<PageFooter />
|
|
32
|
+
</main>
|
|
33
|
+
<apple-drawer v-model="mobileIndex" title="组件目录" placement="left" width="290px">
|
|
34
|
+
<div class="directory-search"><apple-search v-model="directorySearch" label="搜索组件目录" placeholder="名称、标签或描述" /></div>
|
|
35
|
+
<ComponentIndex :active-group="activeGroup" :active-id="activeId" :search="directorySearch" @navigate="mobileIndex = false" />
|
|
36
|
+
</apple-drawer>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
41
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
42
|
+
import { Hash, Monitor, PanelLeft, Smartphone } from 'lucide-vue-next'
|
|
43
|
+
import { useApple } from 'apptify'
|
|
44
|
+
import { catalog, sections, componentId } from '../catalog'
|
|
45
|
+
import ComponentDemo from '../ComponentDemo.vue'
|
|
46
|
+
import ComponentIndex from '../components/ComponentIndex.vue'
|
|
47
|
+
import PageFooter from '../components/PageFooter.vue'
|
|
48
|
+
|
|
49
|
+
const route = useRoute()
|
|
50
|
+
const router = useRouter()
|
|
51
|
+
const apple = useApple()
|
|
52
|
+
const feed = ref<HTMLElement>()
|
|
53
|
+
const activeGroup = ref(sections[0].id)
|
|
54
|
+
const activeId = ref('')
|
|
55
|
+
const previewMode = ref('desktop')
|
|
56
|
+
const mobileIndex = ref(false)
|
|
57
|
+
const directorySearch = ref('')
|
|
58
|
+
const sourceSections = [{ label: '代码与 API', value: 'source' }]
|
|
59
|
+
let anchors: HTMLElement[] = []
|
|
60
|
+
let frame = 0
|
|
61
|
+
let observer: ResizeObserver | undefined
|
|
62
|
+
|
|
63
|
+
function updatePosition() {
|
|
64
|
+
frame = 0
|
|
65
|
+
let current: HTMLElement | undefined
|
|
66
|
+
for (const anchor of anchors) {
|
|
67
|
+
if (anchor.getBoundingClientRect().top > 128) break
|
|
68
|
+
if (!current || Math.abs(anchor.getBoundingClientRect().top - current.getBoundingClientRect().top) > 1) current = anchor
|
|
69
|
+
else if (anchor.id === route.hash.slice(1) || (current.id !== route.hash.slice(1) && anchor.id === activeId.value)) current = anchor
|
|
70
|
+
}
|
|
71
|
+
activeId.value = current?.id ?? ''
|
|
72
|
+
activeGroup.value = current?.dataset.group ?? sections[0].id
|
|
73
|
+
}
|
|
74
|
+
function schedulePosition() {
|
|
75
|
+
if (!frame) frame = requestAnimationFrame(updatePosition)
|
|
76
|
+
}
|
|
77
|
+
function navigateGroup(label: string) {
|
|
78
|
+
const section = sections.find(section => section.label === label)
|
|
79
|
+
void router.push(section ? `/components#${section.id}` : '/components')
|
|
80
|
+
}
|
|
81
|
+
async function copy(value: string) {
|
|
82
|
+
try { await navigator.clipboard.writeText(value); apple.notify('已复制', { tone: 'success' }) }
|
|
83
|
+
catch { apple.notify('无法访问剪贴板,请手动选择代码复制', { tone: 'warning' }) }
|
|
84
|
+
}
|
|
85
|
+
watch(() => route.hash, () => { mobileIndex.value = false; schedulePosition() })
|
|
86
|
+
watch(activeId, async () => {
|
|
87
|
+
await nextTick()
|
|
88
|
+
const sidebar = document.querySelector<HTMLElement>('.sidebar')
|
|
89
|
+
const current = sidebar?.querySelector<HTMLElement>('[aria-current="location"]')
|
|
90
|
+
if (!sidebar || !current) return
|
|
91
|
+
const item = current.getBoundingClientRect(), bounds = sidebar.getBoundingClientRect()
|
|
92
|
+
if (item.top < bounds.top + 16) sidebar.scrollTop -= bounds.top + 16 - item.top
|
|
93
|
+
else if (item.bottom > bounds.bottom - 16) sidebar.scrollTop += item.bottom - bounds.bottom + 16
|
|
94
|
+
})
|
|
95
|
+
onMounted(() => {
|
|
96
|
+
anchors = Array.from(feed.value!.querySelectorAll<HTMLElement>('.feed-anchor'))
|
|
97
|
+
window.addEventListener('scroll', schedulePosition, { passive: true })
|
|
98
|
+
window.addEventListener('resize', schedulePosition)
|
|
99
|
+
observer = new ResizeObserver(schedulePosition)
|
|
100
|
+
observer.observe(feed.value!)
|
|
101
|
+
schedulePosition()
|
|
102
|
+
})
|
|
103
|
+
onBeforeUnmount(() => {
|
|
104
|
+
window.removeEventListener('scroll', schedulePosition)
|
|
105
|
+
window.removeEventListener('resize', schedulePosition)
|
|
106
|
+
observer?.disconnect()
|
|
107
|
+
cancelAnimationFrame(frame)
|
|
108
|
+
})
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<style scoped>
|
|
112
|
+
.directory-search { min-width: 0; margin: 0 4px 16px; }
|
|
113
|
+
.directory-search :deep(.apple-field__label) { font-size: 11px; }
|
|
114
|
+
.directory-search :deep(input) { min-width: 0; font-size: 12px; }
|
|
115
|
+
.components-page > * { max-width: none; }
|
|
116
|
+
.feed-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr)); gap: 20px; align-items: start; }
|
|
117
|
+
.feed-group-heading { grid-column: 1 / -1; padding-bottom: 0; }
|
|
118
|
+
.component-card { margin-bottom: 0; }
|
|
119
|
+
.component-source :deep(.apple-accordion__item) { border: 0; }
|
|
120
|
+
.component-source :deep(.apple-accordion__item > h3) { font-size: 11px; font-weight: 400; }
|
|
121
|
+
.component-source :deep(.apple-accordion__item > h3 > button) { min-height: 44px; gap: 8px; padding: 13px 28px; color: var(--apple-secondary); }
|
|
122
|
+
.component-source :deep(.apple-accordion__content) { padding: 0; }
|
|
123
|
+
@media (max-width: 800px) {
|
|
124
|
+
.component-source :deep(.apple-accordion__item > h3 > button) { padding-inline: 18px; }
|
|
125
|
+
}
|
|
126
|
+
</style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<main class="main-content home-page">
|
|
3
|
+
<section class="home-intro">
|
|
4
|
+
<div class="page-kicker">APPTIFY / 界面与交互</div>
|
|
5
|
+
<h1>让界面自然,<br /><span>让细节动人。</span></h1>
|
|
6
|
+
<p>从一个按钮,到完整体验。<br class="mobile-break" /> 简洁的组件,恰到好处的交互。</p>
|
|
7
|
+
<div class="home-actions">
|
|
8
|
+
<router-link v-slot="{ href, navigate }" to="/components" custom>
|
|
9
|
+
<apple-button :href="href" @click="navigate">浏览全部组件 <ArrowRight :size="17" aria-hidden="true" /></apple-button>
|
|
10
|
+
</router-link>
|
|
11
|
+
<router-link v-slot="{ href, navigate }" to="/settings" custom>
|
|
12
|
+
<apple-link :href="href" class="home-link" @click="navigate">调整外观与动效 <ArrowUpRight :size="16" aria-hidden="true" /></apple-link>
|
|
13
|
+
</router-link>
|
|
14
|
+
</div>
|
|
15
|
+
</section>
|
|
16
|
+
<section aria-label="精选交互示例">
|
|
17
|
+
<div class="section-heading"><h2>细节,即刻体验。</h2><span>精选交互示例 · 直接试一试</span></div>
|
|
18
|
+
<div class="specimen-grid">
|
|
19
|
+
<section class="specimen"><div class="specimen-caption"><span>01 / 导航</span></div><h2>内容,各归其位。</h2><apple-search v-model="settingQuery" placeholder="搜索设置" label="搜索设置" /><apple-list v-model="setting" :items="filteredSettings" selectable label="设置导航示例" /><p v-if="!filteredSettings.length" class="search-empty" role="status">没有匹配的设置,试试其他关键词。</p></section>
|
|
20
|
+
<section class="specimen"><div class="specimen-caption"><span>02 / 控制</span></div><h2>点到,即止。</h2><div class="button-specimens"><apple-button :icon="arrowIcon" @click="$apple.notify('操作已完成',{tone:'success'})">继续探索</apple-button><apple-button variant="secondary" @click="$apple.notify('已加入收藏')">加入收藏</apple-button><apple-button variant="outline" @click="$router.push('/components#apple-button')">了解更多</apple-button><div class="icon-button-row"><apple-button variant="secondary" :icon="plusIcon" icon-only label="添加" @click="$apple.notify('已添加')" /><apple-button :variant="liked?'primary':'secondary'" :icon="heartIcon" icon-only :label="liked ? '取消收藏' : '收藏'" :aria-pressed="liked" @click="liked=!liked" /><apple-button variant="secondary" :icon="downloadIcon" icon-only label="浏览组件示例" @click="$router.push('/components')" /></div></div><div class="specimen-foot"><code>apple-button</code><span>5 种样式</span></div></section>
|
|
21
|
+
<section class="specimen"><div class="specimen-caption"><span>03 / 偏好</span></div><h2>恰好,是你的。</h2><div class="preference-demo"><apple-segmented-control v-model="period" :items="periods" label="统计周期" /><div class="setting-line"><div><strong>消息通知</strong><small>不错过重要的事</small></div><apple-switch v-model="notices" aria-label="消息通知" /></div><div class="setting-line"><div><strong>自动同步</strong><small>在所有设备上保持一致</small></div><apple-switch v-model="sync" aria-label="自动同步" /></div><apple-slider v-model="volume" label="音量" /><div class="check-row"><apple-checkbox v-model="agreed" label="记住我的偏好" /></div></div></section>
|
|
22
|
+
<section class="specimen form-specimen"><div class="specimen-caption"><span>04 / 输入</span></div><h2>开始一段对话。</h2><apple-form @submit="$apple.notify(`你好,${name || '新朋友'}!`,{tone:'success'})"><apple-input v-model="name" label="你的名字" placeholder="怎么称呼你" autocomplete="name" clearable required /><apple-input v-model="email" label="电子邮箱" type="email" placeholder="name@example.com" autocomplete="email" required /><apple-button type="submit">加入我们<ArrowRight :size="15" /></apple-button></apple-form></section>
|
|
23
|
+
<section class="specimen media-specimen"><div class="specimen-caption"><span>05 / 媒体</span></div><h2>靠近一点,再一点。</h2><apple-image :gallery="[{ src: '/images/lake.jpg', alt: '山间湖泊和木屋' }, { src: '/images/airpods-max-orange.jpg', alt: '橙色耳机' }]" aspect-ratio="4/3" /><div class="specimen-foot"><code>apple-image</code><span>山间的一刻</span></div></section>
|
|
24
|
+
<section class="specimen feedback-specimen"><div class="specimen-caption"><span>06 / 反馈</span></div><h2>每一步,都有回应。</h2><apple-alert title="所有更改已保存" tone="success" /><div class="progress-demo"><div><span>正在同步</span><span>{{progress}}%</span></div><apple-progress :model-value="progress" label="正在同步" /></div><apple-avatar-group :items="[{name:'林初'},{name:'Alex'},{name:'Taylor'},{name:'Quinn'}]" /><div class="feedback-actions"><apple-button variant="secondary" size="small" @click="openDemoDialog">打开对话框</apple-button><apple-button variant="ghost" size="small" @click="$apple.notify('所有更改已保存',{tone:'success'});progress=Math.min(100,progress+10)">显示提示</apple-button></div></section>
|
|
25
|
+
</div>
|
|
26
|
+
</section>
|
|
27
|
+
<section class="more-components">
|
|
28
|
+
<div><h2>更多组件,同样顺手。</h2><span>{{ catalog.length }} 个组件,预览与交互都在一页之中。</span></div>
|
|
29
|
+
<router-link v-slot="{ href, navigate }" to="/components" custom>
|
|
30
|
+
<apple-link :href="href" class="home-link" @click="navigate">查看全部组件 <ArrowRight :size="16" aria-hidden="true" /></apple-link>
|
|
31
|
+
</router-link>
|
|
32
|
+
</section>
|
|
33
|
+
<PageFooter />
|
|
34
|
+
</main>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
import { defineComponent, markRaw } from 'vue'
|
|
39
|
+
import { ArrowRight, ArrowUpRight, Download, Heart, Plus } from 'lucide-vue-next'
|
|
40
|
+
import { catalog } from '../catalog'
|
|
41
|
+
import PageFooter from '../components/PageFooter.vue'
|
|
42
|
+
|
|
43
|
+
export default defineComponent({
|
|
44
|
+
components: { ArrowRight, ArrowUpRight, PageFooter },
|
|
45
|
+
data() { return {
|
|
46
|
+
catalog, arrowIcon: markRaw(ArrowRight), plusIcon: markRaw(Plus), heartIcon: markRaw(Heart), downloadIcon: markRaw(Download),
|
|
47
|
+
period: 'month', periods: [{label:'日',value:'day'},{label:'周',value:'week'},{label:'月',value:'month'}],
|
|
48
|
+
notices: true, sync: false, agreed: true, volume: 64, progress: 68, name: '', email: '', liked: false,
|
|
49
|
+
settingQuery: '', setting: 'profile', overviewSettings: [
|
|
50
|
+
{label:'个人资料',value:'profile',description:'姓名、头像和联系方式'},
|
|
51
|
+
{label:'通知',value:'notices',description:'推送和邮件偏好'},
|
|
52
|
+
{label:'隐私与安全',value:'privacy',description:'管理你的账户安全设置'},
|
|
53
|
+
],
|
|
54
|
+
} },
|
|
55
|
+
computed: {
|
|
56
|
+
filteredSettings() {
|
|
57
|
+
const query = this.settingQuery.trim()
|
|
58
|
+
return this.overviewSettings.filter(item => item.label.includes(query) || item.description.includes(query))
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
methods: {
|
|
62
|
+
openDemoDialog() { this.$apple.dialog({title:'一切,就从这里开始。',message:'试试这些组件,找到适合你的交互。',confirmText:'好的',cancelText:'稍后'}) },
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<style scoped>
|
|
68
|
+
.home-link { min-height: 44px; gap: 9px; font-size: 13px; font-weight: 500; }
|
|
69
|
+
.search-empty { margin: 12px 0 0; color: var(--apple-secondary); font-size: 12px; line-height: 1.7; }
|
|
70
|
+
/* Keep the library's control sizes in the compact specimens. */
|
|
71
|
+
.button-specimens > .apple-button { min-height: 44px; font-size: inherit; }
|
|
72
|
+
.icon-button-row .apple-button { width: 44px; height: 44px; min-height: 44px; }
|
|
73
|
+
.form-specimen .apple-form .apple-button { min-height: 44px; font-size: inherit; }
|
|
74
|
+
.feedback-actions .apple-button { font-size: 13px; }
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<main class="main-content settings-page">
|
|
3
|
+
<header class="page-heading"><div style="padding: 0 16px"><h1>设置</h1></div></header>
|
|
4
|
+
<section class="settings-section" aria-labelledby="theme-heading">
|
|
5
|
+
<div class="settings-section-heading"><Palette :size="21" aria-hidden="true" /><div><h2 id="theme-heading">主题</h2><p>应用于所有页面,并保存在这台设备上。</p></div></div>
|
|
6
|
+
<div class="theme-picker" role="group" aria-label="选择主题">
|
|
7
|
+
<apple-button v-for="theme in themes" variant="ghost" class="theme-choice" :key="theme.value" type="button" :aria-pressed="apple.theme.value.name === theme.value" :class="{ selected: apple.theme.value.name === theme.value }" @click="apple.theme.value.set(theme.value)">
|
|
8
|
+
<span aria-hidden="true" class="theme-swatch" :style="{ background: theme.background, color: theme.accent }"><i /><i /><i /></span>
|
|
9
|
+
<span class="theme-label">{{ theme.label }}<Check v-if="apple.theme.value.name === theme.value" :size="15" aria-hidden="true" /></span>
|
|
10
|
+
</apple-button>
|
|
11
|
+
</div>
|
|
12
|
+
</section>
|
|
13
|
+
<section class="settings-section" aria-labelledby="glass-heading">
|
|
14
|
+
<div class="settings-section-heading"><Layers :size="21" aria-hidden="true" /><div><h2 id="glass-heading">玻璃效果</h2><p>调整下拉浮层的底色与模糊程度,并保存在这台设备上。</p></div></div>
|
|
15
|
+
<div class="glass-controls">
|
|
16
|
+
<apple-slider :model-value="apple.glass.value.opacity" label="玻璃不透明度" :min="0" :max="100" :step="0.1" :format-value="formatOpacity" hint="0% 完全透明,100% 完全不透明。" @update:model-value="setGlassOpacity" />
|
|
17
|
+
<apple-slider :model-value="apple.glass.value.blur" label="玻璃模糊" :min="2" :max="22" :step="1" :format-value="formatBlur" hint="范围 2–22px,默认 12px。" @update:model-value="setGlassBlur" />
|
|
18
|
+
</div>
|
|
19
|
+
<div class="glass-preview" aria-label="玻璃效果实时预览">
|
|
20
|
+
<div class="glass-preview-backdrop" :style="{ backgroundImage: glassPreviewPattern }" aria-hidden="true" />
|
|
21
|
+
<apple-list class="glass-preview-panel" :items="glassPreviewItems" label="玻璃预览内容" />
|
|
22
|
+
</div>
|
|
23
|
+
<p class="glass-preview-note">细碎图案便于观察模糊程度,预览随滑块实时变化。账号菜单和登录对话框保持实底。</p>
|
|
24
|
+
<div class="glass-preview-select"><apple-select v-model="glassPreviewChoice" label="打开下拉查看实际效果" :items="glassPreviewItems" /></div>
|
|
25
|
+
</section>
|
|
26
|
+
<section class="settings-section" aria-labelledby="motion-heading">
|
|
27
|
+
<div class="settings-section-heading"><Waves :size="21" aria-hidden="true" /><div><h2 id="motion-heading">动效</h2><p>为页面切换和组件交互选择动效强度。</p></div></div>
|
|
28
|
+
<apple-segmented-control :model-value="apple.motion.value.mode" :items="motionOptions" label="全局动效" @update:model-value="setMotion" />
|
|
29
|
+
<p class="settings-note">{{ motionDescriptions[apple.motion.value.mode] }}<template v-if="apple.motion.value.reduced"> 系统已开启减少动态效果。</template></p>
|
|
30
|
+
<div class="motion-preview">
|
|
31
|
+
<div class="motion-preview-heading"><span>试试当前效果</span><apple-button variant="ghost" size="small" @click="expanded = !expanded">{{ expanded ? '收起预览' : '展开预览' }}</apple-button></div>
|
|
32
|
+
<apple-auto-size><div class="motion-preview-content"><apple-button @click="apple.notify('这就是当前的交互效果', { tone: 'success' })">轻点一下</apple-button><p v-if="expanded">舒适的颜色,自然的变化。每一个细节,都跟随你的偏好。</p></div></apple-auto-size>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
<PageFooter />
|
|
36
|
+
</main>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { ref } from 'vue'
|
|
41
|
+
import { Check, Layers, Palette, Waves } from 'lucide-vue-next'
|
|
42
|
+
import { builtInThemes, useApple, type Motion } from 'apptify'
|
|
43
|
+
import PageFooter from '../components/PageFooter.vue'
|
|
44
|
+
|
|
45
|
+
const apple = useApple()
|
|
46
|
+
const expanded = ref(false)
|
|
47
|
+
const glassPreviewPattern = `url("data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="176" height="144" viewBox="0 0 176 144"><g fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><g stroke="#7196ad"><path d="M12 12h15v10H12zM12 12l7.5 6 7.5-6"/><circle cx="64" cy="17" r="8"/><path d="M64 12v5l4 2M110 9l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1zM148 10h12v15h-12zM152 14h5M152 18h5M152 22h3"/></g><g stroke="#bd856b"><path d="M12 48h12v11a5 5 0 0 1-5 5h-2a5 5 0 0 1-5-5zM24 50h3a4 4 0 0 1 0 8h-3M15 43v2M20 42v3M57 46l7-3 7 3v16l-7-3-7 3zM64 43v16M105 47l7-4 7 4v8l-7 4-7-4zM105 47l7 4 7-4M112 51v8M146 48h15v12h-15zM149 45v6M158 45v6M146 53h15M150 56h2M156 56h2"/></g><g stroke="#719281"><path d="M12 84c13-6 18 0 11 9-9 4-15 0-11-9zM12 94l12-11M62 80l-6 9 6 9 6-9zM61 89h3M105 82h13v13h-13zM108 85h7M108 88h7M108 91h4M147 83a7 7 0 0 1 14 0v6M147 85h3v7h-3zM158 85h3v7h-3z"/></g><g stroke="#a58cbb"><path d="M12 118l10-6 4 6-10 6zM12 118l-1 7 5-1M57 119c0-10 14-10 14 0M57 119v6h4v-6zM67 119v6h4v-6zM105 113h13v13h-13zM109 113v13M106 117h12M151 113h7v5l4 5-3 4h-9l-3-4 4-5zM151 118h7"/></g></g></svg>')}")`
|
|
48
|
+
const glassPreviewChoice = ref('profile')
|
|
49
|
+
const glassPreviewItems = [
|
|
50
|
+
{ label: '个人资料', value: 'profile', description: '姓名、头像和联系方式' },
|
|
51
|
+
{ label: '通知', value: 'notices', description: '推送和邮件偏好' },
|
|
52
|
+
{ label: '隐私与安全', value: 'privacy', description: '管理你的账户安全设置' },
|
|
53
|
+
]
|
|
54
|
+
function formatOpacity(value: number) { return `${Number(value.toFixed(1))}%` }
|
|
55
|
+
function formatBlur(value: number) { return `${value}px` }
|
|
56
|
+
function setGlassOpacity(opacity: number) { apple.glass.value.set({ opacity }) }
|
|
57
|
+
function setGlassBlur(blur: number) { apple.glass.value.set({ blur }) }
|
|
58
|
+
const themes = [
|
|
59
|
+
{ label: '跟随系统', value: 'system', background: 'linear-gradient(125deg, #f5f5f7 50%, #242426 50%)', accent: '#8a8a90' },
|
|
60
|
+
{ label: '浅色', value: 'light', background: '#f5f5f7', accent: builtInThemes.light.tokens.accent },
|
|
61
|
+
{ label: '深色', value: 'dark', background: '#242426', accent: builtInThemes.dark.tokens.accent },
|
|
62
|
+
{ label: '石墨', value: 'graphite', background: '#253334', accent: '#a6ceca' },
|
|
63
|
+
{ label: '玫瑰', value: 'rose', background: '#fff1f5', accent: '#a83b65' },
|
|
64
|
+
]
|
|
65
|
+
const motionOptions = [{ label: '自动', value: 'auto' }, { label: '完整', value: 'full' }, { label: '减弱', value: 'reduced' }, { label: '关闭', value: 'none' }]
|
|
66
|
+
const motionDescriptions: Record<string, string> = {
|
|
67
|
+
auto: '跟随系统的动态效果偏好,自动调整。',
|
|
68
|
+
full: '呈现完整的过渡与交互动效,同时尊重系统的减少动态效果设置。',
|
|
69
|
+
reduced: '缩短过渡、减少位移,保留必要的反馈。',
|
|
70
|
+
none: '立即呈现每次变化,关闭过渡动画。',
|
|
71
|
+
}
|
|
72
|
+
function setMotion(value: Motion) { apple.motion.value.set(value) }
|
|
73
|
+
function reset() {
|
|
74
|
+
apple.theme.value.set('light')
|
|
75
|
+
apple.motion.value.set('auto')
|
|
76
|
+
apple.glass.value.reset()
|
|
77
|
+
apple.notify('已恢复默认设置', { tone: 'success' })
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<style scoped>
|
|
82
|
+
.theme-picker { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
|
|
83
|
+
.theme-choice { min-width: 0; padding: 7px; border: 1px solid var(--apple-border); border-radius: 8px; }
|
|
84
|
+
.theme-choice.selected { border-color: var(--apple-accent); outline: 1px solid var(--apple-accent); }
|
|
85
|
+
.theme-choice:focus-visible { outline: 3px solid color-mix(in srgb, var(--apple-accent) 65%, transparent); outline-offset: 3px; }
|
|
86
|
+
.theme-choice :deep(.apple-button__content) { flex-direction: column; width: 100%; gap: 12px; }
|
|
87
|
+
.theme-label { color: var(--apple-text); }
|
|
88
|
+
.glass-controls { display: grid; gap: 20px; margin-bottom: 24px; }
|
|
89
|
+
.glass-controls :deep(.apple-slider__value) { min-width: 58px; }
|
|
90
|
+
.glass-preview { position: relative; isolation: isolate; display: grid; place-items: center; min-height: 274px; padding: 24px; border-radius: 8px; background: var(--apple-surface-alt); }
|
|
91
|
+
.glass-preview-backdrop { position: absolute; inset: 0; z-index: -1; border-radius: inherit; background-color: var(--apple-surface-alt); background-size: 176px 144px; }
|
|
92
|
+
.glass-preview-panel { width: 100%; max-width: 340px; padding: 12px; border: 1px solid var(--apple-border); border-radius: 10px; background: rgb(var(--apple-glass-rgb, 255 255 255) / var(--apple-glass-opacity, 0.31372549)); -webkit-backdrop-filter: blur(var(--apple-glass-blur, 12px)) saturate(2); backdrop-filter: blur(var(--apple-glass-blur, 12px)) saturate(2); box-shadow: var(--apple-shadow); }
|
|
93
|
+
.glass-preview-note { font-size: 12px; line-height: 1.8; color: var(--apple-secondary); margin: 14px 0 20px; }
|
|
94
|
+
.glass-preview-select { max-width: 440px; }
|
|
95
|
+
@media (max-width: 600px) {
|
|
96
|
+
.theme-picker { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
97
|
+
}
|
|
98
|
+
</style>
|