Skip to content

Commit 469c7c6

Browse files
committed
feat: taptip 图片上传和缩放功能
1 parent 929849d commit 469c7c6

9 files changed

Lines changed: 998 additions & 13 deletions

File tree

app/assets/css/writing-studio/plugins/image.css

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,77 @@
55
margin-bottom: 0.8rem;
66
}
77

8+
.writing-editor .tiptap [data-resize-container][data-node="image"] {
9+
width: 100%;
10+
}
11+
12+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-wrapper] {
13+
display: inline-block;
14+
}
15+
16+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle] {
17+
opacity: 0;
18+
pointer-events: none;
19+
transition: opacity 0.15s ease;
20+
}
21+
22+
.writing-editor .tiptap [data-resize-container][data-node="image"].ProseMirror-selectednode [data-resize-handle] {
23+
opacity: 1;
24+
pointer-events: auto;
25+
}
26+
27+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle] {
28+
z-index: 4;
29+
}
30+
31+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="left"],
32+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="right"] {
33+
width: 14px;
34+
cursor: ew-resize;
35+
}
36+
37+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="left"]::after,
38+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="right"]::after {
39+
content: "";
40+
position: absolute;
41+
top: 50%;
42+
width: 8px;
43+
height: 44px;
44+
border-radius: 999px;
45+
background: oklch(var(--primary));
46+
box-shadow: 0 0 0 1px oklch(var(--background) / 0.8);
47+
transform: translateY(-50%);
48+
}
49+
50+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="left"]::after {
51+
left: -4px;
52+
}
53+
54+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="right"]::after {
55+
right: -4px;
56+
}
57+
58+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle$="left"][data-resize-handle*="top"],
59+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle$="right"][data-resize-handle*="top"],
60+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle$="left"][data-resize-handle*="bottom"],
61+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle$="right"][data-resize-handle*="bottom"] {
62+
width: 12px;
63+
height: 12px;
64+
border-radius: 999px;
65+
background: oklch(var(--primary));
66+
box-shadow: 0 0 0 2px oklch(var(--background));
67+
}
68+
69+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="top-left"],
70+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="bottom-right"] {
71+
cursor: nwse-resize;
72+
}
73+
74+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="top-right"],
75+
.writing-editor .tiptap [data-resize-container][data-node="image"] [data-resize-handle="bottom-left"] {
76+
cursor: nesw-resize;
77+
}
78+
879
.writing-editor .tiptap img.ws-image[data-align="left"] {
980
margin-left: 0;
1081
margin-right: auto;
@@ -26,6 +97,12 @@
2697
border-radius: 0.375rem;
2798
}
2899

