fluentui-svelte 0.1.1 → 0.2.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 +0 -6
- package/dist/components/auto-suggest-box/auto-suggest-box.svelte +6 -2
- package/dist/components/auto-suggest-box/docs/llms.md +14 -13
- package/dist/components/auto-suggest-box/types.d.ts +5 -0
- package/dist/components/avatar/avatar.svelte +1 -1
- package/dist/components/badge/badge-icon.svelte +5 -8
- package/dist/components/badge/badge.svelte +6 -2
- package/dist/components/button/button.svelte +2 -2
- package/dist/components/button/types.d.ts +5 -1
- package/dist/components/card/card-context.svelte.d.ts +2 -0
- package/dist/components/card/card-context.svelte.js +2 -0
- package/dist/components/card/card-footer.svelte +37 -0
- package/dist/components/card/card-footer.svelte.d.ts +4 -0
- package/dist/components/card/card-header.svelte +81 -0
- package/dist/components/card/card-header.svelte.d.ts +4 -0
- package/dist/components/card/card-preview.svelte +64 -0
- package/dist/components/card/card-preview.svelte.d.ts +4 -0
- package/dist/components/card/card.svelte +242 -0
- package/dist/components/card/card.svelte.d.ts +39 -0
- package/dist/components/card/docs/llms.md +206 -0
- package/dist/components/card/types.d.ts +129 -0
- package/dist/components/card/types.js +1 -0
- package/dist/components/checkbox/checkbox.svelte +5 -3
- package/dist/components/dropdown/dropdown.svelte +0 -1
- package/dist/components/flyout/flyout.svelte +1 -0
- package/dist/components/menu/menu-item-checkbox.svelte +1 -1
- package/dist/components/menu/menu-item-radio.svelte +1 -1
- package/dist/components/menu/menu-item-switch.svelte +1 -1
- package/dist/components/menu/menu-item.svelte +1 -1
- package/dist/components/menu/menu.svelte +1 -0
- package/dist/css/class-darkmode.css +3 -1
- package/dist/css/media-darkmode.css +3 -1
- package/dist/css/theme.css +3 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/internal/components/render-soc/render-soc.svelte +17 -23
- package/dist/internal/components/render-soc/render-soc.svelte.d.ts +16 -16
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
value = $bindable(''),
|
|
32
32
|
inputRef = $bindable(),
|
|
33
33
|
selectOnFocus,
|
|
34
|
+
openOnFocus = false,
|
|
34
35
|
virtualizer,
|
|
35
36
|
flyoutRef = $bindable(),
|
|
36
37
|
flyoutProps,
|
|
@@ -250,6 +251,10 @@
|
|
|
250
251
|
inputRef?.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, cancelable: true }));
|
|
251
252
|
}
|
|
252
253
|
|
|
254
|
+
function handleFocus() {
|
|
255
|
+
if (openOnFocus) open = true;
|
|
256
|
+
}
|
|
257
|
+
|
|
253
258
|
async function handleTextChanged(e: InputEvent, val: string) {
|
|
254
259
|
if (val.length > 0 && !open) open = true;
|
|
255
260
|
lastTypedValue = val;
|
|
@@ -363,6 +368,7 @@
|
|
|
363
368
|
querySubmitted={(e) => querySubmitted?.(e, value)}
|
|
364
369
|
textChanged={(e) => handleTextChanged(e, value)}
|
|
365
370
|
onClear={handleClear}
|
|
371
|
+
onfocus={handleFocus}
|
|
366
372
|
role="combobox"
|
|
367
373
|
aria-expanded={open}
|
|
368
374
|
aria-autocomplete="list"
|
|
@@ -405,9 +411,7 @@
|
|
|
405
411
|
position: relative;
|
|
406
412
|
width: 100%;
|
|
407
413
|
& :global(.fs-flyout) {
|
|
408
|
-
position: absolute;
|
|
409
414
|
padding: 0;
|
|
410
|
-
top: 0;
|
|
411
415
|
left: -1px;
|
|
412
416
|
width: calc(100% + 2px);
|
|
413
417
|
}
|
|
@@ -58,16 +58,17 @@ built-in filter that a plain option list applies is skipped.
|
|
|
58
58
|
|
|
59
59
|
## Component Props
|
|
60
60
|
|
|
61
|
-
| Name | Type | Description
|
|
62
|
-
| ------------------ | --------------------------------------------------------- |
|
|
63
|
-
| `placeholder` | `string` | Placeholder text shown in the input.
|
|
64
|
-
| `value` | `string` | The current value of the input field.
|
|
65
|
-
| `open` | `boolean` | Whether the suggestion flyout is open. Default: `false`.
|
|
66
|
-
| `multiselect` | `boolean` | Allow selecting multiple suggestions.
|
|
67
|
-
| `selectOnFocus` | `boolean` | Select a suggestion as it is focused during keyboard navigation.
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
61
|
+
| Name | Type | Description |
|
|
62
|
+
| ------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
63
|
+
| `placeholder` | `string` | Placeholder text shown in the input. |
|
|
64
|
+
| `value` | `string` | The current value of the input field. |
|
|
65
|
+
| `open` | `boolean` | Whether the suggestion flyout is open. Default: `false`. |
|
|
66
|
+
| `multiselect` | `boolean` | Allow selecting multiple suggestions. |
|
|
67
|
+
| `selectOnFocus` | `boolean` | Select a suggestion as it is focused during keyboard navigation. |
|
|
68
|
+
| `openOnFocus` | `boolean` | Open the suggestion list as soon as the input takes focus. Default: `false`. |
|
|
69
|
+
| `notFoundText` | `string` | Text shown when no suggestions match. Default: `No results found`. |
|
|
70
|
+
| `maxItemsInView` | `number` | Number of suggestions visible before the flyout scrolls. Default: `6`. |
|
|
71
|
+
| `ref` | `HTMLElement` | The DOM reference of the auto-suggest box element. |
|
|
72
|
+
| `suggestionChosen` | `(e: Event, selection: string) => void` | Event triggered when a suggestion is chosen. |
|
|
73
|
+
| `querySubmitted` | `(e: MouseEvent \| KeyboardEvent, query: string) => void` | Event triggered when a query is submitted. |
|
|
74
|
+
| `virtualizer` | `{ size, scrollToIndex?, scrollToTop?, scrollToBottom? }` | Bridge to a windowed list. `size` is the total suggestion count. |
|
|
@@ -29,6 +29,11 @@ export type AutoSuggestBoxProps = {
|
|
|
29
29
|
* Not supported together with `multiselect`.
|
|
30
30
|
*/
|
|
31
31
|
selectOnFocus?: boolean;
|
|
32
|
+
/** Opens the suggestion list as soon as the text box takes focus, instead of waiting for the
|
|
33
|
+
* first keystroke. Useful on touch devices, which have no arrow key to open the list with.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
openOnFocus?: boolean;
|
|
32
37
|
/** Lists every chosen suggestion in the text box, comma separated. Multiselect only. */
|
|
33
38
|
showTextualMultiselect?: boolean;
|
|
34
39
|
/** The message shown in place of the list when nothing matches.
|
|
@@ -49,15 +49,12 @@
|
|
|
49
49
|
{...attributes}
|
|
50
50
|
>
|
|
51
51
|
{#if Icon}
|
|
52
|
-
<RenderSoC
|
|
53
|
-
SoC={Icon}
|
|
54
|
-
args={[{ 'aria-hidden': true, width: size, height: size }]}
|
|
55
|
-
aria-hidden="true"
|
|
56
|
-
width="{size}px"
|
|
57
|
-
height="{size}px"
|
|
58
|
-
/>
|
|
52
|
+
<RenderSoC SoC={Icon} args={{ 'aria-hidden': true, width: `${size}px`, height: `${size}px` }} />
|
|
59
53
|
{:else}
|
|
60
|
-
|
|
54
|
+
{@const statusIcon = getIconBadge(outOfOffice, status)}
|
|
55
|
+
{#if statusIcon}
|
|
56
|
+
<RenderSoC SoC={statusIcon} args={{ 'aria-hidden': true, width: `${size}px`, height: `${size}px` }} />
|
|
57
|
+
{/if}
|
|
61
58
|
{/if}
|
|
62
59
|
</span>
|
|
63
60
|
|
|
@@ -43,9 +43,13 @@
|
|
|
43
43
|
>
|
|
44
44
|
{#if iconPosition === 'after'}
|
|
45
45
|
{@render children?.()}
|
|
46
|
-
|
|
46
|
+
{#if Icon}
|
|
47
|
+
<RenderSoC SoC={Icon} />
|
|
48
|
+
{/if}
|
|
47
49
|
{:else}
|
|
48
|
-
|
|
50
|
+
{#if Icon}
|
|
51
|
+
<RenderSoC SoC={Icon} />
|
|
52
|
+
{/if}
|
|
49
53
|
{@render children?.()}
|
|
50
54
|
{/if}
|
|
51
55
|
</span>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
>
|
|
55
55
|
{#if isMenuButton && indicatorPosition === 'before'}
|
|
56
56
|
{#if indicatorIcon}
|
|
57
|
-
<RenderSoC SoC={indicatorIcon}
|
|
57
|
+
<RenderSoC SoC={indicatorIcon} args={{ class: 'indicator' }} />
|
|
58
58
|
{:else}
|
|
59
59
|
<ChevronDownFilled class="indicator" />
|
|
60
60
|
{/if}
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
{#if isMenuButton && indicatorPosition === 'after'}
|
|
70
70
|
{#if indicatorIcon}
|
|
71
|
-
<RenderSoC SoC={indicatorIcon}
|
|
71
|
+
<RenderSoC SoC={indicatorIcon} args={{ class: 'indicator' }} />
|
|
72
72
|
{:else}
|
|
73
73
|
<ChevronDownFilled class="indicator" />
|
|
74
74
|
{/if}
|
|
@@ -40,7 +40,11 @@ export type ButtonProps<T extends ButtonGenerics = 'button'> = {
|
|
|
40
40
|
/** The icon used to indicate the button triggers a menu.
|
|
41
41
|
* @type Snippet | Component
|
|
42
42
|
*/
|
|
43
|
-
indicatorIcon?: Snippet<
|
|
43
|
+
indicatorIcon?: Snippet<[{
|
|
44
|
+
class: string;
|
|
45
|
+
}]> | Component<{
|
|
46
|
+
class: string;
|
|
47
|
+
}>;
|
|
44
48
|
} & PolymorphicProps<T>;
|
|
45
49
|
export type ButtonElementDOMType = {
|
|
46
50
|
button: HTMLButtonElement;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CardFooterProps } from './types.js';
|
|
3
|
+
import { RenderSoC } from '../../internal/index.js';
|
|
4
|
+
import { getCardContext } from './card-context.svelte.js';
|
|
5
|
+
|
|
6
|
+
let { ref = $bindable(), class: classes, action, ...attributes }: CardFooterProps = $props();
|
|
7
|
+
|
|
8
|
+
const context = getCardContext();
|
|
9
|
+
|
|
10
|
+
if (!context) throw new Error('Card context is not available. Make sure this component is used within a Card.');
|
|
11
|
+
|
|
12
|
+
const { config } = context;
|
|
13
|
+
|
|
14
|
+
const showFloatingAction = $derived(config.showFloatingAction);
|
|
15
|
+
const selectable = $derived(config.selectable);
|
|
16
|
+
|
|
17
|
+
// Rendered through `_action`, so the invariant is enforced during SSR and on every prop update.
|
|
18
|
+
const _action = $derived.by(() => {
|
|
19
|
+
if (action && selectable) throw new Error('Action cannot be used with selectable cards.');
|
|
20
|
+
return action;
|
|
21
|
+
});
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div bind:this={ref} class={['fs-card-footer', showFloatingAction && 'with-action', classes]} {...attributes}>
|
|
25
|
+
{#if _action}
|
|
26
|
+
<RenderSoC SoC={_action} />
|
|
27
|
+
{/if}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.fs-card-footer {
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: 0.5rem;
|
|
34
|
+
padding: 0.5rem;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CardHeaderProps } from './types.js';
|
|
3
|
+
import { RenderSoC } from '../../internal/index.js';
|
|
4
|
+
import { getCardContext } from './card-context.svelte.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ref = $bindable(),
|
|
8
|
+
class: classes,
|
|
9
|
+
title,
|
|
10
|
+
image,
|
|
11
|
+
imageAlt = '',
|
|
12
|
+
description,
|
|
13
|
+
action,
|
|
14
|
+
...attributes
|
|
15
|
+
}: CardHeaderProps = $props();
|
|
16
|
+
|
|
17
|
+
const context = getCardContext();
|
|
18
|
+
|
|
19
|
+
if (!context) throw new Error('Card context is not available. Make sure this component is used within a Card.');
|
|
20
|
+
|
|
21
|
+
const { config } = context;
|
|
22
|
+
|
|
23
|
+
const showFloatingAction = $derived(config.showFloatingAction);
|
|
24
|
+
const selectable = $derived(config.selectable);
|
|
25
|
+
const id = $derived(config.id);
|
|
26
|
+
|
|
27
|
+
// Rendered through `_action`, so the invariant is enforced during SSR and on every prop update.
|
|
28
|
+
const _action = $derived.by(() => {
|
|
29
|
+
if (action && selectable) throw new Error('Action cannot be used with selectable cards.');
|
|
30
|
+
return action;
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div bind:this={ref} class={['fs-card-header', showFloatingAction && 'with-action', classes]} {...attributes}>
|
|
35
|
+
{#if typeof image === 'string'}
|
|
36
|
+
<img src={image} alt={imageAlt} />
|
|
37
|
+
{:else if image}
|
|
38
|
+
<RenderSoC SoC={image} />
|
|
39
|
+
{/if}
|
|
40
|
+
{#if description}
|
|
41
|
+
<div class="fs-card-header-content">
|
|
42
|
+
{#if typeof title === 'string'}
|
|
43
|
+
<h4 class="body" id={`${id}-title`}>{title}</h4>
|
|
44
|
+
{:else if title}
|
|
45
|
+
<RenderSoC SoC={title} id={`${id}-title`} />
|
|
46
|
+
{/if}
|
|
47
|
+
{#if typeof description === 'string'}
|
|
48
|
+
<p class="caption">{description}</p>
|
|
49
|
+
{:else if description}
|
|
50
|
+
<RenderSoC SoC={description} />
|
|
51
|
+
{/if}
|
|
52
|
+
</div>
|
|
53
|
+
{:else if typeof title === 'string'}
|
|
54
|
+
<h4 class="body" id={`${id}-title`}>{title}</h4>
|
|
55
|
+
{:else if title}
|
|
56
|
+
<RenderSoC SoC={title} id={`${id}-title`} />
|
|
57
|
+
{/if}
|
|
58
|
+
{#if _action}
|
|
59
|
+
<RenderSoC SoC={_action} />
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.fs-card-header {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: center;
|
|
67
|
+
gap: 0.5rem;
|
|
68
|
+
padding: 0.5rem;
|
|
69
|
+
&.with-action:nth-child(2) {
|
|
70
|
+
padding-right: 2.4rem;
|
|
71
|
+
}
|
|
72
|
+
& :global(img) {
|
|
73
|
+
width: 48px;
|
|
74
|
+
}
|
|
75
|
+
& .fs-card-header-content {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
margin-right: auto;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CardPreviewProps } from './types.js';
|
|
3
|
+
import { getCardContext } from './card-context.svelte.js';
|
|
4
|
+
|
|
5
|
+
let { ref = $bindable(), logoSrc, logoAlt = '', class: classes, children, ...attrs }: CardPreviewProps = $props();
|
|
6
|
+
|
|
7
|
+
const context = getCardContext();
|
|
8
|
+
|
|
9
|
+
if (!context) throw new Error('Card context is not available. Make sure this component is used within a Card.');
|
|
10
|
+
|
|
11
|
+
const { config, state: _state } = context;
|
|
12
|
+
|
|
13
|
+
const showFloatingAction = $derived(config.showFloatingAction);
|
|
14
|
+
|
|
15
|
+
const orientation = $derived(_state.orientation);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<div class={['fs-card-preview', showFloatingAction && 'with-action', orientation, classes]} bind:this={ref} {...attrs}>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
<img src={logoSrc} alt={logoAlt} class="fs-card-preview-logo" />
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
.fs-card-preview {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
position: relative;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
/* 2nd child (After floating Checkbox) */
|
|
32
|
+
&:first-child,
|
|
33
|
+
&.with-action:nth-child(2) {
|
|
34
|
+
border-radius: var(--fs-control-overlay-border-radius) var(--fs-control-overlay-border-radius) 0 0;
|
|
35
|
+
}
|
|
36
|
+
&:last-child {
|
|
37
|
+
border-radius: 0 0 var(--fs-control-overlay-border-radius) var(--fs-control-overlay-border-radius);
|
|
38
|
+
}
|
|
39
|
+
&.horizontal {
|
|
40
|
+
&:first-child,
|
|
41
|
+
&.with-action:nth-child(2) {
|
|
42
|
+
border-radius: var(--fs-control-overlay-border-radius) 0 0 var(--fs-control-overlay-border-radius);
|
|
43
|
+
}
|
|
44
|
+
&:last-child {
|
|
45
|
+
border-radius: 0 var(--fs-control-overlay-border-radius) var(--fs-control-overlay-border-radius) 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
&:only-child {
|
|
49
|
+
border-radius: var(--fs-control-overlay-border-radius);
|
|
50
|
+
}
|
|
51
|
+
& :global(img:not(.fs-card-preview-logo)) {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: auto;
|
|
54
|
+
}
|
|
55
|
+
& .fs-card-preview-logo {
|
|
56
|
+
width: 32px;
|
|
57
|
+
height: auto;
|
|
58
|
+
position: absolute;
|
|
59
|
+
bottom: 0.5rem;
|
|
60
|
+
left: 0.5rem;
|
|
61
|
+
z-index: 1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<script lang="ts" generics="Tag extends CardTagTypes = 'div'">
|
|
2
|
+
import type { Horientation } from '../../types/index.js';
|
|
3
|
+
import { invokeHandlers } from '../../internal/utils.js';
|
|
4
|
+
import { setCardContext } from './card-context.svelte.js';
|
|
5
|
+
import Checkbox from '../checkbox/checkbox.svelte';
|
|
6
|
+
import type { CardContext, CardProps, CardTagTypes } from './types.js';
|
|
7
|
+
import { PREFIX } from '../../internal/constants.js';
|
|
8
|
+
|
|
9
|
+
const _ID = $props.id();
|
|
10
|
+
const FALLBACK_ID = PREFIX + '-card-' + _ID;
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
as = 'div' as Tag,
|
|
14
|
+
ref = $bindable(),
|
|
15
|
+
orientation = 'horizontal',
|
|
16
|
+
id = FALLBACK_ID,
|
|
17
|
+
selectable = false,
|
|
18
|
+
showFloatingAction = false,
|
|
19
|
+
selected = $bindable(false),
|
|
20
|
+
disabled = false,
|
|
21
|
+
appearance = 'filled',
|
|
22
|
+
class: classes,
|
|
23
|
+
children,
|
|
24
|
+
onclick,
|
|
25
|
+
onSelectionChange,
|
|
26
|
+
...attributes
|
|
27
|
+
}: CardProps<Tag> = $props();
|
|
28
|
+
|
|
29
|
+
const context: CardContext = $state({
|
|
30
|
+
config: {
|
|
31
|
+
get as() {
|
|
32
|
+
return as;
|
|
33
|
+
},
|
|
34
|
+
get appearance() {
|
|
35
|
+
return appearance;
|
|
36
|
+
},
|
|
37
|
+
get selectable() {
|
|
38
|
+
return selectable;
|
|
39
|
+
},
|
|
40
|
+
get showFloatingAction() {
|
|
41
|
+
return showFloatingAction;
|
|
42
|
+
},
|
|
43
|
+
get id() {
|
|
44
|
+
return id;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
state: {
|
|
48
|
+
get selected() {
|
|
49
|
+
return selected;
|
|
50
|
+
},
|
|
51
|
+
set selected(value: boolean) {
|
|
52
|
+
selected = value;
|
|
53
|
+
},
|
|
54
|
+
get disabled() {
|
|
55
|
+
return disabled;
|
|
56
|
+
},
|
|
57
|
+
set disabled(value: boolean) {
|
|
58
|
+
disabled = value;
|
|
59
|
+
},
|
|
60
|
+
get orientation() {
|
|
61
|
+
return orientation;
|
|
62
|
+
},
|
|
63
|
+
set orientation(value: Horientation) {
|
|
64
|
+
orientation = value;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
events: null,
|
|
68
|
+
methods: {
|
|
69
|
+
handleAction(e) {
|
|
70
|
+
if (selectable && !disabled) {
|
|
71
|
+
selected = !selected;
|
|
72
|
+
onSelectionChange?.(id, selected);
|
|
73
|
+
}
|
|
74
|
+
onclick?.(e as any);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
setCardContext(context);
|
|
80
|
+
|
|
81
|
+
let actionable = $derived(!disabled && (selectable || as === 'a'));
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The props the invariants rule over. The markup renders them through here, so reading any of
|
|
85
|
+
* them validates the whole set during SSR and on every prop update.
|
|
86
|
+
*/
|
|
87
|
+
const _card = $derived.by(() => {
|
|
88
|
+
if (showFloatingAction && !selectable)
|
|
89
|
+
throw new Error('Floating action can only be shown if the card is selectable.');
|
|
90
|
+
if (selected && !selectable) throw new Error('A card cannot be selected if it is not selectable.');
|
|
91
|
+
if (as === 'a' && selectable) throw new Error('A card cannot be both a link and selectable.');
|
|
92
|
+
return { as, selected, showFloatingAction };
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let isLink = $derived(as === 'a');
|
|
96
|
+
|
|
97
|
+
/** The root carries the selection semantics itself when no floating checkbox is there to carry them. */
|
|
98
|
+
let isRootControl = $derived(selectable && !isLink && !_card.showFloatingAction);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A focusable root needs an accessible name. The title of the header provides it, unless the
|
|
102
|
+
* consumer named the card itself.
|
|
103
|
+
*/
|
|
104
|
+
let rootLabelledBy = $derived(
|
|
105
|
+
isRootControl && !attributes['aria-label'] && !attributes['aria-labelledby'] ? `${id}-title` : undefined
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
/** Enter and Space toggle the selection, the same way a pointer click does. */
|
|
109
|
+
const handleKeydown = (e: KeyboardEvent) => {
|
|
110
|
+
if (e.key !== 'Enter' && e.key !== ' ') return;
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
if (e.currentTarget instanceof HTMLElement) e.currentTarget.click();
|
|
113
|
+
};
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!--
|
|
117
|
+
@component
|
|
118
|
+
A card is a container that displays content and actions on a single topic.
|
|
119
|
+
- Usage:
|
|
120
|
+
```tsx
|
|
121
|
+
<script>
|
|
122
|
+
import { Card, CardHeader } from 'fluentui-svelte';
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<Card>
|
|
126
|
+
<CardHeader title="Card title" />
|
|
127
|
+
<p>Content goes here</p>
|
|
128
|
+
</Card>
|
|
129
|
+
```
|
|
130
|
+
-->
|
|
131
|
+
<svelte:element
|
|
132
|
+
this={_card.as}
|
|
133
|
+
class={['fs-card', { actionable, disabled, selected: _card.selected }, orientation, appearance, classes]}
|
|
134
|
+
bind:this={ref}
|
|
135
|
+
{id}
|
|
136
|
+
role={isLink ? undefined : isRootControl ? 'checkbox' : 'group'}
|
|
137
|
+
aria-checked={isRootControl ? _card.selected : undefined}
|
|
138
|
+
aria-labelledby={rootLabelledBy}
|
|
139
|
+
aria-disabled={disabled || undefined}
|
|
140
|
+
tabindex={isRootControl && !disabled ? 0 : undefined}
|
|
141
|
+
onclick={(e: MouseEvent) => invokeHandlers(e, [disabled], [context.methods.handleAction])}
|
|
142
|
+
onkeydown={isRootControl ? handleKeydown : undefined}
|
|
143
|
+
{...attributes}
|
|
144
|
+
>
|
|
145
|
+
{#if selectable && _card.showFloatingAction && as !== 'a'}
|
|
146
|
+
<Checkbox
|
|
147
|
+
wrapperAs="div"
|
|
148
|
+
aria-labelledby={`${id}-title`}
|
|
149
|
+
bind:checked={selected}
|
|
150
|
+
{disabled}
|
|
151
|
+
onclick={(e) => e.stopPropagation()}
|
|
152
|
+
onchange={(e) => onSelectionChange?.(id, e.currentTarget.checked)}
|
|
153
|
+
/>
|
|
154
|
+
{/if}
|
|
155
|
+
{@render children?.()}
|
|
156
|
+
</svelte:element>
|
|
157
|
+
|
|
158
|
+
<style>
|
|
159
|
+
.fs-card {
|
|
160
|
+
display: flex;
|
|
161
|
+
position: relative;
|
|
162
|
+
border-radius: var(--fs-control-overlay-border-radius);
|
|
163
|
+
background-clip: padding-box;
|
|
164
|
+
color: var(--fs-text-primary);
|
|
165
|
+
transition: box-shadow var(--fs-fast-duration) var(--fs-point-to-point);
|
|
166
|
+
text-decoration: none;
|
|
167
|
+
@media (prefers-reduced-motion: reduce) {
|
|
168
|
+
transition: none;
|
|
169
|
+
}
|
|
170
|
+
&.filled {
|
|
171
|
+
background: var(--fs-card-background-default);
|
|
172
|
+
box-shadow: var(--fs-shadow-card);
|
|
173
|
+
}
|
|
174
|
+
&.filled,
|
|
175
|
+
&.outlined {
|
|
176
|
+
border: 1px solid var(--fs-control-stroke-default);
|
|
177
|
+
}
|
|
178
|
+
&.disabled {
|
|
179
|
+
user-select: none;
|
|
180
|
+
box-shadow: unset;
|
|
181
|
+
&.filled {
|
|
182
|
+
background: var(--fs-control-fill-disabled);
|
|
183
|
+
border-color: var(--fs-control-stroke-secondary);
|
|
184
|
+
color: var(--fs-text-disabled);
|
|
185
|
+
}
|
|
186
|
+
&:hover {
|
|
187
|
+
cursor: not-allowed;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
&:hover {
|
|
191
|
+
&.filled:not(.disabled) {
|
|
192
|
+
box-shadow: var(--fs-shadow-card-hover);
|
|
193
|
+
}
|
|
194
|
+
&.outlined:not(.disabled) {
|
|
195
|
+
border-color: var(--fs-control-stroke-secondary);
|
|
196
|
+
}
|
|
197
|
+
&.subtle:not(.disabled) {
|
|
198
|
+
background: var(--fs-subtle-fill-secondary);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
&:active.actionable:not(.disabled) {
|
|
202
|
+
box-shadow: unset;
|
|
203
|
+
&.filled {
|
|
204
|
+
background: var(--fs-card-background-tertiary);
|
|
205
|
+
border-color: var(--fs-control-surface-stroke-default);
|
|
206
|
+
color: var(--fs-text-secondary);
|
|
207
|
+
}
|
|
208
|
+
&.outlined {
|
|
209
|
+
border-color: var(--fs-control-surface-stroke-default);
|
|
210
|
+
color: var(--fs-text-secondary);
|
|
211
|
+
}
|
|
212
|
+
&.subtle {
|
|
213
|
+
background: var(--fs-subtle-fill-tertiary);
|
|
214
|
+
color: var(--fs-text-tertiary);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
&.actionable {
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
}
|
|
220
|
+
&.selected {
|
|
221
|
+
&.filled {
|
|
222
|
+
background: var(--fs-card-background-tertiary);
|
|
223
|
+
box-shadow: var(--fs-shadow-card);
|
|
224
|
+
}
|
|
225
|
+
&.outlined {
|
|
226
|
+
background: var(--fs-card-background-default);
|
|
227
|
+
}
|
|
228
|
+
&.subtle {
|
|
229
|
+
background: var(--fs-subtle-fill-tertiary);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
&.vertical {
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
}
|
|
235
|
+
& :global(.fs-checkbox) {
|
|
236
|
+
position: absolute;
|
|
237
|
+
top: 0.5rem;
|
|
238
|
+
right: 0.5rem;
|
|
239
|
+
z-index: 2;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
</style>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CardProps, CardTagTypes } from './types.js';
|
|
2
|
+
declare function $$render<Tag extends CardTagTypes = 'div'>(): {
|
|
3
|
+
props: CardProps<Tag>;
|
|
4
|
+
exports: {};
|
|
5
|
+
bindings: "ref" | "selected";
|
|
6
|
+
slots: {};
|
|
7
|
+
events: {};
|
|
8
|
+
};
|
|
9
|
+
declare class __sveltets_Render<Tag extends CardTagTypes = 'div'> {
|
|
10
|
+
props(): ReturnType<typeof $$render<Tag>>['props'];
|
|
11
|
+
events(): ReturnType<typeof $$render<Tag>>['events'];
|
|
12
|
+
slots(): ReturnType<typeof $$render<Tag>>['slots'];
|
|
13
|
+
bindings(): "ref" | "selected";
|
|
14
|
+
exports(): {};
|
|
15
|
+
}
|
|
16
|
+
interface $$IsomorphicComponent {
|
|
17
|
+
new <Tag extends CardTagTypes = 'div'>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Tag>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Tag>['props']>, ReturnType<__sveltets_Render<Tag>['events']>, ReturnType<__sveltets_Render<Tag>['slots']>> & {
|
|
18
|
+
$$bindings?: ReturnType<__sveltets_Render<Tag>['bindings']>;
|
|
19
|
+
} & ReturnType<__sveltets_Render<Tag>['exports']>;
|
|
20
|
+
<Tag extends CardTagTypes = 'div'>(internal: unknown, props: ReturnType<__sveltets_Render<Tag>['props']> & {}): ReturnType<__sveltets_Render<Tag>['exports']>;
|
|
21
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A card is a container that displays content and actions on a single topic.
|
|
25
|
+
* - Usage:
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <script>
|
|
28
|
+
* import { Card, CardHeader } from 'fluentui-svelte';
|
|
29
|
+
* </script>
|
|
30
|
+
*
|
|
31
|
+
* <Card>
|
|
32
|
+
* <CardHeader title="Card title" />
|
|
33
|
+
* <p>Content goes here</p>
|
|
34
|
+
* </Card>
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare const Card: $$IsomorphicComponent;
|
|
38
|
+
type Card<Tag extends CardTagTypes = 'div'> = InstanceType<typeof Card<Tag>>;
|
|
39
|
+
export default Card;
|