@stubber/ui 1.1.1 → 1.3.0

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/README.md CHANGED
@@ -4,8 +4,8 @@ This is a sveltekit library project that contains standardized UI components to
4
4
 
5
5
  # Getting Started
6
6
 
7
- - `npm i`
8
- - `npm run storybook`
7
+ - `pnpm i`
8
+ - `pnpm run storybook`
9
9
 
10
10
  # Publishing
11
11
 
@@ -29,7 +29,7 @@
29
29
  <span slot="tooltip"> Copy stubberhandle </span>
30
30
  </Tooltip>
31
31
  <a class="mt-[17px] w-full" href={manageLink}>
32
- <PrimaryButton label="Manage Stubber" variant="secondary-transparent" width="full" />
32
+ <PrimaryButton label="Personal Settings" variant="secondary-transparent" width="full" />
33
33
  </a>
34
34
  </div>
35
35
  <a href={documentationLink}>
@@ -9,7 +9,7 @@
9
9
  import SideNavTitle from "./SideNavTitle.svelte";
10
10
  </script>
11
11
 
12
- <div class="p-4 bg-[#F0F6FC]">
12
+ <div class="p-4 bg-[#FFFFFF] border-r border-suface-100 h-full w-[250px]">
13
13
  <SideNavTitle title="General" />
14
14
  <SideNavItem label="Recent" href="/" icon="far fa-home" />
15
15
  <SideNavItem label="Archive" href="/" icon="far fa-archive" />
@@ -1,4 +1,6 @@
1
1
  <script>
2
+ import { Label } from "@stubber/ui/label";
3
+ import { Button } from "@stubber/ui/button";
2
4
  export let label;
3
5
  export let href;
4
6
  export let icon;
@@ -13,30 +15,33 @@
13
15
  }
14
16
  </script>
15
17
 
16
- <!-- If $$slots.default == true, then it means this SideNavItem is a group item, i.e it has child elements in <slots/> -->
18
+ <!-- If $slots.default == true, then it means this SideNavItem is a group item, i.e it has child elements in <slots/> -->
17
19
 
18
- <button
20
+ <Button
19
21
  on:click={handle_click}
20
- class="group h-10 my-2.5 w-full flex items-center {is_selected
21
- ? 'bg-white'
22
- : 'bg-transparent hover:bg-white hover:bg-opacity-[0.5]'} rounded-lg hover:bg active:bg-white"
22
+ variant="ghost"
23
+ class="group h-10 my-1 w-full flex items-center {is_selected
24
+ ? 'bg-surface-100'
25
+ : 'bg-transparent hover:bg-surface-100 hover:bg-opacity-[0.5]'} active:bg-surface-100"
23
26
  >
