@lobb-js/studio 0.10.0 → 0.11.1

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.
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { EnumOption } from "@lobb-js/core";
3
+
4
+ interface Props {
5
+ value: any;
6
+ enum: EnumOption[] | string[];
7
+ }
8
+
9
+ const { value, enum: enumOptions }: Props = $props();
10
+
11
+ const levelClasses: Record<string, string> = {
12
+ success: "bg-green-100 text-green-700 border-green-200",
13
+ warning: "bg-yellow-100 text-yellow-700 border-yellow-300",
14
+ danger: "bg-red-100 text-red-700 border-red-200",
15
+ info: "bg-blue-100 text-blue-700 border-blue-200",
16
+ neutral: "bg-gray-100 text-gray-600 border-gray-200",
17
+ muted: "bg-gray-50 text-gray-400 border-gray-100",
18
+ };
19
+
20
+ const normalizedOptions = $derived(
21
+ (enumOptions as Array<string | EnumOption>).map((e) =>
22
+ typeof e === "string" ? { value: e, level: "neutral" as const } : e,
23
+ ),
24
+ );
25
+
26
+ const enumOption = $derived(normalizedOptions.find((e) => e.value === value));
27
+ </script>
28
+
29
+ {#if enumOption}
30
+ <span class="px-2 py-0.5 rounded-full text-xs font-medium border {levelClasses[enumOption.level]}">
31
+ {value}
32
+ </span>
33
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { EnumOption } from "@lobb-js/core";
2
+ interface Props {
3
+ value: any;
4
+ enum: EnumOption[] | string[];
5
+ }
6
+ declare const EnumBadge: import("svelte").Component<Props, {}, "">;
7
+ type EnumBadge = ReturnType<typeof EnumBadge>;
8
+ export default EnumBadge;
@@ -3,6 +3,7 @@
3
3
  import { ExternalLink } from "lucide-svelte";
4
4
  import UpdateDetailViewButton from "../detailView/update/updateDetailViewButton.svelte";
5
5
  import { getField } from "./utils";
6
+ import EnumBadge from "./enumBadge.svelte";
6
7
  import _ from "lodash";
7
8
  import { getStudioContext } from "../../context";
8
9
 
@@ -66,6 +67,8 @@
66
67
  {:else}
67
68
  <div class="text-muted-foreground">PARENT ID</div>
68
69
  {/if}
70
+ {:else if field?.enum}
71
+ <EnumBadge {value} enum={field.enum} />
69
72
  {:else if field.type === "datetime"}
70
73
  {@const date = new Date(value).toLocaleDateString()}
71
74
  {@const time = new Date(value).toLocaleTimeString()}
@@ -7,6 +7,8 @@
7
7
  import FieldCustomInput from "./fieldCustomInput.svelte";
8
8
  import Input from "../ui/input/input.svelte";
9
9
  import * as Select from "../../components/ui/select/index";
10
+ import EnumBadge from "../dataTable/enumBadge.svelte";
11
+ import type { EnumOption } from "@lobb-js/core";
10
12
  import Textarea from "../ui/textarea/textarea.svelte";
11
13
  import ForeingKeyInput from "../foreingKeyInput.svelte";
12
14
  import ExtensionsComponents from "../extensionsComponents.svelte";
@@ -91,6 +93,38 @@
91
93
  {destructive}
92
94
  />
93
95
  </ExtensionsComponents>
96
+ {:else if field.type === "string" && field.enum}
97
+ {@const rawEnum = field.enum as (string | EnumOption)[]}
98
+ {@const enumOptions = rawEnum.map((e): EnumOption => typeof e === "string" ? { value: e, level: "neutral" } : e)}
99
+ <Select.Root
100
+ type="single"
101
+ onValueChange={(newValue) => {
102
+ value = newValue;
103
+ }}
104
+ >
105
+ <Select.Trigger
106
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
107
+ class="
108
+ h-9 w-full bg-muted/30 pr-8
109
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
110
+ "
111
+ >
112
+ {#if value}
113
+ <EnumBadge {value} enum={enumOptions} />
114
+ {:else}
115
+ NULL
116
+ {/if}
117
+ </Select.Trigger>
118
+ <Select.Content>
119
+ <Select.Group>
120
+ {#each enumOptions as option}
121
+ <Select.Item value={option.value} label={option.value}>
122
+ <EnumBadge value={option.value} enum={enumOptions} />
123
+ </Select.Item>
124
+ {/each}
125
+ </Select.Group>
126
+ </Select.Content>
127
+ </Select.Root>
94
128
  {:else if field.type === "string"}
95
129
  <!-- if the string has a validator of type enum -->
96
130
  {#if field.validators && field.validators.enum}
@@ -2,6 +2,7 @@
2
2
  import { calculateDrawerWidth } from "../utils";
3
3
  import type { Snippet } from "svelte";
4
4
  import { fade, fly } from "svelte/transition";
5
+ import Portal from "svelte-portal";
5
6
 
6
7
  interface Props {
7
8
  children?: Snippet<[]>;
@@ -11,18 +12,20 @@
11
12
  let { onHide, children }: Props = $props();
12
13
  </script>
13
14
 
14
- <button
15
- transition:fade={{ duration: 250 }}
16
- onclick={() => onHide?.()}
17
- class="backgroundDrawerButton fixed left-0 top-0 z-30 h-screen w-screen bg-background opacity-50 cursor-default"
18
- aria-label="background used to hide the background"
19
- ></button>
15
+ <Portal target="body">
16
+ <button
17
+ transition:fade={{ duration: 250 }}
18
+ onclick={() => onHide?.()}
19
+ class="backgroundDrawerButton fixed left-0 top-0 z-40 h-screen w-screen bg-background opacity-50 cursor-default"
20
+ aria-label="background used to hide the background"
21
+ ></button>
20
22
 
21
- <!-- the drawer -->
22
- <div
23
- transition:fly={{ x: "100%", duration: 250 }}
24
- class="fixed right-0 top-0 z-30 flex h-full w-full flex-col border-l bg-background"
25
- style="max-width: {calculateDrawerWidth()}px;"
26
- >
27
- {@render children?.()}
28
- </div>
23
+ <!-- the drawer -->
24
+ <div
25
+ transition:fly={{ x: "100%", duration: 250 }}
26
+ class="fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-background"
27
+ style="max-width: {calculateDrawerWidth()}px;"
28
+ >
29
+ {@render children?.()}
30
+ </div>
31
+ </Portal>
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@lobb-js/studio",
3
- "version": "0.10.0",
3
+ "license": "UNLICENSED",
4
+ "version": "0.11.1",
4
5
  "type": "module",
5
6
  "publishConfig": {
6
7
  "access": "public"
@@ -41,6 +42,7 @@
41
42
  "postpublish": "./scripts/postpublish.sh"
42
43
  },
43
44
  "devDependencies": {
45
+ "@lobb-js/core": "^0.18.0",
44
46
  "@chromatic-com/storybook": "^4.1.2",
45
47
  "@storybook/addon-a11y": "^10.0.1",
46
48
  "@storybook/addon-docs": "^10.0.1",
@@ -106,6 +108,7 @@
106
108
  "mode-watcher": "^0.5.1",
107
109
  "mustache": "^4.2.0",
108
110
  "qs": "^6.14.1",
111
+ "svelte-portal": "^2.2.1",
109
112
  "svelte-sonner": "^0.3.28",
110
113
  "tailwind-merge": "^3.4.0",
111
114
  "tailwind-variants": "^3.2.2"
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { EnumOption } from "@lobb-js/core";
3
+
4
+ interface Props {
5
+ value: any;
6
+ enum: EnumOption[] | string[];
7
+ }
8
+
9
+ const { value, enum: enumOptions }: Props = $props();
10
+
11
+ const levelClasses: Record<string, string> = {
12
+ success: "bg-green-100 text-green-700 border-green-200",
13
+ warning: "bg-yellow-100 text-yellow-700 border-yellow-300",
14
+ danger: "bg-red-100 text-red-700 border-red-200",
15
+ info: "bg-blue-100 text-blue-700 border-blue-200",
16
+ neutral: "bg-gray-100 text-gray-600 border-gray-200",
17
+ muted: "bg-gray-50 text-gray-400 border-gray-100",
18
+ };
19
+
20
+ const normalizedOptions = $derived(
21
+ (enumOptions as Array<string | EnumOption>).map((e) =>
22
+ typeof e === "string" ? { value: e, level: "neutral" as const } : e,
23
+ ),
24
+ );
25
+
26
+ const enumOption = $derived(normalizedOptions.find((e) => e.value === value));
27
+ </script>
28
+
29
+ {#if enumOption}
30
+ <span class="px-2 py-0.5 rounded-full text-xs font-medium border {levelClasses[enumOption.level]}">
31
+ {value}
32
+ </span>
33
+ {/if}
@@ -3,6 +3,7 @@
3
3
  import { ExternalLink } from "lucide-svelte";
4
4
  import UpdateDetailViewButton from "../detailView/update/updateDetailViewButton.svelte";
5
5
  import { getField } from "./utils";
6
+ import EnumBadge from "./enumBadge.svelte";
6
7
  import _ from "lodash";
7
8
  import { getStudioContext } from "../../context";
8
9
 
@@ -66,6 +67,8 @@
66
67
  {:else}
67
68
  <div class="text-muted-foreground">PARENT ID</div>
68
69
  {/if}
70
+ {:else if field?.enum}
71
+ <EnumBadge {value} enum={field.enum} />
69
72
  {:else if field.type === "datetime"}
70
73
  {@const date = new Date(value).toLocaleDateString()}
71
74
  {@const time = new Date(value).toLocaleTimeString()}
@@ -7,6 +7,8 @@
7
7
  import FieldCustomInput from "./fieldCustomInput.svelte";
8
8
  import Input from "../ui/input/input.svelte";
9
9
  import * as Select from "../../components/ui/select/index";
10
+ import EnumBadge from "../dataTable/enumBadge.svelte";
11
+ import type { EnumOption } from "@lobb-js/core";
10
12
  import Textarea from "../ui/textarea/textarea.svelte";
11
13
  import ForeingKeyInput from "../foreingKeyInput.svelte";
12
14
  import ExtensionsComponents from "../extensionsComponents.svelte";
@@ -91,6 +93,38 @@
91
93
  {destructive}
92
94
  />
93
95
  </ExtensionsComponents>
96
+ {:else if field.type === "string" && field.enum}
97
+ {@const rawEnum = field.enum as (string | EnumOption)[]}
98
+ {@const enumOptions = rawEnum.map((e): EnumOption => typeof e === "string" ? { value: e, level: "neutral" } : e)}
99
+ <Select.Root
100
+ type="single"
101
+ onValueChange={(newValue) => {
102
+ value = newValue;
103
+ }}
104
+ >
105
+ <Select.Trigger
106
+ placeholder={ui?.placeholder ? ui.placeholder : "NULL"}
107
+ class="
108
+ h-9 w-full bg-muted/30 pr-8
109
+ {destructive ? 'border-destructive bg-destructive/10' : ''}
110
+ "
111
+ >
112
+ {#if value}
113
+ <EnumBadge {value} enum={enumOptions} />
114
+ {:else}
115
+ NULL
116
+ {/if}
117
+ </Select.Trigger>
118
+ <Select.Content>
119
+ <Select.Group>
120
+ {#each enumOptions as option}
121
+ <Select.Item value={option.value} label={option.value}>
122
+ <EnumBadge value={option.value} enum={enumOptions} />
123
+ </Select.Item>
124
+ {/each}
125
+ </Select.Group>
126
+ </Select.Content>
127
+ </Select.Root>
94
128
  {:else if field.type === "string"}
95
129
  <!-- if the string has a validator of type enum -->
96
130
  {#if field.validators && field.validators.enum}
@@ -2,6 +2,7 @@
2
2
  import { calculateDrawerWidth } from "../utils";
3
3
  import type { Snippet } from "svelte";
4
4
  import { fade, fly } from "svelte/transition";
5
+ import Portal from "svelte-portal";
5
6
 
6
7
  interface Props {
7
8
  children?: Snippet<[]>;
@@ -11,18 +12,20 @@
11
12
  let { onHide, children }: Props = $props();
12
13
  </script>
13
14
 
14
- <button
15
- transition:fade={{ duration: 250 }}
16
- onclick={() => onHide?.()}
17
- class="backgroundDrawerButton fixed left-0 top-0 z-30 h-screen w-screen bg-background opacity-50 cursor-default"
18
- aria-label="background used to hide the background"
19
- ></button>
15
+ <Portal target="body">
16
+ <button
17
+ transition:fade={{ duration: 250 }}
18
+ onclick={() => onHide?.()}
19
+ class="backgroundDrawerButton fixed left-0 top-0 z-40 h-screen w-screen bg-background opacity-50 cursor-default"
20
+ aria-label="background used to hide the background"
21
+ ></button>
20
22
 
21
- <!-- the drawer -->
22
- <div
23
- transition:fly={{ x: "100%", duration: 250 }}
24
- class="fixed right-0 top-0 z-30 flex h-full w-full flex-col border-l bg-background"
25
- style="max-width: {calculateDrawerWidth()}px;"
26
- >
27
- {@render children?.()}
28
- </div>
23
+ <!-- the drawer -->
24
+ <div
25
+ transition:fly={{ x: "100%", duration: 250 }}
26
+ class="fixed right-0 top-0 z-40 flex h-full w-full flex-col border-l bg-background"
27
+ style="max-width: {calculateDrawerWidth()}px;"
28
+ >
29
+ {@render children?.()}
30
+ </div>
31
+ </Portal>