@gradio/tabs 0.5.0 → 0.5.2-dev.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @gradio/tabs
2
2
 
3
+ ## 0.5.2-dev.0
4
+
5
+ ### Dependency updates
6
+
7
+ - @gradio/utils@0.10.3-dev.0
8
+
9
+ ## 0.5.1
10
+
11
+ ### Fixes
12
+
13
+ - [#11784](https://github.com/gradio-app/gradio/pull/11784) [`d9dd3f5`](https://github.com/gradio-app/gradio/commit/d9dd3f54b7fb34cf7118e549d39fc63937ca3489) - Add "hidden" option to component's `visible` kwarg to render but visually hide the component. Thanks @pngwn!
14
+
3
15
  ## 0.5.0
4
16
 
5
17
  ### Features
package/Index.svelte CHANGED
@@ -3,50 +3,44 @@
3
3
  </script>
4
4
 
5
5
  <script lang="ts">
6
- import type { Gradio, SelectData } from "@gradio/utils";
7
- import { createEventDispatcher } from "svelte";
8
- import Tabs, { type Tab } from "./shared/Tabs.svelte";
6
+ import { Gradio } from "@gradio/utils";
7
+ import Tabs from "./shared/Tabs.svelte";
9
8
  import Walkthrough from "./shared/Walkthrough.svelte";
9
+ import type { TabsProps, TabsEvents } from "./types";
10
10
 
11
- const dispatch = createEventDispatcher();
11
+ let props = $props();
12
+ const gradio = new Gradio<TabsEvents, TabsProps>(props);
12
13
 
13
- export let visible = true;
14
- export let elem_id = "";
15
- export let elem_classes: string[] = [];
16
- export let selected: number | string;
17
- export let initial_tabs: Tab[] = [];
18
- export let name: "tabs" | "walkthrough" = "tabs";
19
- export let gradio:
20
- | Gradio<{
21
- change: never;
22
- select: SelectData;
23
- }>
24
- | undefined;
14
+ let old_selected = $state(gradio.props.selected);
25
15
 
26
- $: dispatch("prop_change", { selected });
16
+ $effect(() => {
17
+ if (old_selected !== gradio.props.selected) {
18
+ old_selected = gradio.props.selected;
19
+ }
20
+ });
27
21
  </script>
28
22
 
29
- {#if name === "walkthrough"}
23
+ {#if gradio.props.name === "walkthrough"}
30
24
  <Walkthrough
31
- {visible}
32
- {elem_id}
33
- {elem_classes}
34
- bind:selected
35
- on:change={() => gradio?.dispatch("change")}
36
- on:select={(e) => gradio?.dispatch("select", e.detail)}
37
- {initial_tabs}
25
+ visible={gradio.shared.visible}
26
+ elem_id={gradio.shared.elem_id}
27
+ elem_classes={gradio.shared.elem_classes}
28
+ bind:selected={gradio.props.selected}
29
+ on:change={() => gradio.dispatch("change")}
30
+ on:select={(e) => gradio.dispatch("select", e.detail)}
31
+ initial_tabs={gradio.props.initial_tabs}
38
32
  >
39
33
  <slot />
40
34
  </Walkthrough>
41
35
  {:else}
42
36
  <Tabs
43
- {visible}
44
- {elem_id}
45
- {elem_classes}
46
- bind:selected
47
- on:change={() => gradio?.dispatch("change")}
48
- on:select={(e) => gradio?.dispatch("select", e.detail)}
49
- {initial_tabs}
37
+ visible={gradio.shared.visible}
38
+ elem_id={gradio.shared.elem_id}
39
+ elem_classes={gradio.shared.elem_classes}
40
+ bind:selected={gradio.props.selected}
41
+ on:change={() => gradio.dispatch("change")}
42
+ on:select={(e) => gradio.dispatch("select", e.detail)}
43
+ initial_tabs={gradio.props.initial_tabs}
50
44
  >
51
45
  <slot />
52
46
  </Tabs>
package/dist/Index.svelte CHANGED
@@ -1,41 +1,46 @@
1
- <script context="module">export { default as BaseTabs, TABS } from "./shared/Tabs.svelte";
1
+ <script context="module" lang="ts">
2
+ export { default as BaseTabs, TABS, type Tab } from "./shared/Tabs.svelte";
2
3
  </script>
3
4
 
4
- <script>import { createEventDispatcher } from "svelte";
5
- import Tabs, {} from "./shared/Tabs.svelte";
6
- import Walkthrough from "./shared/Walkthrough.svelte";
7
- const dispatch = createEventDispatcher();
8
- export let visible = true;
9
- export let elem_id = "";
10
- export let elem_classes = [];
11
- export let selected;
12
- export let initial_tabs = [];
13
- export let name = "tabs";
14
- export let gradio;
15
- $: dispatch("prop_change", { selected });
5
+ <script lang="ts">
6
+ import { Gradio } from "@gradio/utils";
7
+ import Tabs from "./shared/Tabs.svelte";
8
+ import Walkthrough from "./shared/Walkthrough.svelte";
9
+ import type { TabsProps, TabsEvents } from "./types";
10
+
11
+ let props = $props();
12
+ const gradio = new Gradio<TabsEvents, TabsProps>(props);
13
+
14
+ let old_selected = $state(gradio.props.selected);
15
+
16
+ $effect(() => {
17
+ if (old_selected !== gradio.props.selected) {
18
+ old_selected = gradio.props.selected;
19
+ }
20
+ });
16
21
  </script>
17
22
 
18
- {#if name === "walkthrough"}
23
+ {#if gradio.props.name === "walkthrough"}
19
24
  <Walkthrough
20
- {visible}
21
- {elem_id}
22
- {elem_classes}
23
- bind:selected
24
- on:change={() => gradio?.dispatch("change")}
25
- on:select={(e) => gradio?.dispatch("select", e.detail)}
26
- {initial_tabs}
25
+ visible={gradio.shared.visible}
26
+ elem_id={gradio.shared.elem_id}
27
+ elem_classes={gradio.shared.elem_classes}
28
+ bind:selected={gradio.props.selected}
29
+ on:change={() => gradio.dispatch("change")}
30
+ on:select={(e) => gradio.dispatch("select", e.detail)}
31
+ initial_tabs={gradio.props.initial_tabs}
27
32
  >
28
33
  <slot />
29
34
  </Walkthrough>
30
35
  {:else}
31
36
  <Tabs
32
- {visible}
33
- {elem_id}
34
- {elem_classes}
35
- bind:selected
36
- on:change={() => gradio?.dispatch("change")}
37
- on:select={(e) => gradio?.dispatch("select", e.detail)}
38
- {initial_tabs}
37
+ visible={gradio.shared.visible}
38
+ elem_id={gradio.shared.elem_id}
39
+ elem_classes={gradio.shared.elem_classes}
40
+ bind:selected={gradio.props.selected}
41
+ on:change={() => gradio.dispatch("change")}
42
+ on:select={(e) => gradio.dispatch("select", e.detail)}
43
+ initial_tabs={gradio.props.initial_tabs}
39
44
  >
40
45
  <slot />
41
46
  </Tabs>
@@ -1,33 +1,21 @@
1
- import { SvelteComponent } from "svelte";
2
1
  export { default as BaseTabs, TABS, type Tab } from "./shared/Tabs.svelte";
3
- import type { Gradio, SelectData } from "@gradio/utils";
4
- import { type Tab } from "./shared/Tabs.svelte";
5
- declare const __propDef: {
6
- props: {
7
- visible?: boolean;
8
- elem_id?: string;
9
- elem_classes?: string[];
10
- selected: number | string;
11
- initial_tabs?: Tab[];
12
- name?: "tabs" | "walkthrough";
13
- gradio: Gradio<{
14
- change: never;
15
- select: SelectData;
16
- }> | undefined;
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
17
12
  };
18
- events: {
19
- prop_change: CustomEvent<any>;
20
- } & {
21
- [evt: string]: CustomEvent<any>;
22
- };
23
- slots: {
24
- default: {};
25
- };
26
- exports?: {} | undefined;
27
- bindings?: string | undefined;
28
- };
29
- export type IndexProps = typeof __propDef.props;
30
- export type IndexEvents = typeof __propDef.events;
31
- export type IndexSlots = typeof __propDef.slots;
32
- export default class Index extends SvelteComponent<IndexProps, IndexEvents, IndexSlots> {
13
+ z_$$bindings?: Bindings;
33
14
  }
15
+ declare const Index: $$__sveltets_2_IsomorphicComponent<any, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {
18
+ default: {};
19
+ }, {}, "">;
20
+ type Index = InstanceType<typeof Index>;
21
+ export default Index;
@@ -1,25 +1,26 @@
1
- /** @typedef {typeof __propDef.props} OverflowIconProps */
2
- /** @typedef {typeof __propDef.events} OverflowIconEvents */
3
- /** @typedef {typeof __propDef.slots} OverflowIconSlots */
4
- export default class OverflowIcon extends SvelteComponent<{
1
+ export default OverflowIcon;
2
+ type OverflowIcon = SvelteComponent<{
5
3
  [x: string]: never;
6
4
  }, {
7
5
  [evt: string]: CustomEvent<any>;
8
- }, {}> {
9
- }
10
- export type OverflowIconProps = typeof __propDef.props;
11
- export type OverflowIconEvents = typeof __propDef.events;
12
- export type OverflowIconSlots = typeof __propDef.slots;
13
- import { SvelteComponent } from "svelte";
14
- declare const __propDef: {
15
- props: {
16
- [x: string]: never;
17
- };
18
- events: {
19
- [evt: string]: CustomEvent<any>;
20
- };
21
- slots: {};
22
- exports?: undefined;
23
- bindings?: undefined;
6
+ }, {}> & {
7
+ $$bindings?: string | undefined;
24
8
  };
25
- export {};
9
+ declare const OverflowIcon: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
@@ -1,110 +1,164 @@
1
- <script context="module">export const TABS = {};
1
+ <script context="module" lang="ts">
2
+ export const TABS = {};
3
+
4
+ export interface Tab {
5
+ label: string;
6
+ id: string | number;
7
+ elem_id: string | undefined;
8
+ visible: boolean | "hidden";
9
+ interactive: boolean;
10
+ scale: number | null;
11
+ }
2
12
  </script>
3
13
 
4
- <script>import { setContext, createEventDispatcher, tick, onMount } from "svelte";
5
- import OverflowIcon from "./OverflowIcon.svelte";
6
- import { writable } from "svelte/store";
7
- export let visible = true;
8
- export let elem_id = "";
9
- export let elem_classes = [];
10
- export let selected;
11
- export let initial_tabs;
12
- let tabs = [...initial_tabs];
13
- let visible_tabs = [...initial_tabs];
14
- let overflow_tabs = [];
15
- let overflow_menu_open = false;
16
- let overflow_menu;
17
- $: has_tabs = tabs.length > 0;
18
- let tab_nav_el;
19
- const selected_tab = writable(
20
- selected || tabs[0]?.id || false
21
- );
22
- const selected_tab_index = writable(
23
- tabs.findIndex((t) => t?.id === selected) || 0
24
- );
25
- const dispatch = createEventDispatcher();
26
- let is_overflowing = false;
27
- let overflow_has_selected_tab = false;
28
- let tab_els = {};
29
- onMount(() => {
30
- if (!tab_nav_el) return;
31
- const observer = new IntersectionObserver((entries) => {
32
- handle_menu_overflow();
33
- });
34
- observer.observe(tab_nav_el);
35
- });
36
- setContext(TABS, {
37
- register_tab: (tab, order) => {
38
- tabs[order] = tab;
39
- if ($selected_tab === false && tab.visible && tab.interactive) {
40
- $selected_tab = tab.id;
41
- $selected_tab_index = order;
42
- }
43
- return order;
44
- },
45
- unregister_tab: (tab, order) => {
46
- if ($selected_tab === tab.id) {
47
- $selected_tab = tabs[0]?.id || false;
48
- }
49
- tabs[order] = null;
50
- },
51
- selected_tab,
52
- selected_tab_index
53
- });
54
- function change_tab(id) {
55
- const tab_to_activate = tabs.find((t) => t?.id === id);
56
- if (id !== void 0 && tab_to_activate && tab_to_activate.interactive && tab_to_activate.visible && $selected_tab !== tab_to_activate.id) {
57
- selected = id;
58
- $selected_tab = id;
59
- $selected_tab_index = tabs.findIndex((t) => t?.id === id);
60
- dispatch("change");
61
- overflow_menu_open = false;
62
- }
63
- }
64
- $: tabs, selected !== null && change_tab(selected);
65
- $: tabs, tab_nav_el, tab_els, handle_menu_overflow();
66
- function handle_outside_click(event) {
67
- if (overflow_menu_open && overflow_menu && !overflow_menu.contains(event.target)) {
68
- overflow_menu_open = false;
69
- }
70
- }
71
- async function handle_menu_overflow() {
72
- if (!tab_nav_el) return;
73
- await tick();
74
- const tab_nav_size = tab_nav_el.getBoundingClientRect();
75
- let max_width = tab_nav_size.width;
76
- const tab_sizes = get_tab_sizes(tabs, tab_els);
77
- let last_visible_index = 0;
78
- const offset = tab_nav_size.left;
79
- for (let i = tabs.length - 1; i >= 0; i--) {
80
- const tab = tabs[i];
81
- if (!tab) continue;
82
- const tab_rect = tab_sizes[tab.id];
83
- if (!tab_rect) continue;
84
- if (tab_rect.right - offset < max_width) {
85
- last_visible_index = i;
86
- break;
87
- }
88
- }
89
- overflow_tabs = tabs.slice(last_visible_index + 1);
90
- visible_tabs = tabs.slice(0, last_visible_index + 1);
91
- overflow_has_selected_tab = handle_overflow_has_selected_tab($selected_tab);
92
- is_overflowing = overflow_tabs.length > 0;
93
- }
94
- $: overflow_has_selected_tab = handle_overflow_has_selected_tab($selected_tab);
95
- function handle_overflow_has_selected_tab(selected_tab2) {
96
- if (selected_tab2 === false) return false;
97
- return overflow_tabs.some((t) => t?.id === selected_tab2);
98
- }
99
- function get_tab_sizes(tabs2, tab_els2) {
100
- const tab_sizes = {};
101
- tabs2.forEach((tab) => {
102
- if (!tab) return;
103
- tab_sizes[tab.id] = tab_els2[tab.id]?.getBoundingClientRect();
104
- });
105
- return tab_sizes;
106
- }
107
- $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
14
+ <script lang="ts">
15
+ import { setContext, createEventDispatcher, tick, onMount } from "svelte";
16
+ import OverflowIcon from "./OverflowIcon.svelte";
17
+ import { writable } from "svelte/store";
18
+ import type { SelectData } from "@gradio/utils";
19
+
20
+ export let visible: boolean | "hidden" = true;
21
+ export let elem_id = "";
22
+ export let elem_classes: string[] = [];
23
+ export let selected: number | string;
24
+ export let initial_tabs: Tab[];
25
+
26
+ let tabs: (Tab | null)[] = [...initial_tabs];
27
+ let visible_tabs: (Tab | null)[] = [...initial_tabs];
28
+ let overflow_tabs: (Tab | null)[] = [];
29
+ let overflow_menu_open = false;
30
+ let overflow_menu: HTMLElement;
31
+
32
+ $: has_tabs = tabs.length > 0;
33
+
34
+ let tab_nav_el: HTMLDivElement;
35
+
36
+ const selected_tab = writable<false | number | string>(
37
+ selected || tabs[0]?.id || false
38
+ );
39
+ const selected_tab_index = writable<number>(
40
+ tabs.findIndex((t) => t?.id === selected) || 0
41
+ );
42
+ const dispatch = createEventDispatcher<{
43
+ change: undefined;
44
+ select: SelectData;
45
+ }>();
46
+
47
+ let is_overflowing = false;
48
+ let overflow_has_selected_tab = false;
49
+ let tab_els: Record<string | number, HTMLElement> = {};
50
+
51
+ onMount(() => {
52
+ if (!tab_nav_el) return;
53
+ const observer = new IntersectionObserver((entries) => {
54
+ handle_menu_overflow();
55
+ });
56
+ observer.observe(tab_nav_el);
57
+ });
58
+
59
+ setContext(TABS, {
60
+ register_tab: (tab: Tab, order: number) => {
61
+ tabs[order] = tab;
62
+
63
+ if ($selected_tab === false && tab.visible !== false && tab.interactive) {
64
+ $selected_tab = tab.id;
65
+ $selected_tab_index = order;
66
+ }
67
+ return order;
68
+ },
69
+ unregister_tab: (tab: Tab, order: number) => {
70
+ if ($selected_tab === tab.id) {
71
+ $selected_tab = tabs[0]?.id || false;
72
+ }
73
+ tabs[order] = null;
74
+ },
75
+ selected_tab,
76
+ selected_tab_index
77
+ });
78
+
79
+ function change_tab(id: string | number | undefined): void {
80
+ const tab_to_activate = tabs.find((t) => t?.id === id);
81
+ if (
82
+ id !== undefined &&
83
+ tab_to_activate &&
84
+ tab_to_activate.interactive &&
85
+ tab_to_activate.visible !== false &&
86
+ $selected_tab !== tab_to_activate.id
87
+ ) {
88
+ selected = id;
89
+ $selected_tab = id;
90
+ $selected_tab_index = tabs.findIndex((t) => t?.id === id);
91
+ dispatch("change");
92
+ overflow_menu_open = false;
93
+ }
94
+ }
95
+
96
+ $: (tabs, selected !== null && change_tab(selected));
97
+ $: (tabs, tab_nav_el, tab_els, handle_menu_overflow());
98
+
99
+ function handle_outside_click(event: MouseEvent): void {
100
+ if (
101
+ overflow_menu_open &&
102
+ overflow_menu &&
103
+ !overflow_menu.contains(event.target as Node)
104
+ ) {
105
+ overflow_menu_open = false;
106
+ }
107
+ }
108
+
109
+ async function handle_menu_overflow(): Promise<void> {
110
+ if (!tab_nav_el) return;
111
+
112
+ await tick();
113
+ const tab_nav_size = tab_nav_el.getBoundingClientRect();
114
+
115
+ let max_width = tab_nav_size.width;
116
+ const tab_sizes = get_tab_sizes(tabs, tab_els);
117
+ let last_visible_index = 0;
118
+ const offset = tab_nav_size.left;
119
+
120
+ for (let i = tabs.length - 1; i >= 0; i--) {
121
+ const tab = tabs[i];
122
+ if (!tab) continue;
123
+ const tab_rect = tab_sizes[tab.id];
124
+ if (!tab_rect) continue;
125
+ if (tab_rect.right - offset < max_width) {
126
+ last_visible_index = i;
127
+ break;
128
+ }
129
+ }
130
+
131
+ overflow_tabs = tabs.slice(last_visible_index + 1);
132
+ visible_tabs = tabs.slice(0, last_visible_index + 1);
133
+
134
+ overflow_has_selected_tab = handle_overflow_has_selected_tab($selected_tab);
135
+ is_overflowing = overflow_tabs.length > 0;
136
+ }
137
+
138
+ $: overflow_has_selected_tab =
139
+ handle_overflow_has_selected_tab($selected_tab);
140
+
141
+ function handle_overflow_has_selected_tab(
142
+ selected_tab: number | string | false
143
+ ): boolean {
144
+ if (selected_tab === false) return false;
145
+ return overflow_tabs.some((t) => t?.id === selected_tab);
146
+ }
147
+
148
+ function get_tab_sizes(
149
+ tabs: (Tab | null)[],
150
+ tab_els: Record<string | number, HTMLElement>
151
+ ): Record<string | number, DOMRect> {
152
+ const tab_sizes: Record<string | number, DOMRect> = {};
153
+ tabs.forEach((tab) => {
154
+ if (!tab) return;
155
+ tab_sizes[tab.id] = tab_els[tab.id]?.getBoundingClientRect();
156
+ });
157
+ return tab_sizes;
158
+ }
159
+
160
+ $: tab_scale =
161
+ tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
108
162
  </script>
109
163
 
110
164
  <svelte:window
@@ -114,7 +168,8 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
114
168
 
115
169
  <div
116
170
  class="tabs {elem_classes.join(' ')}"
117
- class:hide={!visible}
171
+ class:hide={visible === false}
172
+ class:hidden={visible === "hidden"}
118
173
  id={elem_id}
119
174
  style:flex-grow={tab_scale}
120
175
  >
@@ -122,7 +177,7 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
122
177
  <div class="tab-wrapper">
123
178
  <div class="tab-container visually-hidden" aria-hidden="true">
124
179
  {#each tabs as t, i}
125
- {#if t?.visible}
180
+ {#if t && t?.visible !== false && t?.visible !== "hidden"}
126
181
  <button bind:this={tab_els[t.id]}>
127
182
  {t?.label}
128
183
  </button>
@@ -131,7 +186,7 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
131
186
  </div>
132
187
  <div class="tab-container" bind:this={tab_nav_el} role="tablist">
133
188
  {#each visible_tabs as t, i}
134
- {#if t?.visible}
189
+ {#if t && t?.visible !== false}
135
190
  <button
136
191
  role="tab"
137
192
  class:selected={t.id === $selected_tab}
@@ -144,7 +199,7 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
144
199
  on:click={() => {
145
200
  if (t.id !== $selected_tab) {
146
201
  change_tab(t.id);
147
- dispatch("select", { value: t.label, index: i });
202
+ dispatch("select", { value: t.label, index: i, id: t.id });
148
203
  }
149
204
  }}
150
205
  >
@@ -155,7 +210,8 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
155
210
  </div>
156
211
  <span
157
212
  class="overflow-menu"
158
- class:hide={!is_overflowing || !overflow_tabs.some((t) => t?.visible)}
213
+ class:hide={!is_overflowing ||
214
+ !overflow_tabs.some((t) => t?.visible !== false)}
159
215
  bind:this={overflow_menu}
160
216
  >
161
217
  <button
@@ -167,7 +223,7 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
167
223
  </button>
168
224
  <div class="overflow-dropdown" class:hide={!overflow_menu_open}>
169
225
  {#each overflow_tabs as t}
170
- {#if t?.visible}
226
+ {#if t?.visible !== false}
171
227
  <button
172
228
  on:click={() => change_tab(t?.id)}
173
229
  class:selected={t?.id === $selected_tab}
@@ -195,6 +251,10 @@ $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
195
251
  display: none;
196
252
  }
197
253
 
254
+ .hidden {
255
+ display: none !important;
256
+ }
257
+
198
258
  .tab-wrapper {
199
259
  display: flex;
200
260
  align-items: center;
@@ -1,37 +1,46 @@
1
- import { SvelteComponent } from "svelte";
2
1
  export declare const TABS: {};
3
2
  export interface Tab {
4
3
  label: string;
5
4
  id: string | number;
6
5
  elem_id: string | undefined;
7
- visible: boolean;
6
+ visible: boolean | "hidden";
8
7
  interactive: boolean;
9
8
  scale: number | null;
10
9
  }
11
10
  import type { SelectData } from "@gradio/utils";
12
- declare const __propDef: {
13
- props: {
14
- visible?: boolean;
15
- elem_id?: string;
16
- elem_classes?: string[];
17
- selected: number | string;
18
- initial_tabs: Tab[];
11
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
12
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
13
+ $$bindings?: Bindings;
14
+ } & Exports;
15
+ (internal: unknown, props: Props & {
16
+ $$events?: Events;
17
+ $$slots?: Slots;
18
+ }): Exports & {
19
+ $set?: any;
20
+ $on?: any;
19
21
  };
20
- events: {
21
- change: CustomEvent<undefined>;
22
- select: CustomEvent<SelectData>;
23
- } & {
24
- [evt: string]: CustomEvent<any>;
25
- };
26
- slots: {
27
- default: {};
28
- };
29
- exports?: {} | undefined;
30
- bindings?: string | undefined;
31
- };
32
- export type TabsProps = typeof __propDef.props;
33
- export type TabsEvents = typeof __propDef.events;
34
- export type TabsSlots = typeof __propDef.slots;
35
- export default class Tabs extends SvelteComponent<TabsProps, TabsEvents, TabsSlots> {
22
+ z_$$bindings?: Bindings;
36
23
  }
37
- export {};
24
+ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
25
+ default: any;
26
+ } ? Props extends Record<string, never> ? any : {
27
+ children?: any;
28
+ } : {});
29
+ declare const Tabs: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
30
+ visible?: boolean | "hidden";
31
+ elem_id?: string;
32
+ elem_classes?: string[];
33
+ selected: number | string;
34
+ initial_tabs: Tab[];
35
+ }, {
36
+ default: {};
37
+ }>, {
38
+ change: CustomEvent<undefined>;
39
+ select: CustomEvent<SelectData>;
40
+ } & {
41
+ [evt: string]: CustomEvent<any>;
42
+ }, {
43
+ default: {};
44
+ }, {}, string>;
45
+ type Tabs = InstanceType<typeof Tabs>;
46
+ export default Tabs;
@@ -1,115 +1,154 @@
1
- <script context="module">import { TABS } from "./Tabs.svelte";
1
+ <script context="module" lang="ts">
2
+ import { TABS, type Tab } from "./Tabs.svelte";
2
3
  </script>
3
4
 
4
- <script>import { setContext, createEventDispatcher, tick, onMount } from "svelte";
5
- import { writable } from "svelte/store";
6
- export let visible = true;
7
- export let elem_id = "";
8
- export let elem_classes = [];
9
- export let selected;
10
- export let initial_tabs;
11
- let tabs = [...initial_tabs];
12
- let stepper_container;
13
- let show_labels_for_all = true;
14
- let measurement_container;
15
- let step_buttons = [];
16
- let step_labels = [];
17
- let label_height = 0;
18
- let compact = false;
19
- let recompute_overflow = true;
20
- $: has_tabs = tabs.length > 0;
21
- const selected_tab = writable(
22
- selected || tabs[0]?.id || false
23
- );
24
- const selected_tab_index = writable(
25
- tabs.findIndex((t) => t?.id === selected) || 0
26
- );
27
- const dispatch = createEventDispatcher();
28
- async function check_overflow() {
29
- if (!stepper_container || !measurement_container || !recompute_overflow)
30
- return;
31
- recompute_overflow = false;
32
- await tick();
33
- show_labels_for_all = true;
34
- await tick();
35
- const SEP_WIDTH = 50;
36
- const button_width = step_buttons[0].getBoundingClientRect().width * step_buttons.length + SEP_WIDTH * (step_buttons.length - 1);
37
- const containerWidth = stepper_container.getBoundingClientRect().width;
38
- const does_it_fit = button_width < containerWidth;
39
- if (!does_it_fit) {
40
- show_labels_for_all = false;
41
- compact = true;
42
- return;
43
- }
44
- let max_height = 0;
45
- let is_overlapping = false;
46
- let last_right = 0;
47
- for (const label of step_labels) {
48
- const { height, width, left, right } = label.getBoundingClientRect();
49
- if (height > max_height) {
50
- max_height = height;
51
- }
52
- if (last_right && left - 10 < last_right && !is_overlapping) {
53
- is_overlapping = true;
54
- }
55
- last_right = right;
56
- }
57
- label_height = max_height;
58
- if (is_overlapping) {
59
- show_labels_for_all = false;
60
- }
61
- }
62
- let last_width = 0;
63
- onMount(() => {
64
- check_overflow();
65
- const observer = new ResizeObserver((entries) => {
66
- if (entries[0].contentRect.width === last_width) return;
67
- last_width = entries[0].contentRect.width;
68
- compact = false;
69
- recompute_overflow = true;
70
- check_overflow();
71
- });
72
- if (stepper_container) {
73
- observer.observe(stepper_container);
74
- }
75
- return () => {
76
- observer.disconnect();
77
- };
78
- });
79
- setContext(TABS, {
80
- register_tab: (tab, order) => {
81
- tabs[order] = tab;
82
- if ($selected_tab === false && tab.visible && tab.interactive) {
83
- $selected_tab = tab.id;
84
- $selected_tab_index = order;
85
- }
86
- return order;
87
- },
88
- unregister_tab: (tab, order) => {
89
- if ($selected_tab === tab.id) {
90
- $selected_tab = tabs[0]?.id || false;
91
- }
92
- tabs[order] = null;
93
- },
94
- selected_tab,
95
- selected_tab_index
96
- });
97
- function change_tab(id, index) {
98
- const tab_to_activate = tabs.find((t) => t?.id === id);
99
- if (id !== void 0 && tab_to_activate && tab_to_activate.interactive && tab_to_activate.visible && $selected_tab !== tab_to_activate.id) {
100
- selected = id;
101
- $selected_tab = id;
102
- $selected_tab_index = tabs.findIndex((t) => t?.id === id);
103
- dispatch("change");
104
- }
105
- }
106
- $: tabs, selected !== null && change_tab(
107
- selected,
108
- tabs.findIndex((t) => t?.id === selected)
109
- );
110
- $: tabs, check_overflow();
111
- $: $selected_tab_index, check_overflow();
112
- $: tab_scale = tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
5
+ <script lang="ts">
6
+ import { setContext, createEventDispatcher, tick, onMount } from "svelte";
7
+ import { writable } from "svelte/store";
8
+ import type { SelectData } from "@gradio/utils";
9
+
10
+ export let visible: boolean | "hidden" = true;
11
+ export let elem_id = "";
12
+ export let elem_classes: string[] = [];
13
+ export let selected: number | string;
14
+ export let initial_tabs: Tab[];
15
+
16
+ let tabs: (Tab | null)[] = [...initial_tabs];
17
+ let stepper_container: HTMLDivElement;
18
+ let show_labels_for_all = true;
19
+ let measurement_container: HTMLDivElement;
20
+ let step_buttons: HTMLButtonElement[] = [];
21
+ let step_labels: HTMLSpanElement[] = [];
22
+ let label_height = 0;
23
+ let compact = false;
24
+ let recompute_overflow = true;
25
+ $: has_tabs = tabs.length > 0;
26
+
27
+ const selected_tab = writable<false | number | string>(
28
+ selected || tabs[0]?.id || false
29
+ );
30
+ const selected_tab_index = writable<number>(
31
+ tabs.findIndex((t) => t?.id === selected) || 0
32
+ );
33
+ const dispatch = createEventDispatcher<{
34
+ change: undefined;
35
+ select: SelectData;
36
+ }>();
37
+
38
+ async function check_overflow(): Promise<void> {
39
+ if (!stepper_container || !measurement_container || !recompute_overflow)
40
+ return;
41
+ recompute_overflow = false;
42
+ await tick();
43
+
44
+ // First, show all labels to measure
45
+ show_labels_for_all = true;
46
+ await tick();
47
+
48
+ const SEP_WIDTH = 50;
49
+ const button_width =
50
+ step_buttons[0].getBoundingClientRect().width * step_buttons.length +
51
+ SEP_WIDTH * (step_buttons.length - 1);
52
+
53
+ const containerWidth = stepper_container.getBoundingClientRect().width;
54
+ const does_it_fit = button_width < containerWidth;
55
+
56
+ if (!does_it_fit) {
57
+ show_labels_for_all = false;
58
+ compact = true;
59
+ return;
60
+ }
61
+
62
+ let max_height = 0;
63
+ let is_overlapping = false;
64
+ let last_right = 0;
65
+
66
+ for (const label of step_labels) {
67
+ const { height, width, left, right } = label.getBoundingClientRect();
68
+ if (height > max_height) {
69
+ max_height = height;
70
+ }
71
+ if (last_right && left - 10 < last_right && !is_overlapping) {
72
+ is_overlapping = true;
73
+ }
74
+ last_right = right;
75
+ }
76
+ label_height = max_height;
77
+
78
+ if (is_overlapping) {
79
+ show_labels_for_all = false;
80
+ }
81
+ }
82
+
83
+ let last_width = 0;
84
+
85
+ onMount(() => {
86
+ check_overflow();
87
+
88
+ const observer = new ResizeObserver((entries) => {
89
+ if (entries[0].contentRect.width === last_width) return;
90
+ last_width = entries[0].contentRect.width;
91
+ compact = false;
92
+ recompute_overflow = true;
93
+ check_overflow();
94
+ });
95
+
96
+ if (stepper_container) {
97
+ observer.observe(stepper_container);
98
+ }
99
+
100
+ return () => {
101
+ observer.disconnect();
102
+ };
103
+ });
104
+
105
+ setContext(TABS, {
106
+ register_tab: (tab: Tab, order: number) => {
107
+ tabs[order] = tab;
108
+
109
+ if ($selected_tab === false && tab.visible && tab.interactive) {
110
+ $selected_tab = tab.id;
111
+ $selected_tab_index = order;
112
+ }
113
+ return order;
114
+ },
115
+ unregister_tab: (tab: Tab, order: number) => {
116
+ if ($selected_tab === tab.id) {
117
+ $selected_tab = tabs[0]?.id || false;
118
+ }
119
+ tabs[order] = null;
120
+ },
121
+ selected_tab,
122
+ selected_tab_index
123
+ });
124
+
125
+ function change_tab(id: string | number | undefined, index: number): void {
126
+ const tab_to_activate = tabs.find((t) => t?.id === id);
127
+ if (
128
+ id !== undefined &&
129
+ tab_to_activate &&
130
+ tab_to_activate.interactive &&
131
+ tab_to_activate.visible &&
132
+ $selected_tab !== tab_to_activate.id
133
+ ) {
134
+ selected = id;
135
+ $selected_tab = id;
136
+ $selected_tab_index = tabs.findIndex((t) => t?.id === id);
137
+ dispatch("change");
138
+ }
139
+ }
140
+
141
+ $: (tabs,
142
+ selected !== null &&
143
+ change_tab(
144
+ selected,
145
+ tabs.findIndex((t) => t?.id === selected)
146
+ ));
147
+ $: (tabs, check_overflow());
148
+ $: ($selected_tab_index, check_overflow());
149
+
150
+ $: tab_scale =
151
+ tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
113
152
  </script>
114
153
 
115
154
  <svelte:window on:resize={check_overflow} />
@@ -1,29 +1,38 @@
1
- import { SvelteComponent } from "svelte";
2
1
  import { type Tab } from "./Tabs.svelte";
3
2
  import type { SelectData } from "@gradio/utils";
4
- declare const __propDef: {
5
- props: {
6
- visible?: boolean;
7
- elem_id?: string;
8
- elem_classes?: string[];
9
- selected: number | string;
10
- initial_tabs: Tab[];
3
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
+ $$bindings?: Bindings;
6
+ } & Exports;
7
+ (internal: unknown, props: Props & {
8
+ $$events?: Events;
9
+ $$slots?: Slots;
10
+ }): Exports & {
11
+ $set?: any;
12
+ $on?: any;
11
13
  };
12
- events: {
13
- change: CustomEvent<undefined>;
14
- select: CustomEvent<SelectData>;
15
- } & {
16
- [evt: string]: CustomEvent<any>;
17
- };
18
- slots: {
19
- default: {};
20
- };
21
- exports?: {} | undefined;
22
- bindings?: string | undefined;
23
- };
24
- export type WalkthroughProps = typeof __propDef.props;
25
- export type WalkthroughEvents = typeof __propDef.events;
26
- export type WalkthroughSlots = typeof __propDef.slots;
27
- export default class Walkthrough extends SvelteComponent<WalkthroughProps, WalkthroughEvents, WalkthroughSlots> {
14
+ z_$$bindings?: Bindings;
28
15
  }
29
- export {};
16
+ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
17
+ default: any;
18
+ } ? Props extends Record<string, never> ? any : {
19
+ children?: any;
20
+ } : {});
21
+ declare const Walkthrough: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
22
+ visible?: boolean | "hidden";
23
+ elem_id?: string;
24
+ elem_classes?: string[];
25
+ selected: number | string;
26
+ initial_tabs: Tab[];
27
+ }, {
28
+ default: {};
29
+ }>, {
30
+ change: CustomEvent<undefined>;
31
+ select: CustomEvent<SelectData>;
32
+ } & {
33
+ [evt: string]: CustomEvent<any>;
34
+ }, {
35
+ default: {};
36
+ }, {}, string>;
37
+ type Walkthrough = InstanceType<typeof Walkthrough>;
38
+ export default Walkthrough;
@@ -0,0 +1,14 @@
1
+ import type { SelectData } from "@gradio/utils";
2
+ import type { Tab } from "./shared/Tabs.svelte";
3
+ export interface TabsProps {
4
+ visible: boolean | "hidden";
5
+ elem_id: string;
6
+ elem_classes: string[];
7
+ selected: number | string;
8
+ initial_tabs: Tab[];
9
+ name: "tabs" | "walkthrough";
10
+ }
11
+ export interface TabsEvents {
12
+ change: never;
13
+ select: SelectData;
14
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/tabs",
3
- "version": "0.5.0",
3
+ "version": "0.5.2-dev.0",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -17,13 +17,13 @@
17
17
  "./package.json": "./package.json"
18
18
  },
19
19
  "dependencies": {
20
- "@gradio/utils": "^0.10.2"
20
+ "@gradio/utils": "^0.10.3-dev.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@gradio/preview": "^0.14.0"
23
+ "@gradio/preview": "^0.15.0-dev.0"
24
24
  },
25
25
  "peerDependencies": {
26
- "svelte": "^4.0.0"
26
+ "svelte": "^5.43.4"
27
27
  },
28
28
  "repository": {
29
29
  "type": "git",
@@ -5,7 +5,7 @@
5
5
  label: string;
6
6
  id: string | number;
7
7
  elem_id: string | undefined;
8
- visible: boolean;
8
+ visible: boolean | "hidden";
9
9
  interactive: boolean;
10
10
  scale: number | null;
11
11
  }
@@ -17,7 +17,7 @@
17
17
  import { writable } from "svelte/store";
18
18
  import type { SelectData } from "@gradio/utils";
19
19
 
20
- export let visible = true;
20
+ export let visible: boolean | "hidden" = true;
21
21
  export let elem_id = "";
22
22
  export let elem_classes: string[] = [];
23
23
  export let selected: number | string;
@@ -60,7 +60,7 @@
60
60
  register_tab: (tab: Tab, order: number) => {
61
61
  tabs[order] = tab;
62
62
 
63
- if ($selected_tab === false && tab.visible && tab.interactive) {
63
+ if ($selected_tab === false && tab.visible !== false && tab.interactive) {
64
64
  $selected_tab = tab.id;
65
65
  $selected_tab_index = order;
66
66
  }
@@ -82,7 +82,7 @@
82
82
  id !== undefined &&
83
83
  tab_to_activate &&
84
84
  tab_to_activate.interactive &&
85
- tab_to_activate.visible &&
85
+ tab_to_activate.visible !== false &&
86
86
  $selected_tab !== tab_to_activate.id
87
87
  ) {
88
88
  selected = id;
@@ -93,8 +93,8 @@
93
93
  }
94
94
  }
95
95
 
96
- $: tabs, selected !== null && change_tab(selected);
97
- $: tabs, tab_nav_el, tab_els, handle_menu_overflow();
96
+ $: (tabs, selected !== null && change_tab(selected));
97
+ $: (tabs, tab_nav_el, tab_els, handle_menu_overflow());
98
98
 
99
99
  function handle_outside_click(event: MouseEvent): void {
100
100
  if (
@@ -168,7 +168,8 @@
168
168
 
169
169
  <div
170
170
  class="tabs {elem_classes.join(' ')}"
171
- class:hide={!visible}
171
+ class:hide={visible === false}
172
+ class:hidden={visible === "hidden"}
172
173
  id={elem_id}
173
174
  style:flex-grow={tab_scale}
174
175
  >
@@ -176,7 +177,7 @@
176
177
  <div class="tab-wrapper">
177
178
  <div class="tab-container visually-hidden" aria-hidden="true">
178
179
  {#each tabs as t, i}
179
- {#if t?.visible}
180
+ {#if t && t?.visible !== false && t?.visible !== "hidden"}
180
181
  <button bind:this={tab_els[t.id]}>
181
182
  {t?.label}
182
183
  </button>
@@ -185,7 +186,7 @@
185
186
  </div>
186
187
  <div class="tab-container" bind:this={tab_nav_el} role="tablist">
187
188
  {#each visible_tabs as t, i}
188
- {#if t?.visible}
189
+ {#if t && t?.visible !== false}
189
190
  <button
190
191
  role="tab"
191
192
  class:selected={t.id === $selected_tab}
@@ -198,7 +199,7 @@
198
199
  on:click={() => {
199
200
  if (t.id !== $selected_tab) {
200
201
  change_tab(t.id);
201
- dispatch("select", { value: t.label, index: i });
202
+ dispatch("select", { value: t.label, index: i, id: t.id });
202
203
  }
203
204
  }}
204
205
  >
@@ -209,7 +210,8 @@
209
210
  </div>
210
211
  <span
211
212
  class="overflow-menu"
212
- class:hide={!is_overflowing || !overflow_tabs.some((t) => t?.visible)}
213
+ class:hide={!is_overflowing ||
214
+ !overflow_tabs.some((t) => t?.visible !== false)}
213
215
  bind:this={overflow_menu}
214
216
  >
215
217
  <button
@@ -221,7 +223,7 @@
221
223
  </button>
222
224
  <div class="overflow-dropdown" class:hide={!overflow_menu_open}>
223
225
  {#each overflow_tabs as t}
224
- {#if t?.visible}
226
+ {#if t?.visible !== false}
225
227
  <button
226
228
  on:click={() => change_tab(t?.id)}
227
229
  class:selected={t?.id === $selected_tab}
@@ -249,6 +251,10 @@
249
251
  display: none;
250
252
  }
251
253
 
254
+ .hidden {
255
+ display: none !important;
256
+ }
257
+
252
258
  .tab-wrapper {
253
259
  display: flex;
254
260
  align-items: center;
@@ -7,7 +7,7 @@
7
7
  import { writable } from "svelte/store";
8
8
  import type { SelectData } from "@gradio/utils";
9
9
 
10
- export let visible = true;
10
+ export let visible: boolean | "hidden" = true;
11
11
  export let elem_id = "";
12
12
  export let elem_classes: string[] = [];
13
13
  export let selected: number | string;
@@ -138,14 +138,14 @@
138
138
  }
139
139
  }
140
140
 
141
- $: tabs,
141
+ $: (tabs,
142
142
  selected !== null &&
143
143
  change_tab(
144
144
  selected,
145
145
  tabs.findIndex((t) => t?.id === selected)
146
- );
147
- $: tabs, check_overflow();
148
- $: $selected_tab_index, check_overflow();
146
+ ));
147
+ $: (tabs, check_overflow());
148
+ $: ($selected_tab_index, check_overflow());
149
149
 
150
150
  $: tab_scale =
151
151
  tabs[$selected_tab_index >= 0 ? $selected_tab_index : 0]?.scale;
package/types.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { SelectData } from "@gradio/utils";
2
+ import type { Tab } from "./shared/Tabs.svelte";
3
+
4
+ export interface TabsProps {
5
+ visible: boolean | "hidden";
6
+ elem_id: string;
7
+ elem_classes: string[];
8
+ selected: number | string;
9
+ initial_tabs: Tab[];
10
+ name: "tabs" | "walkthrough";
11
+ }
12
+
13
+ export interface TabsEvents {
14
+ change: never;
15
+ select: SelectData;
16
+ }