24
27
  {#if $$slots.default}
25
- <div class="h-10 pl-2 w-2.5 flex items-center justify-center">
28
+ <div class="h-10 w-2.5 flex items-center justify-center">
26
29
  <i class="fas fa-caret-right {is_expanded ? 'rotate down' : 'rotate'}" />
27
30
  </div>
28
31
  {/if}
29
- <a class="{$$slots.default ? '' : 'pl-2.5'} w-full flex items-center" {href}>
30
- <div class="text-surface-900 w-fit flex items-center">
32
+ <a class="{$$slots.default ? '' : ''} w-full flex items-center" {href}>
33
+ <div class="text-surface-900 w-fit flex items-center gap-2">
31
34
  <div class="h-10 aspect-square flex items-center justify-center">
32
- <i class={icon} />
35
+ <div class="bg-white rounded border w-7 h-7 flex items-center justify-center">
36
+ <i class="{icon} text-xs" />
37
+ </div>
33
38
  </div>
34
- <span class="text-button font-normal">
39
+ <Label class=" font-medium">
35
40
  {label}
36
- </span>
41
+ </Label>
37
42
  </div>
38
43
  </a>
39
- </button>
44
+ </Button>
40
45
  {#if $$slots.default && is_expanded}
41
46
  <div class={indent_children === true ? "ml-4" : ""}>
42
47
  <slot />
@@ -1,7 +1,9 @@
1
1
  <script>
2
+ import { Label } from "@stubber/ui/label";
3
+
2
4
  export let title;
3
5
  </script>
4
6
 
5
7
  <div class="h-10 w-full flex items-center">
6
- <h2 class="text-small font-normal text-surface-700">{title}</h2>
8
+ <Label class="ml-2 text-small font-medium text-surface-500">{title}</Label>
7
9
  </div>
@@ -0,0 +1,149 @@
1
+ <script>export let gridSize = 40;
2
+ export let lineWidth = 1;
3
+ export let lineColor = "#e2e8f0";
4
+ export let lineOpacity = 0.3;
5
+ export let highlightedCells = 0.05;
6
+ export let cellColor = "#f1f5f9";
7
+ export let cellOpacity = 0.3;
8
+ export let showGradientOverlay = false;
9
+ export let gradientOpacity = 0.7;
10
+ import { onMount } from "svelte";
11
+ let width;
12
+ let height;
13
+ let mounted = false;
14
+ let canvas;
15
+ let ctx;
16
+ let resizeTimeout = null;
17
+ let previousPositions = [];
18
+ let transitionProgress = 1;
19
+ let animationFrame = null;
20
+ let highlightedPositions = [];
21
+ function generateCells() {
22
+ if (!mounted || !width || !height) return;
23
+ const cols = Math.ceil(width / gridSize);
24
+ const rows = Math.ceil(height / gridSize);
25
+ const totalCells = cols * rows;
26
+ const highlightCount = Math.floor(totalCells * highlightedCells);
27
+ highlightedPositions = [];
28
+ const indices = /* @__PURE__ */ new Set();
29
+ while (indices.size < highlightCount) {
30
+ const randomCol = Math.floor(Math.random() * cols);
31
+ const randomRow = Math.floor(Math.random() * rows);
32
+ indices.add(`${randomCol}-${randomRow}`);
33
+ }
34
+ indices.forEach((idx) => {
35
+ const [col, row] = idx.split("-").map(Number);
36
+ highlightedPositions.push({
37
+ x: col * gridSize,
38
+ y: row * gridSize
39
+ });
40
+ });
41
+ renderGrid();
42
+ }
43
+ function renderGrid() {
44
+ if (!ctx || !width || !height) return;
45
+ ctx.clearRect(0, 0, width, height);
46
+ ctx.fillStyle = cellColor;
47
+ if (transitionProgress < 1 && previousPositions.length > 0) {
48
+ ctx.globalAlpha = (1 - transitionProgress) * cellOpacity;
49
+ previousPositions.forEach((pos) => {
50
+ ctx.fillRect(pos.x, pos.y, gridSize, gridSize);
51
+ });
52
+ }
53
+ ctx.globalAlpha = transitionProgress < 1 ? transitionProgress * cellOpacity : cellOpacity;
54
+ highlightedPositions.forEach((pos) => {
55
+ ctx.fillRect(pos.x, pos.y, gridSize, gridSize);
56
+ });
57
+ ctx.globalAlpha = lineOpacity;
58
+ ctx.strokeStyle = lineColor;
59
+ ctx.lineWidth = lineWidth;
60
+ const colCount = Math.ceil(width / gridSize) + 1;
61
+ ctx.beginPath();
62
+ for (let col = 0; col <= colCount; col++) {
63
+ const x = col * gridSize;
64
+ ctx.moveTo(x, 0);
65
+ ctx.lineTo(x, height);
66
+ }
67
+ const rowCount = Math.ceil(height / gridSize) + 1;
68
+ for (let row = 0; row <= rowCount; row++) {
69
+ const y = row * gridSize;
70
+ ctx.moveTo(0, y);
71
+ ctx.lineTo(width, y);
72
+ }
73
+ ctx.stroke();
74
+ if (showGradientOverlay) {
75
+ const gradient = ctx.createLinearGradient(0, 0, width, 0);
76
+ gradient.addColorStop(0, `rgba(255, 255, 255, ${gradientOpacity})`);
77
+ gradient.addColorStop(1, "rgba(255, 255, 255, 0.5)");
78
+ ctx.fillStyle = gradient;
79
+ ctx.globalAlpha = 1;
80
+ ctx.fillRect(0, 0, width, height);
81
+ }
82
+ }
83
+ function animateTransition() {
84
+ if (transitionProgress >= 1) {
85
+ transitionProgress = 1;
86
+ if (animationFrame !== null) {
87
+ cancelAnimationFrame(animationFrame);
88
+ animationFrame = null;
89
+ }
90
+ return;
91
+ }
92
+ transitionProgress += 0.05;
93
+ renderGrid();
94
+ animationFrame = requestAnimationFrame(animateTransition);
95
+ }
96
+ function handleResize() {
97
+ if (resizeTimeout) clearTimeout(resizeTimeout);
98
+ previousPositions = [...highlightedPositions];
99
+ resizeTimeout = setTimeout(() => {
100
+ if (canvas) {
101
+ canvas.width = width;
102
+ canvas.height = height;
103
+ transitionProgress = 0;
104
+ generateCells();
105
+ if (animationFrame !== null) {
106
+ cancelAnimationFrame(animationFrame);
107
+ }
108
+ animationFrame = requestAnimationFrame(animateTransition);
109
+ }
110
+ }, 250);
111
+ }
112
+ onMount(() => {
113
+ mounted = true;
114
+ if (canvas) {
115
+ ctx = canvas.getContext("2d");
116
+ if (ctx && width && height) {
117
+ const dpr = window.devicePixelRatio || 1;
118
+ canvas.width = width * dpr;
119
+ canvas.height = height * dpr;
120
+ ctx.scale(dpr, dpr);
121
+ generateCells();
122
+ }
123
+ window.addEventListener("resize", handleResize);
124
+ }
125
+ return () => {
126
+ if (resizeTimeout) clearTimeout(resizeTimeout);
127
+ if (animationFrame !== null) cancelAnimationFrame(animationFrame);
128
+ window.removeEventListener("resize", handleResize);
129
+ };
130
+ });
131
+ </script>
132
+
133
+ <div
134
+ class="grid-background"
135
+ bind:clientWidth={width}
136
+ bind:clientHeight={height}
137
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: -1;"
138
+ >
139
+ <canvas
140
+ bind:this={canvas}
141
+ style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
142
+ />
143
+ </div>
144
+
145
+ <style>
146
+ .grid-background {
147
+ pointer-events: none;
148
+ }
149
+ </style>
@@ -0,0 +1,26 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ gridSize?: number;
5
+ lineWidth?: number;
6
+ lineColor?: string;
7
+ lineOpacity?: number;
8
+ highlightedCells?: number;
9
+ cellColor?: string;
10
+ cellOpacity?: number;
11
+ showGradientOverlay?: boolean;
12
+ gradientOpacity?: number;
13
+ };
14
+ events: {
15
+ [evt: string]: CustomEvent<any>;
16
+ };
17
+ slots: {};
18
+ exports?: {} | undefined;
19
+ bindings?: string | undefined;
20
+ };
21
+ export type GridBackgroundProps = typeof __propDef.props;
22
+ export type GridBackgroundEvents = typeof __propDef.events;
23
+ export type GridBackgroundSlots = typeof __propDef.slots;
24
+ export default class GridBackground extends SvelteComponent<GridBackgroundProps, GridBackgroundEvents, GridBackgroundSlots> {
25
+ }
26
+ export {};
@@ -0,0 +1,2 @@
1
+ import Root from "./grid-background.svelte";
2
+ export { Root, Root as GridBackground, };
@@ -0,0 +1,4 @@
1
+ import Root from "./grid-background.svelte";
2
+ export { Root,
3
+ //
4
+ Root as GridBackground, };
@@ -11,4 +11,5 @@ import GridHead from "./range-calendar-grid-head.svelte";
11
11
  import HeadCell from "./range-calendar-head-cell.svelte";
