@bycrux/editor 0.4.2 → 0.4.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bycrux/editor",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -84,7 +84,7 @@ function SlideGrid({
84
84
  onDrop={() => handleDrop(idx)}
85
85
  onDragEnd={handleDragEnd}
86
86
  onClick={() => onSelect(slide.id)}
87
- className={`group relative cursor-pointer rounded overflow-hidden border transition-colors ${
87
+ className={`group relative flex-shrink-0 cursor-pointer rounded overflow-hidden border transition-colors ${
88
88
  selectedSlideId === slide.id
89
89
  ? 'border-[var(--editor-accent)]'
90
90
  : dragOverIdx === idx
@@ -357,16 +357,18 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
357
357
  obs.observe(el)
358
358
  return () => obs.disconnect()
359
359
  }, [])
360
- const PADDING = 48
361
- const HINT_RESERVE = 36
360
+ const PADDING = 32
361
+ const HINT_RESERVE = 28
362
362
  const availW = Math.max(0, canvasContainerSize.w - PADDING)
363
363
  const availH = Math.max(0, canvasContainerSize.h - PADDING - HINT_RESERVE)
364
364
  const canvasScale = Math.min(availW / w, availH / h, 1)
365
365
 
366
366
  return (
367
- <div ref={containerRef} className="flex flex-col h-full overflow-hidden bg-[var(--editor-bg)]">
368
- {/* TOP: slide rail + canvas, fills remaining height */}
369
- <div className="flex flex-1 min-h-0 overflow-hidden">
367
+ <div ref={containerRef} className="flex flex-col h-full overflow-y-auto bg-[var(--editor-bg)]">
368
+ {/* TOP: slide rail | canvas | editing panel (right). Given a generous
369
+ viewport-relative height so the slide renders large; the project-media
370
+ region flows beneath and the whole editor scrolls vertically. */}
371
+ <div className="flex flex-shrink-0 min-h-[62vh] overflow-hidden">
370
372
  <SlideGrid
371
373
  project={project}
372
374
  slides={slides}
@@ -380,7 +382,7 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
380
382
  compileOverlay={(t) => adapter.compileOverlay(t)}
381
383
  />
382
384
 
383
- <div ref={canvasContainerRef} className="relative flex-1 flex flex-col items-center justify-center gap-4 overflow-hidden p-6">
385
+ <div ref={canvasContainerRef} className="relative flex-1 flex flex-col items-center justify-center gap-4 overflow-hidden p-4">
384
386
  <button
385
387
  onClick={handleRefresh}
386
388
  disabled={refreshing}
@@ -491,52 +493,50 @@ export default function CarouselEditor<P extends Project = Project>({ project: i
491
493
  </div>
492
494
  )}
493
495
  </div>
494
- </div>
495
496
 
496
- {/* BELOW: the panels, full width under the canvas. Bounded height with
497
- internal scrolling so it never crushes the canvas above. */}
498
- <div className="flex-shrink-0 border-t border-[var(--editor-border)] bg-[var(--editor-bg)] overflow-hidden flex flex-col" style={{ maxHeight: '40%' }}>
499
- {selectedSlide && project.status !== 'pending' && (
500
- <div className="px-4 py-2 border-b border-[var(--editor-border)] bg-[var(--editor-bg)]">
501
- <AddElementMenu
502
- project={project}
503
- selectedSlideId={selectedSlideId}
504
- adapter={adapter}
505
- onAddElement={handleAddElement}
506
- />
507
- </div>
508
- )}
509
- <div className="flex flex-1 min-h-0 overflow-hidden">
510
- <div className="w-80 flex-shrink-0 overflow-y-auto border-r border-[var(--editor-border)]">
511
- <SlidePropertyPanel
512
- project={project}
513
- slide={selectedSlide}
514
- element={selectedElement}
515
- adapter={adapter}
516
- onSlideChange={handleSlideChange}
517
- onElementChange={handlePanelElementChange}
518
- onDeleteSlide={handleDeleteSlide}
519
- onDuplicateSlide={handleDuplicateSlide}
520
- onDeleteElement={handleDeleteElement}
521
- onDuplicateElement={handleDuplicateElement}
522
- onReorderElement={handleReorderElement}
523
- onEnterCrop={(_slideId, elementId) => { setSelectedElementId(elementId); setCropElementId(elementId) }}
524
- updateOverlayProp={state.updateOverlayProp}
525
- hiddenElementIds={hiddenElementIds}
526
- onToggleElementVisibility={onToggleElementVisibility}
527
- />
528
- </div>
529
- {slots?.assetsPanel && (
530
- // Below the canvas the assets slot fills the remaining width (no longer
531
- // capped to a 320px sidebar) and scrolls vertically within the bounded
532
- // below-canvas region.
533
- <div className="flex-1 min-w-0 flex flex-col overflow-y-auto overflow-x-hidden">
534
- {slots.assetsPanel}
497
+ {/* RIGHT: the slide editor (add-element toolbar + property panel),
498
+ beside the canvas with its own vertical scroll. */}
499
+ <div className="w-[24rem] flex-shrink-0 border-l border-[var(--editor-border)] flex flex-col overflow-y-auto bg-[var(--editor-bg)]">
500
+ {selectedSlide && project.status !== 'pending' && (
501
+ <div className="px-4 py-2 border-b border-[var(--editor-border)]">
502
+ <AddElementMenu
503
+ project={project}
504
+ selectedSlideId={selectedSlideId}
505
+ adapter={adapter}
506
+ onAddElement={handleAddElement}
507
+ />
535
508
  </div>
536
509
  )}
510
+ <SlidePropertyPanel
511
+ project={project}
512
+ slide={selectedSlide}
513
+ element={selectedElement}
514
+ adapter={adapter}
515
+ onSlideChange={handleSlideChange}
516
+ onElementChange={handlePanelElementChange}
517
+ onDeleteSlide={handleDeleteSlide}
518
+ onDuplicateSlide={handleDuplicateSlide}
519
+ onDeleteElement={handleDeleteElement}
520
+ onDuplicateElement={handleDuplicateElement}
521
+ onReorderElement={handleReorderElement}
522
+ onEnterCrop={(_slideId, elementId) => { setSelectedElementId(elementId); setCropElementId(elementId) }}
523
+ updateOverlayProp={state.updateOverlayProp}
524
+ hiddenElementIds={hiddenElementIds}
525
+ onToggleElementVisibility={onToggleElementVisibility}
526
+ // Fills the right column (drop the default w-80 width + left border).
527
+ className="w-full border-l-0"
528
+ />
537
529
  </div>
538
530
  </div>
539
531
 
532
+ {/* BELOW: Project media, full width at the bottom. Flows beneath the top
533
+ region and scrolls with the page (the root is overflow-y-auto). */}
534
+ {slots?.assetsPanel && (
535
+ <div className="flex-shrink-0 border-t border-[var(--editor-border)] w-full flex flex-col">
536
+ {slots.assetsPanel}
537
+ </div>
538
+ )}
539
+
540
540
  {renderOpen && (
541
541
  <CarouselRenderModal
542
542
  projectId={project.id}
@@ -9,7 +9,7 @@ import type {
9
9
  GlobalOverlayProp,
10
10
  EditorAdapter,
11
11
  } from '../types'
12
- import { Button } from '../ui'
12
+ import { Button, cn } from '../ui'
13
13
  import { TextFormattingToolbar } from '../text/TextFormattingToolbar'
14
14
 
15
15
  function parseNumber(v: string): number | null {
@@ -39,6 +39,10 @@ interface Props {
39
39
  // selected element; `hiddenElementIds` reflects the current hidden set.
40
40
  hiddenElementIds?: string[]
41
41
  onToggleElementVisibility?: (elementId: string) => void
42
+ // Override the panel's root container classes. Hosts that stack the panel
43
+ // full-width (e.g. below the canvas) pass this to drop the default `w-80`
44
+ // sidebar constraint.
45
+ className?: string
42
46
  }
43
47
 
44
48
  // Small eye toggle to hide/show the selected element in the editor preview only
@@ -179,6 +183,7 @@ export default function SlidePropertyPanel({
179
183
  adapter,
180
184
  hiddenElementIds,
181
185
  onToggleElementVisibility,
186
+ className,
182
187
  }: Props) {
183
188
  // Map of jsxPath → GlobalOverlay for overlay prop schemas
184
189
  const [overlaySchemas, setOverlaySchemas] = useState<Map<string, GlobalOverlay>>(new Map())
@@ -206,7 +211,7 @@ export default function SlidePropertyPanel({
206
211
 
207
212
  if (!slide) {
208
213
  return (
209
- <div className="w-80 flex-shrink-0 flex items-center justify-center text-[var(--editor-text)]/40 text-xs p-4">
214
+ <div className={cn('w-80 flex-shrink-0 flex items-center justify-center text-[var(--editor-text)]/40 text-xs p-4', className)}>
210
215
  Select a slide
211
216
  </div>
212
217
  )
@@ -216,7 +221,7 @@ export default function SlidePropertyPanel({
216
221
  const overlaySchema = overlayEl ? overlaySchemas.get(overlayEl.overlay.template) : null
217
222
 
218
223
  return (
219
- <div className="w-80 flex-shrink-0 border-l border-[var(--editor-border)] flex flex-col overflow-y-auto bg-[var(--editor-bg)]">
224
+ <div className={cn('w-80 flex-shrink-0 border-l border-[var(--editor-border)] flex flex-col overflow-y-auto bg-[var(--editor-bg)]', className)}>
220
225
  {/* Slide header */}
221
226
  <div className="px-4 py-3 border-b border-[var(--editor-border)]">
222
227
  <div className="text-xs font-semibold text-[var(--editor-text)]/60 uppercase tracking-wider mb-2">Slide</div>
@@ -97,11 +97,11 @@ describe('CarouselEditor — editor-core integration', () => {
97
97
  />,
98
98
  )
99
99
  await waitFor(() => getByTestId('assets'))
100
- // The assets slot now lives in the below-canvas region and fills the remaining
101
- // width (flex-1) rather than being capped to a 320px sidebar so a wide host
102
- // panel uses its full share of the row without crushing the canvas above.
100
+ // The assets slot lives in the below-canvas region, full-width at the very
101
+ // bottom (no longer capped to a 320px sidebar) so the host panel spans the
102
+ // editor width beneath the top canvas/editing region.
103
103
  const wrapper = getByTestId('assets').parentElement
104
- expect(wrapper?.className).toContain('flex-1')
104
+ expect(wrapper?.className).toContain('w-full')
105
105
  expect(wrapper?.className).not.toContain('w-80')
106
106
  })
107
107
 
package/src/ui/button.tsx CHANGED
@@ -7,10 +7,10 @@ const buttonVariants = cva(
7
7
  variants: {
8
8
  variant: {
9
9
  default: 'bg-[var(--editor-accent)] text-[var(--editor-accent-foreground)] hover:opacity-90',
10
- secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-[var(--editor-surface)] dark:text-[var(--editor-text)] dark:hover:opacity-90',
11
- ghost: 'text-gray-500 hover:bg-gray-100 hover:text-gray-900 dark:text-[var(--editor-text)]/60 dark:hover:bg-[var(--editor-surface)] dark:hover:text-[var(--editor-text)]',
10
+ secondary: 'bg-[var(--editor-surface)] text-[var(--editor-text)] border border-[var(--editor-border)] hover:border-[var(--editor-accent)]',
11
+ ghost: 'text-[var(--editor-text)] hover:bg-[var(--editor-surface)] hover:text-[var(--editor-text)]',
12
12
  danger: 'bg-red-600 text-white hover:bg-red-700',
13
- outline: 'border border-gray-300 dark:border-[var(--editor-border)] text-gray-700 dark:text-[var(--editor-text)] hover:bg-gray-100 dark:hover:bg-[var(--editor-surface)]',
13
+ outline: 'bg-[var(--editor-surface)] text-[var(--editor-text)] border border-[var(--editor-border)] hover:border-[var(--editor-accent)]',
14
14
  },
15
15
  size: {
16
16
  default: 'h-9 px-4 py-2',