@finggujadhav/svelte 0.9.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/package.json +32 -0
- package/src/Button.svelte +53 -0
- package/src/Card.svelte +41 -0
- package/src/Dropdown.svelte +69 -0
- package/src/DropdownItem.svelte +9 -0
- package/src/Input.svelte +22 -0
- package/src/Modal.svelte +59 -0
- package/src/TabContent.svelte +16 -0
- package/src/TabList.svelte +3 -0
- package/src/TabTrigger.svelte +33 -0
- package/src/Tabs.svelte +13 -0
- package/src/context.ts +82 -0
- package/src/index.ts +17 -0
- package/src/tabs-shared.ts +15 -0
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@finggujadhav/svelte",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Svelte adapter for FingguFlux UI library",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "echo 'Svelte components do not require pre-build for this release'"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./src/index.ts",
|
|
18
|
+
"import": "./src/index.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@finggujadhav/core": "0.9.0",
|
|
23
|
+
"@finggujadhav/js-helper": "0.9.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"svelte": ">=3.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"svelte": "^3.0.0",
|
|
30
|
+
"typescript": "^5.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
const VARIANTS = {
|
|
3
|
+
primary: 'ff-btn-primary',
|
|
4
|
+
secondary: 'ff-btn-secondary',
|
|
5
|
+
ghost: 'ff-btn-ghost',
|
|
6
|
+
outline: 'ff-btn-outline'
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
const SIZES = {
|
|
10
|
+
sm: 'ff-btn-sm',
|
|
11
|
+
md: 'ff-btn-md',
|
|
12
|
+
lg: 'ff-btn-lg'
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
const MOTIONS = {
|
|
16
|
+
fade: 'ff-fade-in',
|
|
17
|
+
'slide-up': 'ff-slide-up',
|
|
18
|
+
'scale-in': 'ff-scale-in',
|
|
19
|
+
lift: 'ff-hover-lift'
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
export const __ffClasses_Button = [
|
|
23
|
+
'ff-btn',
|
|
24
|
+
...Object.values(VARIANTS),
|
|
25
|
+
...Object.values(SIZES),
|
|
26
|
+
...Object.values(MOTIONS),
|
|
27
|
+
'ff-card-glass'
|
|
28
|
+
];
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { useFinggu } from './context';
|
|
33
|
+
|
|
34
|
+
export let variant: keyof typeof VARIANTS = 'primary';
|
|
35
|
+
export let size: keyof typeof SIZES = 'md';
|
|
36
|
+
export let motion: keyof typeof MOTIONS | undefined = undefined;
|
|
37
|
+
export let glass: boolean = false;
|
|
38
|
+
|
|
39
|
+
const finggu = useFinggu();
|
|
40
|
+
|
|
41
|
+
$: ffClasses = $finggu.resolveAll([
|
|
42
|
+
'ff-btn',
|
|
43
|
+
VARIANTS[variant],
|
|
44
|
+
SIZES[size],
|
|
45
|
+
glass && 'ff-card-glass',
|
|
46
|
+
motion && MOTIONS[motion],
|
|
47
|
+
$$props.class
|
|
48
|
+
]);
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<button class={ffClasses} {...$$restProps}>
|
|
52
|
+
<slot />
|
|
53
|
+
</button>
|
package/src/Card.svelte
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
const VARIANTS = {
|
|
3
|
+
outline: 'ff-card-outline',
|
|
4
|
+
glass: 'ff-card-glass'
|
|
5
|
+
} as const;
|
|
6
|
+
|
|
7
|
+
const PADDINGS = {
|
|
8
|
+
none: 'ff-p-0',
|
|
9
|
+
sm: 'ff-p-2',
|
|
10
|
+
md: 'ff-p-4',
|
|
11
|
+
lg: 'ff-p-8'
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export const __ffClasses_Card = [
|
|
15
|
+
'ff-card',
|
|
16
|
+
'ff-card-header',
|
|
17
|
+
'ff-card-body',
|
|
18
|
+
...Object.values(VARIANTS),
|
|
19
|
+
...Object.values(PADDINGS)
|
|
20
|
+
];
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
import { useFinggu } from './context';
|
|
25
|
+
|
|
26
|
+
export let variant: keyof typeof VARIANTS | undefined = undefined;
|
|
27
|
+
export let padding: keyof typeof PADDINGS = 'md';
|
|
28
|
+
|
|
29
|
+
const finggu = useFinggu();
|
|
30
|
+
|
|
31
|
+
$: ffClasses = $finggu.resolveAll([
|
|
32
|
+
'ff-card',
|
|
33
|
+
variant && VARIANTS[variant],
|
|
34
|
+
PADDINGS[padding],
|
|
35
|
+
$$props.class
|
|
36
|
+
]);
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div class={ffClasses} {...$$restProps}>
|
|
40
|
+
<slot />
|
|
41
|
+
</div>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
export const __ffClasses_Dropdown = [
|
|
3
|
+
'ff-dropdown',
|
|
4
|
+
'ff-dropdown-trigger',
|
|
5
|
+
'ff-dropdown-menu',
|
|
6
|
+
'ff-dropdown-menu-right',
|
|
7
|
+
'ff-dropdown-item'
|
|
8
|
+
];
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { onMount, onDestroy } from 'svelte';
|
|
13
|
+
import { useFinggu } from './context';
|
|
14
|
+
|
|
15
|
+
export let align: 'left' | 'right' = 'left';
|
|
16
|
+
|
|
17
|
+
const finggu = useFinggu();
|
|
18
|
+
let isOpen = false;
|
|
19
|
+
let container: HTMLElement;
|
|
20
|
+
|
|
21
|
+
function toggle() { isOpen = !isOpen; }
|
|
22
|
+
function close() { isOpen = false; }
|
|
23
|
+
|
|
24
|
+
function handleClickOutside(event: MouseEvent) {
|
|
25
|
+
if (container && !container.contains(event.target as Node)) {
|
|
26
|
+
close();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
onMount(() => {
|
|
31
|
+
if (typeof document !== 'undefined') {
|
|
32
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
onDestroy(() => {
|
|
37
|
+
if (typeof document !== 'undefined') {
|
|
38
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
$: ffContainerClasses = $finggu.resolveAll(['ff-dropdown']);
|
|
43
|
+
$: ffTriggerClasses = $finggu.resolveAll(['ff-dropdown-trigger']);
|
|
44
|
+
$: ffMenuClasses = $finggu.resolveAll([
|
|
45
|
+
'ff-dropdown-menu',
|
|
46
|
+
align === 'right' && 'ff-dropdown-menu-right'
|
|
47
|
+
]);
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class={ffContainerClasses} bind:this={container}>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
class={ffTriggerClasses}
|
|
54
|
+
on:click={toggle}
|
|
55
|
+
aria-haspopup="true"
|
|
56
|
+
aria-expanded={isOpen}
|
|
57
|
+
>
|
|
58
|
+
<slot name="trigger" />
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<div
|
|
62
|
+
class={ffMenuClasses}
|
|
63
|
+
data-ff-state={isOpen ? 'open' : 'closed'}
|
|
64
|
+
role="menu"
|
|
65
|
+
aria-hidden={!isOpen}
|
|
66
|
+
>
|
|
67
|
+
<slot />
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
package/src/Input.svelte
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
export const __ffClasses_Input = [
|
|
3
|
+
'ff-input',
|
|
4
|
+
'ff-input-error'
|
|
5
|
+
];
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { useFinggu } from './context';
|
|
10
|
+
|
|
11
|
+
export let error: boolean = false;
|
|
12
|
+
|
|
13
|
+
const finggu = useFinggu();
|
|
14
|
+
|
|
15
|
+
$: ffClasses = $finggu.resolveAll([
|
|
16
|
+
'ff-input',
|
|
17
|
+
error && 'ff-input-error',
|
|
18
|
+
$$props.class
|
|
19
|
+
]);
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<input class={ffClasses} {...$$restProps} />
|
package/src/Modal.svelte
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts" context="module">
|
|
2
|
+
export const __ffClasses_Modal = [
|
|
3
|
+
'ff-modal',
|
|
4
|
+
'ff-modal-open',
|
|
5
|
+
'ff-modal-closed',
|
|
6
|
+
'ff-modal-overlay',
|
|
7
|
+
'ff-modal-content'
|
|
8
|
+
];
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import { onMount, createEventDispatcher } from 'svelte';
|
|
13
|
+
import { useFinggu } from './context';
|
|
14
|
+
|
|
15
|
+
export let isOpen: boolean = false;
|
|
16
|
+
|
|
17
|
+
const dispatch = createEventDispatcher();
|
|
18
|
+
const finggu = useFinggu();
|
|
19
|
+
|
|
20
|
+
let mounted = false;
|
|
21
|
+
let animating = false;
|
|
22
|
+
|
|
23
|
+
onMount(() => {
|
|
24
|
+
mounted = true;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
$: if (isOpen) {
|
|
28
|
+
if (typeof document !== 'undefined') {
|
|
29
|
+
document.body.style.overflow = 'hidden';
|
|
30
|
+
animating = true;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
if (typeof document !== 'undefined') {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
animating = false;
|
|
36
|
+
document.body.style.overflow = '';
|
|
37
|
+
}, 300); // Exit animation duration
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
$: ffContainerClasses = $finggu.resolveAll([
|
|
42
|
+
'ff-modal',
|
|
43
|
+
isOpen ? 'ff-modal-open' : 'ff-modal-closed'
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
$: ffContentClasses = $finggu.resolveAll([
|
|
47
|
+
'ff-modal-content',
|
|
48
|
+
$$props.class
|
|
49
|
+
]);
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
{#if mounted && (isOpen || animating)}
|
|
53
|
+
<div class={ffContainerClasses} aria-hidden={!isOpen}>
|
|
54
|
+
<div class="ff-modal-overlay" on:click={() => dispatch('close')} />
|
|
55
|
+
<div class={ffContentClasses}>
|
|
56
|
+
<slot />
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import { TABS_KEY, type TabsContext } from './tabs-shared';
|
|
4
|
+
|
|
5
|
+
export let value: string;
|
|
6
|
+
const context = getContext<TabsContext>(TABS_KEY);
|
|
7
|
+
|
|
8
|
+
$: active = $context?.activeTab ? $context.activeTab : null;
|
|
9
|
+
$: visible = $active === value;
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
{#if visible}
|
|
13
|
+
<div class="ff-tab-content" {...$$restProps}>
|
|
14
|
+
<slot />
|
|
15
|
+
</div>
|
|
16
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import { TABS_KEY, type TabsContext } from './tabs-shared';
|
|
4
|
+
import { useFinggu } from './context';
|
|
5
|
+
|
|
6
|
+
export let value: string;
|
|
7
|
+
|
|
8
|
+
const finggu = useFinggu();
|
|
9
|
+
const context = getContext<TabsContext>(TABS_KEY);
|
|
10
|
+
|
|
11
|
+
$: isActive = $context?.activeTab ? $context.activeTab : null;
|
|
12
|
+
$: active = $isActive === value;
|
|
13
|
+
|
|
14
|
+
$: ffClasses = $finggu.resolveAll([
|
|
15
|
+
'ff-tab',
|
|
16
|
+
active && 'ff-tab-active',
|
|
17
|
+
$$props.class
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function select() {
|
|
21
|
+
context?.activeTab.set(value);
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<button
|
|
26
|
+
role="tab"
|
|
27
|
+
aria-selected={active}
|
|
28
|
+
class={ffClasses}
|
|
29
|
+
on:click={select}
|
|
30
|
+
{...$$restProps}
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</button>
|
package/src/Tabs.svelte
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { setContext } from 'svelte';
|
|
3
|
+
import { writable } from 'svelte/store';
|
|
4
|
+
import { TABS_KEY } from './tabs-shared';
|
|
5
|
+
|
|
6
|
+
export let defaultValue: string;
|
|
7
|
+
const activeTab = writable(defaultValue);
|
|
8
|
+
setContext(TABS_KEY, { activeTab });
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="ff-tabs" {...$$restProps}>
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { setContext, getContext } from 'svelte';
|
|
2
|
+
import { writable, derived, type Readable } from 'svelte/store';
|
|
3
|
+
import { setTheme, FingguTheme } from '@finggujadhav/js-helper';
|
|
4
|
+
|
|
5
|
+
export interface FingguMapping {
|
|
6
|
+
_version?: string;
|
|
7
|
+
[key: string]: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FingguContextValue {
|
|
11
|
+
mapping: FingguMapping | null;
|
|
12
|
+
mode: 'dev' | 'opt' | 'ext';
|
|
13
|
+
version?: string;
|
|
14
|
+
theme?: FingguTheme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const FINGGU_KEY = Symbol('FingguFlux');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Initialize FingguFlux context in a layout or root component.
|
|
21
|
+
*/
|
|
22
|
+
export function setFingguContext(initial: FingguContextValue & { theme?: FingguTheme }) {
|
|
23
|
+
const theme = initial.theme || 'system';
|
|
24
|
+
const store = writable<FingguContextValue & { theme: FingguTheme }>({
|
|
25
|
+
...initial,
|
|
26
|
+
theme
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Version Guard
|
|
30
|
+
if (initial.mapping && initial.version && initial.mapping._version && initial.mapping._version !== initial.version) {
|
|
31
|
+
console.warn(`[FingguFlux] Version Mismatch: mapping.json (${initial.mapping._version}) does not match expected CSS version (${initial.version})`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Apply theme
|
|
35
|
+
if (typeof document !== 'undefined') {
|
|
36
|
+
setTheme(theme);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setContext(FINGGU_KEY, store);
|
|
40
|
+
return store;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* useFinggu for class resolution within Svelte components.
|
|
45
|
+
*/
|
|
46
|
+
export function useFinggu() {
|
|
47
|
+
const store = getContext<Readable<FingguContextValue>>(FINGGU_KEY);
|
|
48
|
+
|
|
49
|
+
// Fallback if context is missing (SSR safety or misconfiguration)
|
|
50
|
+
const fallback: FingguContextValue = { mapping: null, mode: 'dev' };
|
|
51
|
+
|
|
52
|
+
return derived(store || writable(fallback), ($finggu) => {
|
|
53
|
+
const resolve = (className: string): string => {
|
|
54
|
+
if ($finggu.mode === 'dev' || !$finggu.mapping) return className;
|
|
55
|
+
|
|
56
|
+
const mapped = $finggu.mapping[className];
|
|
57
|
+
if ($finggu.mode === 'ext' && !mapped && className.startsWith('ff-')) {
|
|
58
|
+
const errorMsg = `[FingguFlux] Critical: Class '${className}' not found in mapping.json in Extreme mode. This will cause broken styles in production.`;
|
|
59
|
+
|
|
60
|
+
// SvelteKit dev check
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
const isDev = typeof process !== 'undefined' ? process.env.NODE_ENV === 'development' : (import.meta as any).env?.DEV;
|
|
63
|
+
|
|
64
|
+
if (isDev) {
|
|
65
|
+
throw new Error(errorMsg);
|
|
66
|
+
} else {
|
|
67
|
+
console.error(errorMsg);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return mapped || className;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const resolveAll = (classes: (string | undefined | null | false)[]): string => {
|
|
74
|
+
return classes
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.map(c => resolve(c as string))
|
|
77
|
+
.join(' ');
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return { resolve, resolveAll, mode: $finggu.mode, theme: $finggu.theme };
|
|
81
|
+
});
|
|
82
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './context';
|
|
2
|
+
export * from './tabs-shared';
|
|
3
|
+
|
|
4
|
+
// Components
|
|
5
|
+
export { default as Button, __ffClasses_Button } from './Button.svelte';
|
|
6
|
+
export { default as Card, __ffClasses_Card } from './Card.svelte';
|
|
7
|
+
export { default as Input, __ffClasses_Input } from './Input.svelte';
|
|
8
|
+
export { default as Modal, __ffClasses_Modal } from './Modal.svelte';
|
|
9
|
+
export { default as Tabs } from './Tabs.svelte';
|
|
10
|
+
export { default as TabList } from './TabList.svelte';
|
|
11
|
+
export { default as TabTrigger } from './TabTrigger.svelte';
|
|
12
|
+
export { default as TabContent } from './TabContent.svelte';
|
|
13
|
+
export { default as Dropdown, __ffClasses_Dropdown } from './Dropdown.svelte';
|
|
14
|
+
export { default as DropdownItem } from './DropdownItem.svelte';
|
|
15
|
+
|
|
16
|
+
// Re-export shared for manifest consistency
|
|
17
|
+
export { __ffClasses_Tabs } from './tabs-shared';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { writable, type Writable } from 'svelte/store';
|
|
2
|
+
|
|
3
|
+
export const TABS_KEY = Symbol('FingguTabs');
|
|
4
|
+
|
|
5
|
+
export interface TabsContext {
|
|
6
|
+
activeTab: Writable<string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const __ffClasses_Tabs = [
|
|
10
|
+
'ff-tabs',
|
|
11
|
+
'ff-tab-list',
|
|
12
|
+
'ff-tab',
|
|
13
|
+
'ff-tab-active',
|
|
14
|
+
'ff-tab-content'
|
|
15
|
+
];
|