Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default [
document: 'readonly',
navigator: 'readonly',
console: 'readonly',

fetch: 'readonly',
URL: 'readonly',
caches: 'readonly',
Expand Down
44 changes: 44 additions & 0 deletions packages/ui/src/constants/zIndex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Z-Index Constants
*
* Centralized z-index values for overlay components to ensure consistent layering.
* All values are Tailwind CSS classes for compatibility with existing setup.
*
* Z-Index Tier System:
* - z-40: Tooltips, Popovers, Menus, Comboboxes (lowest overlay tier, non-blocking)
* - z-50: Dialogs, Drawers, Toasts (modal overlays and notifications)
* - z-100: Tour backdrops, System banners (high-priority overlays)
* - z-101: Tour spotlight (above tour backdrop)
* - z-102: Tour content (highest, above all other overlays)
*/

export const Z_INDEX = {
/** Tooltip and Popover - Lowest overlay tier for non-blocking hints */
TOOLTIP: 'z-40',
POPOVER: 'z-40',

/** Menu and Combobox - Dropdown menus and autocomplete */
MENU: 'z-40',
COMBOBOX: 'z-40',

/** Dialog and Drawer Backdrop - Modal backdrops */
BACKDROP: 'z-50',

/** Dialog and Drawer Content - Modal content */
DIALOG: 'z-50',

/** Toast - Toast notifications */
TOAST: 'z-50',

/** Tour Backdrop - Tour overlay backdrop */
TOUR_BACKDROP: 'z-100',

/** Tour Spotlight - Tour spotlight effect */
TOUR_SPOTLIGHT: 'z-101',

/** Tour Content - Tour content (highest overlay) */
TOUR_CONTENT: 'z-102',

/** Banner - System-level banners (e.g., ImpersonationBanner) */
BANNER: 'z-100',
Comment on lines +33 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify z-index precedence for TOUR_BACKDROP and BANNER.

Both TOUR_BACKDROP and BANNER are assigned z-100, which could cause ambiguous stacking behavior if both are rendered simultaneously. Consider whether:

  • The tour backdrop should always be above system banners (use z-101 for TOUR_BACKDROP and shift other tour layers up), or
  • System banners should always be above tour backdrops (reduce BANNER to z-90 or similar)

The intended stacking order should be clarified to avoid visual conflicts.

🤖 Prompt for AI Agents
In packages/ui/src/constants/zIndex.js around lines 33 to 43, TOUR_BACKDROP and
BANNER currently share 'z-100', causing ambiguous stacking; update the z-indexes
so tour layers are always above system banners: set BANNER to remain 'z-100',
bump TOUR_BACKDROP to 'z-101' and increment TOUR_SPOTLIGHT and TOUR_CONTENT one
step higher (e.g., 'z-102' and 'z-103') so the backdrop, spotlight, and content
maintain correct precedence without colliding with banners.

};
3 changes: 3 additions & 0 deletions packages/ui/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ export { useWindowDrag } from './primitives/useWindowDrag.js';

// Re-export utilities
export { cn } from './lib/cn.js';

// Re-export constants
export { Z_INDEX } from './constants/zIndex.js';
3 changes: 2 additions & 1 deletion packages/ui/src/zag/Combobox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Portal } from 'solid-js/web';
import { normalizeProps, useMachine } from '@zag-js/solid';
import { createMemo, createSignal, createUniqueId, For, Show, splitProps } from 'solid-js';
import { FiChevronDown, FiX, FiCheck } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Combobox - Searchable select with autocomplete
Expand Down Expand Up @@ -75,7 +76,7 @@ export function Combobox(props) {
<Show when={options().length > 0}>
<ul
{...api().getContentProps()}
class='z-50 max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white py-1 shadow-lg focus:outline-none'
class={`${Z_INDEX.COMBOBOX} max-h-60 overflow-auto rounded-lg border border-gray-200 bg-white py-1 shadow-lg focus:outline-none`}
>
<For each={options()}>
{item => (
Expand Down
9 changes: 5 additions & 4 deletions packages/ui/src/zag/Dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Portal } from 'solid-js/web';
import { useMachine, normalizeProps } from '@zag-js/solid';
import { createMemo, createSignal, createUniqueId, Show } from 'solid-js';
import { FiAlertTriangle, FiX } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Dialog - A generic dialog/modal component
Expand Down Expand Up @@ -51,12 +52,12 @@ export function Dialog(props) {
{/* Backdrop */}
<div
{...api().getBackdropProps()}
class='fixed inset-0 z-50 bg-black/50 transition-opacity'
class={`fixed inset-0 ${Z_INDEX.BACKDROP} bg-black/50 transition-opacity`}
/>
{/* Positioner - scrollable container */}
<div
{...api().getPositionerProps()}
class='fixed inset-0 z-50 flex items-center justify-center overflow-y-auto p-4'
class={`fixed inset-0 ${Z_INDEX.DIALOG} flex items-center justify-center overflow-y-auto p-4`}
>
{/* Content */}
<div
Expand Down Expand Up @@ -170,12 +171,12 @@ export function ConfirmDialog(props) {
{/* Backdrop */}
<div
{...api().getBackdropProps()}
class='fixed inset-0 z-50 bg-black/50 transition-opacity'
class={`fixed inset-0 ${Z_INDEX.BACKDROP} bg-black/50 transition-opacity`}
/>
{/* Positioner */}
<div
{...api().getPositionerProps()}
class='fixed inset-0 z-50 flex items-center justify-center p-4'
class={`fixed inset-0 ${Z_INDEX.DIALOG} flex items-center justify-center p-4`}
>
{/* Content */}
<div
Expand Down
8 changes: 6 additions & 2 deletions packages/ui/src/zag/Drawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Portal } from 'solid-js/web';
import { useMachine, normalizeProps } from '@zag-js/solid';
import { createMemo, createUniqueId, Show } from 'solid-js';
import { FiX } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Drawer - A slide-in panel component composed from Dialog
Expand Down Expand Up @@ -79,11 +80,14 @@ export function Drawer(props) {
<Show when={showBackdrop()}>
<div
{...api().getBackdropProps()}
class='animate-fade-in fixed inset-0 z-50 bg-black/50 transition-opacity'
class={`animate-fade-in fixed inset-0 ${Z_INDEX.BACKDROP} bg-black/50 transition-opacity`}
/>
</Show>
{/* Positioner - full height, aligned to side */}
<div {...api().getPositionerProps()} class={`fixed z-50 ${getPositionClasses()}`}>
<div
{...api().getPositionerProps()}
class={`fixed ${Z_INDEX.DIALOG} ${getPositionClasses()}`}
>
{/* Content - slides in from side */}
<div
{...api().getContentProps()}
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/zag/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as menu from '@zag-js/menu';
import { Portal } from 'solid-js/web';
import { normalizeProps, useMachine } from '@zag-js/solid';
import { createMemo, createUniqueId, Show, For, splitProps } from 'solid-js';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Menu - Dropdown menu for actions
Expand Down Expand Up @@ -51,7 +52,7 @@ export function Menu(props) {
<div {...api().getPositionerProps()}>
<ul
{...api().getContentProps()}
class={`z-50 min-w-40 rounded-lg border border-gray-200 bg-white py-1 shadow-lg focus:outline-none ${local.class || ''}`}
class={`${Z_INDEX.MENU} min-w-40 rounded-lg border border-gray-200 bg-white py-1 shadow-lg focus:outline-none ${local.class || ''}`}
>
<For each={local.items}>
{item => (
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/zag/Popover.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Portal } from 'solid-js/web';
import { normalizeProps, useMachine } from '@zag-js/solid';
import { createMemo, createUniqueId, Show, splitProps, mergeProps } from 'solid-js';
import { FiX } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Popover - Non-modal floating dialog
Expand Down Expand Up @@ -52,7 +53,7 @@ export function Popover(props) {
<div {...api().getPositionerProps()}>
<div
{...api().getContentProps()}
class={`z-50 max-w-sm min-w-50 rounded-lg border border-gray-200 bg-white shadow-lg ${local.class || ''}`}
class={`${Z_INDEX.POPOVER} max-w-sm min-w-50 rounded-lg border border-gray-200 bg-white shadow-lg ${local.class || ''}`}
>
<Show when={local.showArrow}>
<div {...api().getArrowProps()} class='[--arrow-background:white] [--arrow-size:8px]'>
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/zag/Toast.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as toast from '@zag-js/toast';
import { normalizeProps, useMachine, Key } from '@zag-js/solid';
import { createMemo, createUniqueId, Show } from 'solid-js';
import { FiX, FiCheck, FiAlertCircle, FiInfo, FiLoader } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

/**
* Create the toast store - this is the global toaster instance
Expand Down Expand Up @@ -116,7 +117,7 @@ export function Toaster() {
return (
<div
{...api().getGroupProps()}
class='pointer-events-none fixed inset-0 z-50 flex flex-col items-end p-4 sm:p-6'
class={`pointer-events-none fixed inset-0 ${Z_INDEX.TOAST} flex flex-col items-end p-4 sm:p-6`}
>
<Key each={api().getToasts()} by={t => t.id}>
{(toastItem, index) => <ToastItem toast={toastItem} parent={service} index={index} />}
Expand Down
25 changes: 15 additions & 10 deletions packages/ui/src/zag/Tooltip.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import * as tooltip from '@zag-js/tooltip';
import { normalizeProps, useMachine } from '@zag-js/solid';
import { createMemo, createUniqueId, Show } from 'solid-js';
import { Portal } from 'solid-js/web';
import { Z_INDEX } from '../constants/zIndex.js';

export function Tooltip(props) {
const service = useMachine(tooltip.machine, () => ({
id: createUniqueId(),
positioning: {
placement: props.placement || 'top',
gutter: 8,
strategy: 'fixed', // Use fixed positioning to avoid stacking context issues
},
openDelay: props.openDelay ?? 100,
closeDelay: props.closeDelay ?? 100,
Expand All @@ -20,17 +23,19 @@ export function Tooltip(props) {
<>
<span {...api().getTriggerProps()}>{props.children}</span>
<Show when={api().open}>
<div {...api().getPositionerProps()}>
<div {...api().getArrowProps()} class='[--arrow-background:#111827] [--arrow-size:8px]'>
<div {...api().getArrowTipProps()} />
<Portal>
<div {...api().getPositionerProps()} class={Z_INDEX.TOOLTIP}>
<div {...api().getArrowProps()} class='[--arrow-background:#111827] [--arrow-size:8px]'>
<div {...api().getArrowTipProps()} />
</div>
<div
{...api().getContentProps()}
class='pointer-events-none max-w-xs rounded bg-gray-900 px-2 py-1 text-xs text-white shadow-lg'
>
{props.content}
</div>
</div>
<div
{...api().getContentProps()}
class='pointer-events-none max-w-xs rounded bg-gray-900 px-2 py-1 text-xs text-white shadow-lg'
>
{props.content}
</div>
</div>
</Portal>
</Show>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</>
);
Expand Down
7 changes: 4 additions & 3 deletions packages/ui/src/zag/Tour.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
useContext,
} from 'solid-js';
import { FiX } from 'solid-icons/fi';
import { Z_INDEX } from '../constants/zIndex.js';

// Context for providing tour API to children
const TourContext = createContext();
Expand Down Expand Up @@ -75,17 +76,17 @@ export function TourProvider(props) {
<Show when={api().step.backdrop !== false}>
<div
{...api().getBackdropProps()}
class='fixed inset-0 z-100 bg-black/50 transition-opacity'
class={`fixed inset-0 ${Z_INDEX.TOUR_BACKDROP} bg-black/50 transition-opacity`}
/>
</Show>

<Show when={api().step.type === 'tooltip' && api().step.target}>
<div {...api().getSpotlightProps()} class='z-101' />
<div {...api().getSpotlightProps()} class={Z_INDEX.TOUR_SPOTLIGHT} />
</Show>

<div
{...api().getPositionerProps()}
class={`z-102 ${api().step.type === 'dialog' ? 'fixed inset-0 flex items-center justify-center p-4' : ''}`}
class={`${Z_INDEX.TOUR_CONTENT} ${api().step.type === 'dialog' ? 'fixed inset-0 flex items-center justify-center p-4' : ''}`}
>
<div
{...api().getContentProps()}
Expand Down
5 changes: 4 additions & 1 deletion packages/web/src/components/admin-ui/ImpersonationBanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
stopImpersonation,
checkImpersonationStatus,
} from '@/stores/adminStore.js';
import { Z_INDEX } from '@corates/ui';

export default function ImpersonationBanner() {
// Check impersonation status on mount
Expand All @@ -26,7 +27,9 @@ export default function ImpersonationBanner() {

return (
<Show when={isImpersonating()}>
<div class='fixed top-0 right-0 left-0 z-100 bg-orange-500 px-4 py-2 text-white'>
<div
class={`fixed top-0 right-0 left-0 ${Z_INDEX.BANNER} bg-orange-500 px-4 py-2 text-white`}
>
<div class='mx-auto flex max-w-7xl items-center justify-between'>
<div class='flex items-center space-x-2'>
<FiAlertTriangle class='h-5 w-5' />
Expand Down
5 changes: 3 additions & 2 deletions packages/web/src/components/charts/ChartSettingsModal.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Show, Index, createSignal } from 'solid-js';
import { BiRegularX } from 'solid-icons/bi';
import { Z_INDEX } from '@corates/ui';

/**
* ChartSettingsModal - Modal for editing chart settings like labels
Expand Down Expand Up @@ -42,11 +43,11 @@ export default function ChartSettingsModal(props) {
return (
<Show when={props.isOpen}>
{/* Backdrop - click to close */}
<div class='fixed inset-0 z-40 bg-black/50' />
<div class={`fixed inset-0 ${Z_INDEX.BACKDROP} bg-black/50`} />

{/* Modal */}
<div
class='fixed inset-0 z-50 flex items-center justify-center p-4'
class={`fixed inset-0 ${Z_INDEX.DIALOG} flex items-center justify-center p-4`}
onMouseDown={handleBackdropMouseDown}
onMouseUp={handleBackdropMouseUp}
>
Expand Down
29 changes: 21 additions & 8 deletions packages/web/src/components/checklist-ui/SplitScreenLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function SplitScreenLayout(props) {
>
{/* First panel */}
<div
class='overflow-auto'
class='overflow-auto transition-all duration-300 ease-in-out'
style={{
[layout() === 'vertical' ? 'width' : 'height']:
showSecondPanel() ? `${splitRatio()}%` : '100%',
Expand All @@ -113,21 +113,34 @@ export default function SplitScreenLayout(props) {
{firstPanel()}
</div>

{/* Divider / Resize handle */}
<Show when={showSecondPanel() && secondPanel()}>
{/* Divider / Resize handle with animation */}
<Show when={secondPanel()}>
<div
onMouseDown={handleMouseDown}
class={` ${layout() === 'vertical' ? 'w-1 cursor-col-resize' : 'h-1 cursor-row-resize'} shrink-0 bg-gray-200 transition-colors hover:bg-blue-400 active:bg-blue-500 ${isDragging() ? 'bg-blue-500' : ''} `}
class={`${layout() === 'vertical' ? 'w-1 cursor-col-resize' : 'h-1 cursor-row-resize'} shrink-0 bg-gray-200 transition-all duration-300 ease-in-out hover:bg-blue-400 active:bg-blue-500 ${
isDragging() ? 'bg-blue-500' : ''
} ${showSecondPanel() ? 'opacity-100' : 'pointer-events-none opacity-0'}`}
style={{
[layout() === 'vertical' ? 'width' : 'height']: showSecondPanel() ? undefined : '0',
}}
/>
</Show>

{/* Second panel */}
{/* Second panel with slide animation */}
<Show when={secondPanel()}>
<div
class='overflow-auto'
class='overflow-hidden transition-all duration-300 ease-in-out'
style={{
[layout() === 'vertical' ? 'width' : 'height']: `${100 - splitRatio()}%`,
[layout() === 'vertical' ? 'width' : 'height']:
showSecondPanel() ? `${100 - splitRatio()}%` : '0%',
opacity: showSecondPanel() ? 1 : 0,
transform:
showSecondPanel() ? 'translateX(0) translateY(0)'
: layout() === 'vertical' ? 'translateX(20px)'
: 'translateY(20px)',
}}
>
{secondPanel()}
<div class='h-full w-full overflow-auto'>{secondPanel()}</div>
</div>
</Show>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ export default function AnswerPanel(props) {
>
<div class={`p-4 ${props.isFinal ? 'bg-green-50/30' : ''}`}>
{/* Panel Header */}
<div class='mb-4 flex items-center justify-between'>
<div class={`${props.isFinal ? 'mb-0' : 'mb-4'} flex items-center justify-between`}>
<div>
<h3 class='font-semibold text-gray-900'>{props.title}</h3>
<h3 class='-mb-1 font-semibold text-gray-900'>{props.title}</h3>
<Show when={props.isFinal && props.selectedSource}>
<span class='text-xs text-gray-500'>
{props.selectedSource === 'custom' ?
Expand All @@ -127,7 +127,7 @@ export default function AnswerPanel(props) {
<div class='mb-4 flex flex-wrap items-center gap-2'>
<span class='text-xs text-gray-500'>Result:</span>
<span
class={`inline-flex items-center rounded-full border px-3 py-1.5 text-sm font-medium ${getAnswerBadgeStyle(props.finalAnswer)}`}
class={`inline-flex items-center rounded-full border px-2 py-1 text-xs font-medium ${getAnswerBadgeStyle(props.finalAnswer)}`}
>
{props.finalAnswer || 'Not selected'}
</span>
Expand Down
Loading