100+
.writing-editor .tiptap [data-resize-container][data-node="image"].ProseMirror-selectednode img.ws-image {
101+
outline: 2px solid oklch(var(--primary) / 0.45);
102+
outline-offset: 2px;
103+
border-radius: 0.375rem;
104+
}
105+
29106
.ws-image-menu {
30107
display: flex;
31108
align-items: center;
@@ -36,3 +113,166 @@
36113
padding: 0.25rem;
37114
box-shadow: 0 10px 24px -12px oklch(0 0 0 / 0.5);
38115
}
116+
117+
.ws-image-upload-panel {
118+
border: 1px solid oklch(var(--border));
119+
border-radius: 0.75rem;
120+
background: oklch(var(--card));
121+
padding: 0.75rem;
122+
box-shadow: 0 10px 20px -16px oklch(0 0 0 / 0.45);
123+
}
124+
125+
.ws-image-upload-panel-header {
126+
display: flex;
127+
align-items: center;
128+
justify-content: space-between;
129+
gap: 0.5rem;
130+
margin-bottom: 0.75rem;
131+
}
132+
133+
.ws-image-upload-panel-title {
134+
font-size: 0.875rem;
135+
font-weight: 600;
136+
color: oklch(var(--foreground));
137+
}
138+
139+
.ws-image-upload-dropzone {
140+
width: 100%;
141+
border: 2px dashed oklch(var(--primary) / 0.75);
142+
border-radius: 0.75rem;
143+
background: oklch(var(--muted) / 0.38);
144+
min-height: 8.5rem;
145+
padding: 1rem;
146+
display: flex;
147+
flex-direction: column;
148+
align-items: center;
149+
justify-content: center;
150+
gap: 0.45rem;
151+
text-align: center;
152+
transition: background-color 0.15s ease, border-color 0.15s ease;
153+
}
154+
155+
.ws-image-upload-dropzone.is-dragging {
156+
border-color: oklch(var(--primary));
157+
background: oklch(var(--primary) / 0.1);
158+
}
159+
160+
.ws-image-upload-dropzone-icon {
161+
width: 1.8rem;
162+
height: 1.8rem;
163+
color: oklch(var(--primary));
164+
}
165+
166+
.ws-image-upload-dropzone-title {
167+
font-size: 1rem;
168+
font-weight: 600;
169+
color: oklch(var(--foreground));
170+
}
171+
172+
.ws-image-upload-dropzone-hint {
173+
font-size: 0.875rem;
174+
color: oklch(var(--muted-foreground));
175+
}
176+
177+
.ws-image-upload-mode-text {
178+
margin-top: 0.5rem;
179+
font-size: 0.75rem;
180+
color: oklch(var(--muted-foreground));
181+
}
182+
183+
.ws-image-upload-list {
184+
margin-top: 0.75rem;
185+
display: flex;
186+
flex-direction: column;
187+
gap: 0.5rem;
188+
}
189+
190+
.ws-image-upload-item {
191+
border: 1px solid oklch(var(--border));
192+
border-radius: 0.75rem;
193+
background: oklch(var(--background));
194+
padding: 0.625rem 0.75rem;
195+
}
196+
197+
.ws-image-upload-item-main {
198+
display: flex;
199+
flex-direction: column;
200+
gap: 0.375rem;
201+
}
202+
203+
.ws-image-upload-item-name-row {
204+
display: flex;
205+
align-items: center;
206+
justify-content: space-between;
207+
gap: 0.5rem;
208+
}
209+
210+
.ws-image-upload-item-name {
211+
font-size: 0.875rem;
212+
color: oklch(var(--foreground));
213+
line-height: 1.2;
214+
word-break: break-all;
215+
}
216+
217+
.ws-image-upload-item-remove {
218+
width: 1.5rem;
219+
height: 1.5rem;
220+
border-radius: 0.375rem;
221+
display: inline-flex;
222+
align-items: center;
223+
justify-content: center;
224+
color: oklch(var(--muted-foreground));
225+
}
226+
227+
.ws-image-upload-item-remove:hover {
228+
background: oklch(var(--muted));
229+
color: oklch(var(--foreground));
230+
}
231+
232+
.ws-image-upload-item-meta-row {
233+
display: flex;
234+
align-items: center;
235+
justify-content: space-between;
236+
gap: 0.5rem;
237+
}
238+
239+
.ws-image-upload-item-size {
240+
font-size: 0.75rem;
241+
color: oklch(var(--muted-foreground));
242+
}
243+
244+
.ws-image-upload-item-status {
245+
font-size: 0.75rem;
246+
color: oklch(var(--primary));
247+
display: inline-flex;
248+
align-items: center;
249+
gap: 0.25rem;
250+
}
251+
252+
.ws-image-upload-progress-track {
253+
position: relative;
254+
width: 100%;
255+
height: 0.28rem;
256+
border-radius: 999px;
257+
background: oklch(var(--muted));
258+
overflow: hidden;
259+
}
260+
261+
.ws-image-upload-progress-fill {
262+
position: absolute;
263+
left: 0;
264+
top: 0;
265+
bottom: 0;
266+
width: 0;
267+
border-radius: inherit;
268+
background: oklch(var(--primary));
269+
transition: width 0.18s ease;
270+
}
271+
272+
.ws-image-upload-progress-fill.is-success {
273+
background: oklch(var(--primary));
274+
}
275+
276+
.ws-image-upload-progress-fill.is-error {
277+
background: oklch(var(--destructive));
278+
}

0 commit comments

Comments
 (0)