12
12
  import NextButton from "./range-calendar-next-button.svelte";
13
13
  import PrevButton from "./range-calendar-prev-button.svelte";
14
- export { Day, Cell, Grid, Header, Months, GridRow, Heading, GridBody, GridHead, HeadCell, NextButton, PrevButton, Root as RangeCalendar, };
14
+ import Picker from "./range-calendar-picker.svelte";
15
+ export { Day, Cell, Grid, Header, Months, GridRow, Heading, GridBody, GridHead, HeadCell, NextButton, PrevButton, Root as RangeCalendar, Picker as RangeCalendarPicker, };
@@ -11,6 +11,7 @@ import GridHead from "./range-calendar-grid-head.svelte";
11
11
  import HeadCell from "./range-calendar-head-cell.svelte";
12
12
  import NextButton from "./range-calendar-next-button.svelte";
13
13
  import PrevButton from "./range-calendar-prev-button.svelte";
14
+ import Picker from "./range-calendar-picker.svelte";
14
15
  export { Day, Cell, Grid, Header, Months, GridRow, Heading, GridBody, GridHead, HeadCell, NextButton, PrevButton,
15
16
  //
16
- Root as RangeCalendar, };
17
+ Root as RangeCalendar, Picker as RangeCalendarPicker, };
@@ -0,0 +1,132 @@
1
+ <script>import { Button } from "../button/index";
2
+ import { RangeCalendar } from "./index";
3
+ import { RangeCalendar as RangeCalendarPrimitive } from "bits-ui";
4
+ import * as Popover from "../popover/index";
5
+ import * as Select from "../select/index";
6
+ import {
7
+ getLocalTimeZone,
8
+ isToday,
9
+ startOfMonth,
10
+ startOfWeek,
11
+ today
12
+ } from "@internationalized/date";
13
+ export let value = {
14
+ start: today(getLocalTimeZone()).subtract({ days: 7 }),
15
+ end: today(getLocalTimeZone())
16
+ };
17
+ let label = "";
18
+ $: update_label(value);
19
+ const update_label = (new_value) => {
20
+ if (!new_value || !new_value.start || !new_value.end) {
21
+ label = "Select a date range";
22
+ return;
23
+ }
24
+ const now = today(getLocalTimeZone());
25
+ const new_start = new_value.start;
26
+ const new_end = new_value.end;
27
+ if (!new_start || !new_end) {
28
+ label = "Select a date range";
29
+ return;
30
+ }
31
+ const ends_now = isToday(new_end, getLocalTimeZone());
32
+ const diffDays = new_end.compare(new_start);
33
+ const diffWeeks = Math.floor(diffDays / 7);
34
+ const diffMonths = (now.year - new_start.year) * 12 + (now.month - new_start.month);
35
+ if (ends_now && new_start.day === now.day && diffMonths >= 1 && new_start.day <= 28) {
36
+ label = diffMonths === 1 ? "1 month ago" : `${diffMonths} months ago`;
37
+ } else if (ends_now && diffWeeks >= 1 && diffDays % 7 === 0) {
38
+ label = diffWeeks === 1 ? "1 week ago" : `${diffWeeks} weeks ago`;
39
+ } else if (ends_now && diffDays >= 1) {
40
+ label = diffDays === 1 ? "1 day ago" : `${diffDays} days ago`;
41
+ } else {
42
+ label = format_range(new_start, new_end);
43
+ }
44
+ };
45
+ const format_range = (start, end) => {
46
+ const formatter = new Intl.DateTimeFormat("en-US", {
47
+ month: "short",
48
+ day: "numeric"
49
+ });
50
+ const startStr = formatter.format(start.toDate(getLocalTimeZone()));
51
+ const endStr = formatter.format(end.toDate(getLocalTimeZone()));
52
+ return `${startStr} - ${endStr}`;
53
+ };
54
+ const select_options = [
55
+ { value: "week-to-date", label: "Week to date" },
56
+ { value: "month-to-date", label: "Month to date" },
57
+ { value: "last-7-days", label: "Last 7 days" },
58
+ { value: "last-14-days", label: "Last 14 days" },
59
+ { value: "last-30-days", label: "Last 30 days" },
60
+ { value: "last-6-months", label: "Last 6 months" }
61
+ ];
62
+ const handle_select = (selected) => {
63
+ if (!selected) {
64
+ return;
65
+ }
66
+ switch (selected.value) {
67
+ case "week-to-date":
68
+ value = {
69
+ start: startOfWeek(today(getLocalTimeZone()), "en-GB"),
70
+ end: today(getLocalTimeZone())
71
+ };
72
+ break;
73
+ case "month-to-date":
74
+ value = {
75
+ start: startOfMonth(today(getLocalTimeZone())),
76
+ end: today(getLocalTimeZone())
77
+ };
78
+ break;
79
+ case "last-7-days":
80
+ value = {
81
+ start: today(getLocalTimeZone()).subtract({ days: 7 }),
82
+ end: today(getLocalTimeZone())
83
+ };
84
+ break;
85
+ case "last-14-days":
86
+ value = {
87
+ start: today(getLocalTimeZone()).subtract({ days: 14 }),
88
+ end: today(getLocalTimeZone())
89
+ };
90
+ break;
91
+ case "last-30-days":
92
+ value = {
93
+ start: today(getLocalTimeZone()).subtract({ days: 30 }),
94
+ end: today(getLocalTimeZone())
95
+ };
96
+ break;
97
+ case "last-6-months":
98
+ value = {
99
+ start: today(getLocalTimeZone()).subtract({ months: 6 }),
100
+ end: today(getLocalTimeZone())
101
+ };
102
+ break;
103
+ default:
104
+ console.warn("Unknown selection:", selected);
105
+ }
106
+ };
107
+ </script>
108
+
109
+ <Popover.Root>
110
+ <Popover.Trigger asChild let:builder>
111
+ <Button builders={[builder]} variant="outline">
112
+ <i class="far fa-calendar" aria-hidden="true"></i>
113
+ {label}
114
+ <i class="far fa-chevron-down" aria-hidden="true"></i>
115
+ </Button>
116
+ </Popover.Trigger>
117
+ <Popover.Content class="w-fit">
118
+ <Select.Root onSelectedChange={handle_select}>
119
+ <Select.Trigger class="">
120
+ <Select.Value placeholder="Select a date range" />
121
+ </Select.Trigger>
122
+ <Select.Content>
123
+ {#each select_options as option}
124
+ <Select.Item value={option.value} label={option.label}>
125
+ {option.label}
126
+ </Select.Item>
127
+ {/each}
128
+ </Select.Content>
129
+ </Select.Root>
130
+ <RangeCalendar class="px-0" bind:value {...$$restProps} />
131
+ </Popover.Content>
132
+ </Popover.Root>
@@ -0,0 +1,17 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import { RangeCalendar as RangeCalendarPrimitive } from "bits-ui";
3
+ declare const __propDef: {
4
+ props: RangeCalendarPrimitive.Props;
5
+ events: {
6
+ [evt: string]: CustomEvent<any>;
7
+ };
8
+ slots: {};
9
+ exports?: {} | undefined;
10
+ bindings?: string | undefined;
11
+ };
12
+ export type RangeCalendarPickerProps = typeof __propDef.props;
13
+ export type RangeCalendarPickerEvents = typeof __propDef.events;
14
+ export type RangeCalendarPickerSlots = typeof __propDef.slots;
15
+ export default class RangeCalendarPicker extends SvelteComponent<RangeCalendarPickerProps, RangeCalendarPickerEvents, RangeCalendarPickerSlots> {
16
+ }
17
+ export {};
@@ -6,8 +6,8 @@ export { className as class };
6
6
 
7
7
  <SelectPrimitive.Trigger
8
8
  class={cn(
9
- "bg-gradient-to-b from-[#2727271F] to-[#27272719] shadow-select ring-offset-background focus-visible:ring-ring aria-[invalid]:border-destructive data-[placeholder]:[&>span]:text-black flex h-7 w-full items-center justify-between rounded px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
10
- className
9
+ "border ring-offset-background focus-visible:ring-ring aria-[invalid]:border-destructive data-[placeholder]:[&>span]:text-muted-foreground flex h-7 w-full items-center justify-between rounded px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
10
+ className,
11
11
  )}
12
12
  {...$$restProps}
13
13
  let:builder
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stubber/ui",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && pnpm run package && node ./fix_dts_imports.js",