@quaffui/quaff 1.0.0-beta4 → 1.0.0-beta5

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.
@@ -10,6 +10,7 @@
10
10
  let {
11
11
  disabled = false,
12
12
  variant,
13
+ color,
13
14
  filled = false,
14
15
  tonal = false,
15
16
  outlined = false,
@@ -25,17 +26,18 @@
25
26
  unelevated = false,
26
27
  size = "md",
27
28
  target,
29
+ tag,
28
30
  onclick,
29
31
  children,
30
32
  ...props
31
33
  }: QBtnProps = $props();
32
34
 
33
- let qBtn: HTMLButtonElement | HTMLAnchorElement;
35
+ let qBtn: HTMLElement;
34
36
  let qBtnLabel: HTMLSpanElement;
35
37
 
36
38
  type QBtnMouseEvent = QEvent<MouseEvent, typeof qBtn>;
37
39
 
38
- const tag = $derived(to ? "a" : "button");
40
+ const computedTag = $derived(to ? "a" : tag || "button");
39
41
  const qSize = $derived(useSize(size, "q-btn"));
40
42
 
41
43
  const src = $derived(extractImgSrc(icon));
@@ -57,11 +59,15 @@
57
59
  variant || boolVariant || "elevated",
58
60
  );
59
61
 
60
- const color = $derived.by(() => {
62
+ const computedColor = $derived.by(() => {
61
63
  if (disabled) {
62
64
  return undefined;
63
65
  }
64
66
 
67
+ if (color) {
68
+ return color;
69
+ }
70
+
65
71
  if (finalVariant === "filled") {
66
72
  return "on-primary";
67
73
  }
@@ -73,7 +79,7 @@
73
79
  return "primary";
74
80
  });
75
81
 
76
- const colorVar = $derived(color && `var(--${color})`);
82
+ const colorVar = $derived(computedColor && `var(--${computedColor})`);
77
83
 
78
84
  const rippleColorVar = $derived(
79
85
  rippleColor ? `var(--${rippleColor}, ${rippleColor})` : colorVar,
@@ -118,7 +124,7 @@
118
124
  </script>
119
125
 
120
126
  <svelte:element
121
- this={tag}
127
+ this={computedTag}
122
128
  bind:this={qBtn}
123
129
  use:ripple={{
124
130
  disabled: noRipple || disabled,
@@ -138,9 +144,9 @@
138
144
  style:--ripple-color={colorVar}
139
145
  {target}
140
146
  href={to}
141
- role={tag === "a" ? "button" : undefined}
147
+ role={computedTag === "a" ? "button" : undefined}
142
148
  aria-disabled={disabled || undefined}
143
- tabindex={disabled ? -1 : 0}
149
+ tabindex={disabled ? -1 : props.tabindex || 0}
144
150
  {onkeydown}
145
151
  onclick={stopIfDisabled}
146
152
  data-quaff
@@ -1,5 +1,5 @@
1
1
  // AUTO GENERATED FILE - DO NOT MODIFY OR DELETE
2
- // @quaffHash 0be24e79cccd1bb68f94fa2bcb3fd8ff
2
+ // @quaffHash 7a471ce6ecf43b530f7b11bbb7dcabc9
3
3
  export const QBtnDocsProps = [
4
4
  {
5
5
  isArray: false,
@@ -13,6 +13,18 @@ export const QBtnDocsProps = [
13
13
  description: "Puts the button in a disabled state, making it unclickable.",
14
14
  default: "false",
15
15
  },
16
+ {
17
+ isArray: false,
18
+ optional: true,
19
+ isSnippet: false,
20
+ name: "color",
21
+ type: {
22
+ name: "string",
23
+ isClickable: false,
24
+ },
25
+ description: "Sets the color of the button. If a color is specified, it overwrites all other color variants defined with boolean attributes.",
26
+ default: "",
27
+ },
16
28
  {
17
29
  isArray: false,
18
30
  optional: true,
@@ -23,7 +35,7 @@ export const QBtnDocsProps = [
23
35
  isClickable: true,
24
36
  },
25
37
  description: "Choose the variant for the button. If a variant is specified, it overwrites all other variants defined with boolean attributes. If no variant is specified using this prop or boolean props, the `elevated` variant will be used.",
26
- default: "undefined",
38
+ default: "",
27
39
  },
28
40
  {
29
41
  isArray: false,
@@ -211,13 +223,25 @@ export const QBtnDocsProps = [
211
223
  description: 'For "a" (anchor) tag only, apply the target attribute.',
212
224
  default: "undefined",
213
225
  },
226
+ {
227
+ isArray: false,
228
+ optional: true,
229
+ isSnippet: false,
230
+ name: "tag",
231
+ type: {
232
+ name: "keyof HTMLElementTagNameMap",
233
+ isClickable: false,
234
+ },
235
+ description: "The tag to use for the button. If not specified, a button element will be used or, if `to` is specified, an anchor tag will be used.",
236
+ default: "",
237
+ },
214
238
  {
215
239
  isArray: false,
216
240
  optional: true,
217
241
  isSnippet: false,
218
242
  name: "onclick",
219
243
  type: {
220
- name: "MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>",
244
+ name: "MouseEventHandler<HTMLElement>",
221
245
  isClickable: false,
222
246
  },
223
247
  description: "This event is emitted when the button is clicked.",
@@ -8,9 +8,12 @@ export interface QBtnProps extends HTMLAttributes<HTMLButtonElement> {
8
8
  * @default false
9
9
  */
10
10
  disabled?: boolean;
11
+ /**
12
+ * Sets the color of the button. If a color is specified, it overwrites all other color variants defined with boolean attributes.
13
+ */
14
+ color?: string;
11
15
  /**
12
16
  * Choose the variant for the button. If a variant is specified, it overwrites all other variants defined with boolean attributes. If no variant is specified using this prop or boolean props, the `elevated` variant will be used.
13
- * @default undefined
14
17
  */
15
18
  variant?: QBtnVariantOptions;
16
19
  /**
@@ -88,9 +91,13 @@ export interface QBtnProps extends HTMLAttributes<HTMLButtonElement> {
88
91
  * @default undefined
89
92
  */
90
93
  target?: HTMLAnchorAttributes["target"];
94
+ /**
95
+ * The tag to use for the button. If not specified, a button element will be used or, if `to` is specified, an anchor tag will be used.
96
+ */
97
+ tag?: keyof HTMLElementTagNameMap;
91
98
  /**
92
99
  * This event is emitted when the button is clicked.
93
100
  * @default undefined
94
101
  */
95
- onclick?: MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
102
+ onclick?: MouseEventHandler<HTMLElement>;
96
103
  }
@@ -0,0 +1,46 @@
1
+ @use "$css/mixins";
2
+ @use "$css/variables";
3
+
4
+ .q-expansion-item {
5
+ width: 100%;
6
+ padding: 0;
7
+
8
+ &[aria-disabled] :is(.q-item__section, .q-item__section *) {
9
+ color: unset;
10
+ }
11
+
12
+ & summary::marker,
13
+ & summary::-webkit-details-marker {
14
+ display: none;
15
+ content: "";
16
+ }
17
+
18
+ &::details-content {
19
+ transition: content-visibility var(--duration) allow-discrete;
20
+ }
21
+
22
+ &__toggle-icon {
23
+ margin: 0;
24
+ z-index: 1;
25
+
26
+ &:is(.q-btn) {
27
+ margin-right: -0.5rem;
28
+ }
29
+
30
+ &:not(.q-btn),
31
+ &.q-btn .q-icon {
32
+ transition: rotate var(--duration);
33
+ }
34
+
35
+ &--rotate:not(.q-btn),
36
+ &--rotate.q-btn .q-icon {
37
+ rotate: 180deg;
38
+ }
39
+ }
40
+
41
+ &__content {
42
+ @include mixins.padding("x-md", "y-sm");
43
+ width: 100%;
44
+ overflow: hidden;
45
+ }
46
+ }
@@ -0,0 +1,300 @@
1
+ <script lang="ts">
2
+ import { slide } from "svelte/transition";
3
+ import { goto } from "$app/navigation";
4
+ import { QBtn, QIcon, QItem, QItemSection } from "..";
5
+ import { isActivationKey, type QEvent } from "../../utils";
6
+ import type { QExpansionItemProps } from "./props";
7
+
8
+ let {
9
+ value = $bindable(false),
10
+ label,
11
+ icon,
12
+ caption,
13
+ expandIcon = "keyboard_arrow_down",
14
+ expandedIcon,
15
+ defaultOpened = false,
16
+ dense = false,
17
+ duration = 300,
18
+ hideExpandIcon = false,
19
+ toggleAriaLabel = "Open details",
20
+ expandIconToggle = false,
21
+ to,
22
+ href,
23
+ name,
24
+ noRotateExpandIcon = false,
25
+ disabled = false,
26
+ noRipple = false,
27
+ summary,
28
+ children,
29
+ onExpandIconClick,
30
+ ...props
31
+ }: QExpansionItemProps = $props();
32
+
33
+ const id = $props.id();
34
+ const contentId = `q-expansion-item__content-${id}`;
35
+ const summaryId = `q-expansion-item__summary-${id}`;
36
+
37
+ const supportDetailsContent = CSS.supports(
38
+ "selector(details::details-content)",
39
+ );
40
+
41
+ const summaryAttributes = $derived(
42
+ !supportDetailsContent
43
+ ? {
44
+ id: summaryId,
45
+ "aria-expanded": value,
46
+ "aria-controls": contentId,
47
+ }
48
+ : {},
49
+ );
50
+
51
+ const contentAttributes = $derived(
52
+ !supportDetailsContent
53
+ ? {
54
+ id: contentId,
55
+ role: "region",
56
+ "aria-labelledby": summaryId,
57
+ }
58
+ : {},
59
+ );
60
+
61
+ let detailsEl = $state<HTMLDetailsElement>();
62
+
63
+ const iconAttributes = $derived({
64
+ [expandIconToggle ? "icon" : "name"]:
65
+ expandedIcon && value ? expandedIcon : expandIcon,
66
+ flat: expandIconToggle || undefined,
67
+ "aria-label": toggleAriaLabel,
68
+ });
69
+
70
+ $effect.pre(() => {
71
+ if (defaultOpened) {
72
+ show();
73
+ }
74
+ });
75
+
76
+ $effect(() => {
77
+ if (supportDetailsContent || !name || !value) {
78
+ return;
79
+ }
80
+
81
+ // If the browser does not support details content, we need to manually
82
+ // handle the group open state of the details elements
83
+ const parent = detailsEl?.parentElement;
84
+
85
+ if (!parent) {
86
+ return;
87
+ }
88
+
89
+ const group = parent.querySelectorAll("details[open]");
90
+ group.forEach((item) => {
91
+ if (item !== detailsEl) {
92
+ item.removeAttribute("open");
93
+ }
94
+ });
95
+ });
96
+
97
+ export function toggle() {
98
+ value = !value;
99
+ }
100
+
101
+ export function show() {
102
+ value = true;
103
+ }
104
+
105
+ export function hide() {
106
+ value = false;
107
+ }
108
+
109
+ function onclick(e: QEvent<MouseEvent, HTMLElement>) {
110
+ console.log("QExpansionItem: onclick", e);
111
+ if (disabled) {
112
+ e.preventDefault();
113
+ e.stopPropagation();
114
+ return;
115
+ }
116
+
117
+ props.onclick?.(e);
118
+ }
119
+
120
+ function onIconClick(e: QEvent<MouseEvent, HTMLElement>) {
121
+ if (disabled) {
122
+ e.preventDefault();
123
+ return;
124
+ }
125
+
126
+ e.stopPropagation();
127
+ e.preventDefault();
128
+
129
+ toggle();
130
+ onExpandIconClick?.(e);
131
+ }
132
+
133
+ function onkeydown(e: KeyboardEvent) {
134
+ if (disabled) {
135
+ e.preventDefault();
136
+ return;
137
+ }
138
+
139
+ if (e.key === "Escape") {
140
+ detailsEl?.blur();
141
+ return;
142
+ }
143
+
144
+ if (!isActivationKey(e)) {
145
+ return;
146
+ }
147
+
148
+ if (to || href) {
149
+ e.preventDefault();
150
+ goto((to || href) as string);
151
+ return;
152
+ }
153
+
154
+ if (expandIconToggle) {
155
+ // If expandIconToggle is true, we don't want to toggle the expansion item
156
+ // as the icon should do it
157
+ return;
158
+ }
159
+
160
+ e.preventDefault();
161
+ toggle();
162
+ }
163
+
164
+ function onIconKeydown(e: KeyboardEvent) {
165
+ if (disabled) {
166
+ e.preventDefault();
167
+ return;
168
+ }
169
+
170
+ if (e.key === "Escape") {
171
+ (e.target as HTMLElement)?.blur();
172
+ return;
173
+ }
174
+
175
+ if (!isActivationKey(e) || !expandIconToggle) {
176
+ return;
177
+ }
178
+
179
+ e.preventDefault();
180
+ e.stopPropagation();
181
+
182
+ const clickEvent = new MouseEvent("click", {
183
+ relatedTarget: e.target as HTMLElement,
184
+ }) as QEvent<MouseEvent, HTMLElement>;
185
+ onIconClick(clickEvent);
186
+ }
187
+ </script>
188
+
189
+ {#snippet labelSnippet()}
190
+ {label}
191
+ {/snippet}
192
+
193
+ {#snippet captionSnippet()}
194
+ {caption}
195
+ {/snippet}
196
+
197
+ {#snippet content()}
198
+ {#if value}
199
+ <div
200
+ class="q-expansion-item__content"
201
+ {...contentAttributes}
202
+ transition:slide={{ duration }}
203
+ >
204
+ {@render children?.()}
205
+ </div>
206
+ {/if}
207
+ {/snippet}
208
+
209
+ <details
210
+ bind:this={detailsEl}
211
+ bind:open={value}
212
+ {...props}
213
+ {name}
214
+ aria-disabled={disabled}
215
+ class={["q-expansion-item", value && "q-expansion-item--expanded"]}
216
+ style:--duration="{duration}ms"
217
+ >
218
+ <summary tabindex="-1" {...summaryAttributes}>
219
+ {#if summary}
220
+ <QItem
221
+ {dense}
222
+ {to}
223
+ {href}
224
+ {disabled}
225
+ noRipple={expandIconToggle || noRipple}
226
+ clickable={!expandIconToggle}
227
+ {onclick}
228
+ {onkeydown}
229
+ >
230
+ {@render summary({ expanded: value, show, hide, toggle })}
231
+ </QItem>
232
+ {:else}
233
+ <QItem
234
+ {dense}
235
+ {to}
236
+ {href}
237
+ {disabled}
238
+ noRipple={expandIconToggle || noRipple}
239
+ clickable={!expandIconToggle}
240
+ {onclick}
241
+ {onkeydown}
242
+ >
243
+ {#if icon}
244
+ <QItemSection type="icon">
245
+ <QIcon name={icon} />
246
+ </QItemSection>
247
+ {/if}
248
+
249
+ {#if label || caption}
250
+ <QItemSection
251
+ headline={label ? labelSnippet : undefined}
252
+ line1={caption ? captionSnippet : undefined}
253
+ />
254
+ {/if}
255
+
256
+ {#if !hideExpandIcon}
257
+ <QItemSection type="trailingIcon">
258
+ {#if expandIconToggle}
259
+ <QBtn
260
+ class={[
261
+ "q-expansion-item__toggle-icon",
262
+ value &&
263
+ !expandedIcon &&
264
+ !noRotateExpandIcon &&
265
+ "q-expansion-item__toggle-icon--rotate",
266
+ ]}
267
+ {...iconAttributes}
268
+ {disabled}
269
+ color="on-surface"
270
+ tag="div"
271
+ tabindex={0}
272
+ onclick={onIconClick}
273
+ onkeydowncapture={onIconKeydown}
274
+ />
275
+ {:else}
276
+ <QIcon
277
+ class={[
278
+ "q-expansion-item__toggle-icon",
279
+ value &&
280
+ !expandedIcon &&
281
+ !noRotateExpandIcon &&
282
+ "q-expansion-item__toggle-icon--rotate",
283
+ ]}
284
+ {...iconAttributes}
285
+ />
286
+ {/if}
287
+ </QItemSection>
288
+ {/if}
289
+ </QItem>
290
+ {/if}
291
+ </summary>
292
+
293
+ {#if supportDetailsContent}
294
+ {@render content()}
295
+ {/if}
296
+ </details>
297
+
298
+ {#if !supportDetailsContent}
299
+ {@render content()}
300
+ {/if}
@@ -0,0 +1,21 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ toggle?: () => void;
5
+ show?: () => void;
6
+ hide?: () => void;
7
+ };
8
+ events: {
9
+ [evt: string]: CustomEvent<any>;
10
+ };
11
+ slots: {};
12
+ };
13
+ type QExpansionItemProps_ = typeof __propDef.props;
14
+ export { QExpansionItemProps_ as QExpansionItemProps };
15
+ export type QExpansionItemEvents = typeof __propDef.events;
16
+ export type QExpansionItemSlots = typeof __propDef.slots;
17
+ export default class QExpansionItem extends SvelteComponentTyped<QExpansionItemProps_, QExpansionItemEvents, QExpansionItemSlots> {
18
+ get toggle(): () => void;
19
+ get show(): () => void;
20
+ get hide(): () => void;
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { QComponentDocs } from "../../utils";
2
+ export declare const QExpansionItemDocs: QComponentDocs;
@@ -0,0 +1,17 @@
1
+ import { QExpansionItemDocsProps, QExpansionItemDocsSnippets } from "./docs.props";
2
+ export const QExpansionItemDocs = {
3
+ name: "QExpansionItem",
4
+ description: "The QExpansionItem component allows users to create expandable/collapsible sections within a list or a card.",
5
+ docs: {
6
+ props: QExpansionItemDocsProps,
7
+ snippets: QExpansionItemDocsSnippets,
8
+ methods: [],
9
+ events: [
10
+ {
11
+ name: "click",
12
+ type: "(e: MouseEvent) => void",
13
+ description: "Emitted when the user clicks on the expansion item.",
14
+ },
15
+ ],
16
+ },
17
+ };
@@ -0,0 +1,3 @@
1
+ import { ParsedProp, ParsedSnippet } from "../../../../docgen/props/parseInterface";
2
+ export declare const QExpansionItemDocsProps: ParsedProp[];
3
+ export declare const QExpansionItemDocsSnippets: ParsedSnippet[];