11---
22// Renderiza un proceso BPMN como SVG servido desde el servidor: cero JS en el
3- // cliente, cero dependencias. La geometría la calcula src/lib/bpmn-layout.ts;
4- // aquí solo se pintan figuras .
3+ // cliente, cero dependencias. La geometría la calcula src/lib/bpmn-layout.ts y
4+ // cada figura la pinta BpmnShape; aquí se montan la piscina y los flujos .
55import { GEO , layout , type BpmnProcess } from ' ../lib/bpmn-layout'
6+ import BpmnShape from ' ./BpmnShape.astro'
67
78interface Props {
89 proceso: BpmnProcess
@@ -14,20 +15,9 @@ const { width, height, lanes, nodes, edges } = layout(proceso)
1415// Sufijo único por diagrama: varios SVG en la misma página no pueden compartir
1516// los ids de <marker>, o el segundo se queda sin puntas de flecha.
1617const uid = proceso .id
17-
18- const esTarea = (t : string ) => t .startsWith (' task' )
19- const esCompuerta = (t : string ) => t .startsWith (' gateway' )
20-
21- /** Trazo del anillo exterior de cada evento, que es lo que los distingue en BPMN. */
22- function anilloEvento(type : string ) {
23- if (type === ' intermediateEvent' ) return { grosor: 1.4 , doble: true , color: ' var(--bpmn-line)' }
24- if (type === ' endEventError' ) return { grosor: 3.2 , doble: false , color: ' var(--bpmn-error)' }
25- if (type .startsWith (' endEvent' )) return { grosor: 3.2 , doble: false , color: ' var(--bpmn-end)' }
26- return { grosor: 1.4 , doble: false , color: ' var(--bpmn-start)' }
27- }
2818---
2919
30- <figure class =" bpmn" >
20+ <figure class =" bpmn bpmn-scope " >
3121 <div class =" bpmn-scroll" >
3222 <svg
3323 width ={ width }
@@ -84,16 +74,17 @@ function anilloEvento(type: string) {
8474 >
8575 { lane .label }
8676 </text >
87- <line
88- x1 = " 0"
89- y1 = { lane .y }
90- x2 = { width }
91- y2 = { lane .y }
92- stroke = " var(--bpmn-border)"
93- stroke-width = { i === 0 ? 1.2 : 1 }
94- />
77+ { i > 0 && <line x1 = " 0" y1 = { lane .y } x2 = { width } y2 = { lane .y } stroke = " var(--bpmn-border)" stroke-width = " 1" />}
9578 </g >
9679 ))}
80+ <line
81+ x1 ={ GEO .laneHeaderW }
82+ y1 =" 0"
83+ x2 ={ GEO .laneHeaderW }
84+ y2 ={ height }
85+ stroke =" var(--bpmn-border)"
86+ stroke-width =" 1.2"
87+ />
9788 <rect
9889 x =" 0.6"
9990 y =" 0.6"
@@ -104,7 +95,6 @@ function anilloEvento(type: string) {
10495 stroke-width =" 1.2"
10596 rx =" 3"
10697 />
107- <line x1 ={ GEO .laneHeaderW } y1 =" 0" x2 ={ GEO .laneHeaderW } y2 ={ height } stroke =" var(--bpmn-border)" stroke-width =" 1.2" />
10898 </g >
10999
110100 { /* ── Flujos ─────────────────────────────────────────────────────── */ }
@@ -123,13 +113,19 @@ function anilloEvento(type: string) {
123113 <g >
124114 <rect
125115 x = { e .labelAt .x - (e .label .length * 5.1 ) / 2 - 4 }
126- y = { e .labelAt .y - 9 }
116+ y = { e .labelAt .y - 8 }
127117 width = { e .label .length * 5.1 + 8 }
128118 height = " 15"
129119 rx = " 3"
130120 fill = " var(--bpmn-label-bg)"
131121 />
132- <text class = " bpmn-flow-label" x = { e .labelAt .x } y = { e .labelAt .y } text-anchor = " middle" dominant-baseline = " central" >
122+ <text
123+ class = " bpmn-flow-label"
124+ x = { e .labelAt .x }
125+ y = { e .labelAt .y }
126+ text-anchor = " middle"
127+ dominant-baseline = " central"
128+ >
133129 { e .label }
134130 </text >
135131 </g >
@@ -138,160 +134,21 @@ function anilloEvento(type: string) {
138134 ))}
139135 </g >
140136
141- { /* ── Nodos ── ────────────────────────────────────────────────────── */ }
137+ { /* ── Figuras ────────────────────────────────────────────────────── */ }
142138 <g class =" bpmn-nodes" >
143- { nodes .map ((n ) => (
144- <g >
145- { esTarea (n .type ) && (
146- <g >
147- <rect
148- x = { n .cx - n .w / 2 }
149- y = { n .cy - n .h / 2 }
150- width = { n .w }
151- height = { n .h }
152- rx = " 9"
153- fill = " var(--bpmn-task-fill)"
154- stroke = " var(--bpmn-task-stroke)"
155- stroke-width = " 1.3"
156- />
157- { /* Marcador de tipo de tarea, arriba a la izquierda como manda la notación. */ }
158- <g transform = { ` translate(${n .cx - n .w / 2 + 7 } ${n .cy - n .h / 2 + 7 }) ` } class = " bpmn-marker" >
159- { n .type === ' taskUser' && (
160- <g >
161- <circle cx = " 5" cy = " 3.4" r = " 2.3" />
162- <path d = " M 0.6 10 a 4.4 4.4 0 0 1 8.8 0" />
163- </g >
164- )}
165- { n .type === ' taskService' && (
166- <g >
167- <circle cx = " 5" cy = " 5.2" r = " 2.2" />
168- <path d = " M 5 0.6 v 1.6 M 5 8.2 v 1.6 M 0.4 5.2 h 1.6 M 8 5.2 h 1.6 M 1.8 2 l 1.1 1.1 M 7.1 7.3 l 1.1 1.1 M 8.2 2 l -1.1 1.1 M 2.9 7.3 l -1.1 1.1" />
169- </g >
170- )}
171- { n .type === ' taskScript' && (
172- <g >
173- <path d = " M 2 0.8 h 6.2 a 1.6 1.6 0 0 0 -1.6 1.6 v 6.2 a 1.6 1.6 0 0 1 -1.6 1.6 H 1.6" />
174- <path d = " M 3.2 3.4 h 3.4 M 3.2 5.6 h 3.4" />
175- </g >
176- )}
177- { /* El sobre relleno usa `style` y no el atributo `fill`: una
178- regla CSS de la clase le gana a un atributo de presentación. */ }
179- { n .type === ' taskSend' && (
180- <g >
181- <rect x = " 0.6" y = " 1.8" width = " 9" height = " 6.8" rx = " 1" style = " fill: var(--bpmn-line)" />
182- <path d = " M 0.9 2.3 L 5.1 5.6 L 9.3 2.3" style = " stroke: var(--bpmn-task-fill)" />
183- </g >
184- )}
185- </g >
186- <text class = " bpmn-node-label" x = { n .cx } y = { n .cy } text-anchor = " middle" dominant-baseline = " central" >
187- { n .lines .map ((line , i ) => (
188- <tspan x = { n .cx } dy = { i === 0 ? - ((n .lines .length - 1 ) * 13 ) / 2 : 13 } >
189- { line }
190- </tspan >
191- ))}
192- </text >
193- </g >
194- )}
195-
196- { esCompuerta (n .type ) && (
197- <g >
198- <path
199- d = { ` M ${n .cx } ${n .cy - n .h / 2 } L ${n .cx + n .w / 2 } ${n .cy } L ${n .cx } ${n .cy + n .h / 2 } L ${n .cx - n .w / 2 } ${n .cy } Z ` }
200- fill = " var(--bpmn-gw-fill)"
201- stroke = " var(--bpmn-gw-stroke)"
202- stroke-width = " 1.4"
203- />
204- { n .type === ' gatewayExclusive' && (
205- <path
206- d = { ` M ${n .cx - 6 } ${n .cy - 6 } L ${n .cx + 6 } ${n .cy + 6 } M ${n .cx + 6 } ${n .cy - 6 } L ${n .cx - 6 } ${n .cy + 6 } ` }
207- stroke = " var(--bpmn-gw-stroke)"
208- stroke-width = " 1.9"
209- stroke-linecap = " round"
210- />
211- )}
212- { n .type === ' gatewayParallel' && (
213- <path
214- d = { ` M ${n .cx } ${n .cy - 8 } V ${n .cy + 8 } M ${n .cx - 8 } ${n .cy } H ${n .cx + 8 } ` }
215- stroke = " var(--bpmn-gw-stroke)"
216- stroke-width = " 1.9"
217- stroke-linecap = " round"
218- />
219- )}
220- </g >
221- )}
222-
223- { ! esTarea (n .type ) && ! esCompuerta (n .type ) && (
224- <g >
225- <circle
226- cx = { n .cx }
227- cy = { n .cy }
228- r = { n .w / 2 }
229- fill = " var(--bpmn-event-fill)"
230- stroke = { anilloEvento (n .type ).color }
231- stroke-width = { anilloEvento (n .type ).grosor }
232- />
233- { anilloEvento (n .type ).doble && (
234- <circle cx = { n .cx } cy = { n .cy } r = { n .w / 2 - 3.4 } fill = " none" stroke = " var(--bpmn-line)" stroke-width = " 1.4" />
235- )}
236- <g class = " bpmn-event-icon" transform = { ` translate(${n .cx } ${n .cy }) ` } >
237- { (n .type === ' messageStart' || n .type === ' intermediateEvent' ) && (
238- <g >
239- <rect x = " -6.5" y = " -4.5" width = " 13" height = " 9" rx = " 1" />
240- <path d = " M -6.1 -4.1 L 0 0.6 L 6.1 -4.1" />
241- </g >
242- )}
243- { n .type === ' timerStart' && (
244- <g >
245- <circle cx = " 0" cy = " 0" r = " 6.4" />
246- <path d = " M 0 -3.6 V 0 L 2.6 1.7" />
247- </g >
248- )}
249- { n .type === ' endEventError' && (
250- <path d = " M -5 5 L -1.4 -1.6 L 1.4 1.6 L 5 -5" style = " stroke: var(--bpmn-error)" />
251- )}
252- </g >
253- <text class = " bpmn-node-label bpmn-outside" x = { n .cx } y = { n .cy + n .h / 2 + 13 } text-anchor = " middle" >
254- { n .lines .map ((line , i ) => (
255- <tspan x = { n .cx } dy = { i === 0 ? 0 : 12 } >
256- { line }
257- </tspan >
258- ))}
259- </text >
260- </g >
261- )}
262- </g >
263- ))}
139+ { nodes .map ((n ) => <BpmnShape node = { n } />)}
264140 </g >
265141 </svg >
266142 </div >
267143</figure >
268144
269145<style >
270146 .bpmn {
271- --bpmn-lane-a: #13131a;
272- --bpmn-lane-b: #101017;
273- --bpmn-header: #171720;
274- --bpmn-border: #2e2e3a;
275- --bpmn-line: #a6a6b2;
276- --bpmn-msg: #85858f;
277- --bpmn-task-fill: #1c1c26;
278- --bpmn-task-stroke: #4d4d5d;
279- --bpmn-gw-fill: #1c1c26;
280- --bpmn-gw-stroke: #00b8c4;
281- --bpmn-event-fill: #13131a;
282- --bpmn-start: #c9ff5b;
283- --bpmn-end: #e7e7ec;
284- --bpmn-error: #ff6b3d;
285- --bpmn-label-bg: #13131a;
286-
287147 margin: 0;
288148 border: 1px solid rgb(255 255 255 / 0.14);
289149 border-radius: 0.75rem;
290150 background: var(--bpmn-lane-a);
291151 overflow: hidden;
292- /* Sombra hacia dentro en el borde derecho: insinúa que hay más diagrama
293- del que cabe, sin añadir una barra de scroll falsa. */
294- position: relative;
295152 }
296153
297154 .bpmn-scroll {
@@ -304,45 +161,4 @@ function anilloEvento(type: string) {
304161 svg {
305162 display: block;
306163 }
307-
308- .bpmn-node-label {
309- font-size: 11.5px;
310- fill: #e7e7ec;
311- font-weight: 450;
312- }
313-
314- .bpmn-outside {
315- font-size: 10.5px;
316- fill: #c4c4cc;
317- }
318-
319- .bpmn-lane-label {
320- font-size: 10.5px;
321- fill: #c4c4cc;
322- font-weight: 600;
323- letter-spacing: 0.06em;
324- text-transform: uppercase;
325- }
326-
327- .bpmn-flow-label {
328- font-size: 10px;
329- fill: #c4c4cc;
330- font-weight: 500;
331- }
332-
333- .bpmn-marker {
334- fill: none;
335- stroke: var(--bpmn-line);
336- stroke-width: 1.1;
337- stroke-linecap: round;
338- stroke-linejoin: round;
339- }
340-
341- .bpmn-event-icon {
342- fill: none;
343- stroke: var(--bpmn-line);
344- stroke-width: 1.3;
345- stroke-linecap: round;
346- stroke-linejoin: round;
347- }
348164</style >
0 commit comments