🙃
This commit is contained in:
@@ -19,6 +19,7 @@ pub enum LeadErrCode {
|
|||||||
Syntax,
|
Syntax,
|
||||||
Server,
|
Server,
|
||||||
Unsupported,
|
Unsupported,
|
||||||
|
Invalid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::common::Literal;
|
|||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
use crate::parser::*;
|
use crate::parser::*;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::f64;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||||
@@ -117,6 +118,15 @@ fn evaluate_expr(
|
|||||||
Expr::Group(g) => evaluate_expr(g, precs, grid)?,
|
Expr::Group(g) => evaluate_expr(g, precs, grid)?,
|
||||||
Expr::Function { name, args } => match name.as_str() {
|
Expr::Function { name, args } => match name.as_str() {
|
||||||
"AVG" => eval_avg(args, precs, grid)?,
|
"AVG" => eval_avg(args, precs, grid)?,
|
||||||
|
"EXP" => eval_single_arg_numeric(args, precs, grid, |x| x.exp(), "EXP".into())?,
|
||||||
|
"SIN" => eval_single_arg_numeric(args, precs, grid, |x| x.sin(), "SIN".into())?,
|
||||||
|
"COS" => eval_single_arg_numeric(args, precs, grid, |x| x.cos(), "COS".into())?,
|
||||||
|
"TAN" => eval_single_arg_numeric(args, precs, grid, |x| x.tan(), "TAN".into())?,
|
||||||
|
"ASIN" => eval_single_arg_numeric(args, precs, grid, |x| x.asin(), "ASIN".into())?,
|
||||||
|
"ACOS" => eval_single_arg_numeric(args, precs, grid, |x| x.acos(), "ACOS".into())?,
|
||||||
|
"ATAN" => eval_single_arg_numeric(args, precs, grid, |x| x.atan(), "ATAN".into())?,
|
||||||
|
"PI" => eval_const(args, Eval::Literal(Literal::Number(f64::consts::PI)))?,
|
||||||
|
"TAU" => eval_const(args, Eval::Literal(Literal::Number(f64::consts::TAU)))?,
|
||||||
it => {
|
it => {
|
||||||
return Err(LeadErr {
|
return Err(LeadErr {
|
||||||
title: "Evaluation error.".into(),
|
title: "Evaluation error.".into(),
|
||||||
@@ -256,6 +266,49 @@ fn eval_avg(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eval_single_arg_numeric(
|
||||||
|
args: &Vec<Expr>,
|
||||||
|
precs: &mut HashSet<CellRef>,
|
||||||
|
grid: Option<&Grid>,
|
||||||
|
func: fn(f64) -> f64,
|
||||||
|
func_name: String,
|
||||||
|
) -> Result<Eval, LeadErr> {
|
||||||
|
if args.len() != 1 {
|
||||||
|
return Err(LeadErr {
|
||||||
|
title: "Evaluation error.".into(),
|
||||||
|
desc: format!("{func_name} function requires a single argument."),
|
||||||
|
code: LeadErrCode::Invalid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = LeadErr {
|
||||||
|
title: "Evaluation error.".into(),
|
||||||
|
desc: format!("{func_name} function requires a numeric argument."),
|
||||||
|
code: LeadErrCode::TypeErr,
|
||||||
|
};
|
||||||
|
|
||||||
|
match evaluate_expr(&args[0], precs, grid)? {
|
||||||
|
Eval::Literal(Literal::Number(num)) => Ok(Eval::Literal(Literal::Number(func(num)))),
|
||||||
|
Eval::CellRef { eval, .. } => match *eval {
|
||||||
|
Eval::Literal(Literal::Number(n)) => Ok(Eval::Literal(Literal::Number(func(n)))),
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
_ => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_const(args: &Vec<Expr>, value: Eval) -> Result<Eval, LeadErr> {
|
||||||
|
if args.len() != 0 {
|
||||||
|
return Err(LeadErr {
|
||||||
|
title: "Evaluation error.".into(),
|
||||||
|
desc: format!("PI function requires no arguments."),
|
||||||
|
code: LeadErrCode::Invalid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, LeadErr> {
|
fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, LeadErr> {
|
||||||
match (lval, rval) {
|
match (lval, rval) {
|
||||||
(Eval::Literal(a), Eval::Literal(b)) => {
|
(Eval::Literal(a), Eval::Literal(b)) => {
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msgs.len() == 1 {
|
||||||
|
let _ = write
|
||||||
|
.send(serde_json::to_string(&msgs.get(0)).unwrap().into())
|
||||||
|
.await;
|
||||||
|
} else if msgs.len() > 1 {
|
||||||
let msg = LeadMsg {
|
let msg = LeadMsg {
|
||||||
cell: None,
|
cell: None,
|
||||||
raw: None,
|
raw: None,
|
||||||
@@ -91,6 +96,7 @@ async fn accept_connection(stream: TcpStream) {
|
|||||||
.send(serde_json::to_string(&msg).unwrap().into())
|
.send(serde_json::to_string(&msg).unwrap().into())
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let res = LeadMsg {
|
let res = LeadMsg {
|
||||||
msg_type: MsgType::Error,
|
msg_type: MsgType::Error,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{cell::CellRef, evaluator::Eval};
|
use crate::{cell::CellRef, evaluator::Eval};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum MsgType {
|
pub enum MsgType {
|
||||||
Set,
|
Set,
|
||||||
@@ -11,7 +11,7 @@ pub enum MsgType {
|
|||||||
Bulk,
|
Bulk,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct LeadMsg {
|
pub struct LeadMsg {
|
||||||
pub msg_type: MsgType,
|
pub msg_type: MsgType,
|
||||||
pub cell: Option<CellRef>,
|
pub cell: Option<CellRef>,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { CellT } from './utils';
|
import { getErrDesc, getErrTitle, getEvalLiteral, isErr, type CellT } from './utils';
|
||||||
|
import * as HoverCard from '$lib/components/ui/hover-card/index.js';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
cla = '',
|
cla = '',
|
||||||
@@ -56,15 +57,30 @@
|
|||||||
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
||||||
onblur={(e) => {
|
onblur={(e) => {
|
||||||
cell = {
|
cell = {
|
||||||
isErr: false,
|
val: cell?.val,
|
||||||
val: undefined,
|
|
||||||
raw_val: (e.target as HTMLInputElement).value
|
raw_val: (e.target as HTMLInputElement).value
|
||||||
};
|
};
|
||||||
stopediting();
|
stopediting();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{:else if cell && isErr(cell.val)}
|
||||||
|
<HoverCard.Root openDelay={500} closeDelay={500}>
|
||||||
|
<HoverCard.Trigger>
|
||||||
|
{@render InnerCell()}
|
||||||
|
</HoverCard.Trigger>
|
||||||
|
<HoverCard.Content side="right">
|
||||||
|
<h2 class="text-md font-semibold tracking-tight transition-colors">
|
||||||
|
{getErrTitle(cell.val)}
|
||||||
|
</h2>
|
||||||
|
{getErrDesc(cell.val)}
|
||||||
|
</HoverCard.Content>
|
||||||
|
</HoverCard.Root>
|
||||||
{:else}
|
{:else}
|
||||||
|
{@render InnerCell()}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#snippet InnerCell()}
|
||||||
<div
|
<div
|
||||||
ondblclick={startediting}
|
ondblclick={startediting}
|
||||||
{onmousedown}
|
{onmousedown}
|
||||||
@@ -72,17 +88,17 @@
|
|||||||
style:height
|
style:height
|
||||||
class={clsx('placeholder bg-background p-1', { active }, cla)}
|
class={clsx('placeholder bg-background p-1', { active }, cla)}
|
||||||
>
|
>
|
||||||
{#if cell && (cell.raw_val !== '' || cell.val !== '')}
|
{#if cell && (cell.raw_val !== '' || getEvalLiteral(cell.val) !== '')}
|
||||||
<span class={clsx('pointer-events-none select-none', { err: cell.isErr })}>
|
<span class={clsx('pointer-events-none select-none', { err: isErr(cell.val) })}>
|
||||||
{#if cell.val}
|
{#if cell.val}
|
||||||
{cell.val}
|
{getEvalLiteral(cell.val)}
|
||||||
{:else}
|
{:else}
|
||||||
{cell.raw_val}
|
{cell.raw_val}
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/snippet}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.placeholder {
|
.placeholder {
|
||||||
@@ -101,6 +117,7 @@
|
|||||||
.active:has(.err),
|
.active:has(.err),
|
||||||
.placeholder:has(.err) {
|
.placeholder:has(.err) {
|
||||||
position: relative; /* needed for absolute positioning */
|
position: relative; /* needed for absolute positioning */
|
||||||
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active:has(.err)::after,
|
.active:has(.err)::after,
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { Pencil } from '@lucide/svelte';
|
||||||
import Cell from '$lib/components/grid/cell.svelte';
|
import Cell from '$lib/components/grid/cell.svelte';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import CellHeader from './cell-header.svelte';
|
import CellHeader from './cell-header.svelte';
|
||||||
import { colToStr, getEvalLiteral, isErr, refToStr, type CellT } from './utils';
|
import { colToStr, refToStr, type CellT } from './utils';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import { Input } from '../ui/input';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
socket,
|
socket,
|
||||||
@@ -44,7 +46,12 @@
|
|||||||
console.error('Expected cell value for SET msgponse from server.');
|
console.error('Expected cell value for SET msgponse from server.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCellVal(msg.cell.row, msg.cell.col, getEvalLiteral(msg.eval), isErr(msg.eval));
|
|
||||||
|
grid_vals[key(msg.cell.row, msg.cell.col)] = {
|
||||||
|
raw_val: msg.raw ?? '',
|
||||||
|
val: msg.eval
|
||||||
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'bulk': {
|
case 'bulk': {
|
||||||
@@ -108,35 +115,16 @@
|
|||||||
const key = (i: number, j: number) => `${i}:${j}`;
|
const key = (i: number, j: number) => `${i}:${j}`;
|
||||||
|
|
||||||
const getCell = (i: number, j: number) => grid_vals[key(i, j)];
|
const getCell = (i: number, j: number) => grid_vals[key(i, j)];
|
||||||
|
const setCell = (row: number, col: number, v: CellT) => {
|
||||||
const getCellRaw = (i: number, j: number) => getCell(i, j)?.raw_val ?? '';
|
if (v?.raw_val == null || v.raw_val === '') {
|
||||||
const setCellRaw = (i: number, j: number, val: string) => {
|
delete grid_vals[key(row, col)];
|
||||||
if (grid_vals[key(i, j)] === undefined) {
|
return;
|
||||||
grid_vals[key(i, j)] = {
|
|
||||||
raw_val: val,
|
|
||||||
isErr: false,
|
|
||||||
val: undefined
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
grid_vals[key(i, j)].raw_val = val;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
const getCellVal = (i: number, j: number) => getCell(i, j);
|
|
||||||
const setCellVal = (i: number, j: number, val: LiteralValue, isErr: boolean) => {
|
|
||||||
if (grid_vals[key(i, j)] === undefined) {
|
|
||||||
console.warn('Cell raw value was undefined but recieved eval.');
|
|
||||||
} else {
|
|
||||||
let cell = grid_vals[key(i, j)];
|
|
||||||
cell.val = val;
|
|
||||||
cell.isErr = isErr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCell = (row: number, col: number, v: CellT | undefined) => {
|
grid_vals[key(row, col)] = {
|
||||||
// ignore “no value” so we don’t create keys on mount
|
raw_val: v.raw_val,
|
||||||
if (v?.raw_val == null || v.raw_val === '') delete grid_vals[key(row, col)];
|
val: v.val
|
||||||
else {
|
};
|
||||||
setCellRaw(row, col, v.raw_val);
|
|
||||||
|
|
||||||
let msg: LeadMsg = {
|
let msg: LeadMsg = {
|
||||||
msg_type: 'set',
|
msg_type: 'set',
|
||||||
@@ -145,7 +133,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
socket.send(JSON.stringify(msg));
|
socket.send(JSON.stringify(msg));
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleCellInteraction(i: number, j: number, e: MouseEvent) {
|
function handleCellInteraction(i: number, j: number, e: MouseEvent) {
|
||||||
@@ -180,6 +167,33 @@
|
|||||||
active_cell = [i, j];
|
active_cell = [i, j];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getActiveCell(): CellT {
|
||||||
|
if (active_cell != null && grid_vals[key(active_cell[0], active_cell[1])])
|
||||||
|
return grid_vals[key(active_cell[0], active_cell[1])];
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
raw_val: '',
|
||||||
|
val: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setActiveCellRaw(raw: string): void {
|
||||||
|
if (active_cell == null) return;
|
||||||
|
|
||||||
|
if (grid_vals[key(active_cell[0], active_cell[1])]) {
|
||||||
|
let cell = grid_vals[key(active_cell[0], active_cell[1])];
|
||||||
|
setCell(active_cell[0], active_cell[1], {
|
||||||
|
raw_val: raw,
|
||||||
|
val: cell.val
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setCell(active_cell[0], active_cell[1], {
|
||||||
|
raw_val: raw,
|
||||||
|
val: undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const handler = (e: MouseEvent) => {
|
const handler = (e: MouseEvent) => {
|
||||||
// optional: check if click target is outside grid container
|
// optional: check if click target is outside grid container
|
||||||
@@ -192,6 +206,15 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="mb-5 ml-5 flex items-center gap-5">
|
||||||
|
<Pencil />
|
||||||
|
<Input
|
||||||
|
bind:value={() => getActiveCell().raw_val, (raw) => setActiveCellRaw(raw)}
|
||||||
|
class="relative w-[200px] rounded-none p-1 !transition-none delay-0 duration-0
|
||||||
|
focus:z-20 focus:shadow-[0_0_0_1px_var(--color-primary)] focus:outline-none"
|
||||||
|
></Input>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={clsx('grid-wrapper relative h-full min-h-0 max-w-full min-w-0 overflow-auto', className)}
|
class={clsx('grid-wrapper relative h-full min-h-0 max-w-full min-w-0 overflow-auto', className)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export interface CellT {
|
export interface CellT {
|
||||||
raw_val: string;
|
raw_val: string;
|
||||||
val: LiteralValue | undefined;
|
val?: Eval;
|
||||||
isErr: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,16 +43,38 @@ export function refToStr(row: number, col: number): string {
|
|||||||
return colToStr(col) + (row + 1).toString();
|
return colToStr(col) + (row + 1).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEvalLiteral(value: Eval): LiteralValue {
|
export function getEvalLiteral(value: Eval | undefined): LiteralValue {
|
||||||
|
if (value === undefined) return '';
|
||||||
if (value === 'unset') return '';
|
if (value === 'unset') return '';
|
||||||
if ('literal' in value) return value.literal.value;
|
if ('literal' in value) {
|
||||||
|
if (value.literal.value == null) return 'NaN';
|
||||||
|
return value.literal.value;
|
||||||
|
}
|
||||||
if ('cellref' in value) return getEvalLiteral(value.cellref.eval);
|
if ('cellref' in value) return getEvalLiteral(value.cellref.eval);
|
||||||
if ('err' in value) return `err: ${value.err.code}`;
|
if ('err' in value) return `#${value.err.code.toUpperCase()}`;
|
||||||
// if ('range' in value) return 'err';
|
// if ('range' in value) return 'err';
|
||||||
return 'todo!';
|
return 'todo!';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isErr(value: Eval): boolean {
|
export function isErr(value: Eval | undefined): boolean {
|
||||||
|
if (value === undefined) return false;
|
||||||
if (value === 'unset') return false;
|
if (value === 'unset') return false;
|
||||||
|
if ('cellref' in value) return isErr(value.cellref.eval);
|
||||||
return 'err' in value;
|
return 'err' in value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getErrTitle(value: Eval | undefined): string {
|
||||||
|
if (value === undefined) return '';
|
||||||
|
if (value === 'unset') return '';
|
||||||
|
if ('cellref' in value) return getErrTitle(value.cellref.eval);
|
||||||
|
if (!('err' in value)) return '';
|
||||||
|
return value.err.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getErrDesc(value: Eval | undefined): string {
|
||||||
|
if (value === undefined) return '';
|
||||||
|
if (value === 'unset') return '';
|
||||||
|
if ('cellref' in value) return getErrDesc(value.cellref.eval);
|
||||||
|
if (!('err' in value)) return '';
|
||||||
|
return value.err.desc;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { LinkPreview as HoverCardPrimitive } from 'bits-ui';
|
||||||
|
import { cn } from '$lib/utils.js';
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
align = 'center',
|
||||||
|
sideOffset = 4,
|
||||||
|
portalProps,
|
||||||
|
...restProps
|
||||||
|
}: HoverCardPrimitive.ContentProps & {
|
||||||
|
portalProps?: HoverCardPrimitive.PortalProps;
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<HoverCardPrimitive.Portal {...portalProps}>
|
||||||
|
<HoverCardPrimitive.Content
|
||||||
|
bind:ref
|
||||||
|
data-slot="hover-card-content"
|
||||||
|
{align}
|
||||||
|
{sideOffset}
|
||||||
|
class={cn(
|
||||||
|
'z-[200] mt-3 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
</HoverCardPrimitive.Portal>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), ...restProps }: HoverCardPrimitive.TriggerProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<HoverCardPrimitive.Trigger bind:ref data-slot="hover-card-trigger" {...restProps} />
|
||||||
14
frontend/src/lib/components/ui/hover-card/index.ts
Normal file
14
frontend/src/lib/components/ui/hover-card/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
|
||||||
|
import Content from "./hover-card-content.svelte";
|
||||||
|
import Trigger from "./hover-card-trigger.svelte";
|
||||||
|
|
||||||
|
const Root = HoverCardPrimitive.Root;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Trigger,
|
||||||
|
Root as HoverCard,
|
||||||
|
Content as HoverCardContent,
|
||||||
|
Trigger as HoverCardTrigger,
|
||||||
|
};
|
||||||
@@ -21,8 +21,10 @@
|
|||||||
<div class="absolute left-0 min-h-0 w-full">
|
<div class="absolute left-0 min-h-0 w-full">
|
||||||
<div class="flex h-[100vh] flex-col">
|
<div class="flex h-[100vh] flex-col">
|
||||||
<div class="h-[60px] w-full p-3">
|
<div class="h-[60px] w-full p-3">
|
||||||
|
<div class="flex items-center gap-5">
|
||||||
<Sidebar.Trigger />
|
<Sidebar.Trigger />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid-wrapper min-h-0 w-full flex-1">
|
<div class="grid-wrapper min-h-0 w-full flex-1">
|
||||||
<Grid class="h-full min-w-0" {socket} />
|
<Grid class="h-full min-w-0" {socket} />
|
||||||
@@ -32,6 +34,6 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.grid-wrapper {
|
.grid-wrapper {
|
||||||
border-top: 2px solid var(--color-input);
|
/* border-top: 2px solid var(--color-input); */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user