@indietabletop/appkit 5.0.0-2 → 5.0.0-4

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.
@@ -4,12 +4,17 @@ import { cx } from "../class-names.ts";
4
4
  import { LoadingIndicator } from "../LoadingIndicator.js";
5
5
  import { code } from "./style.css.ts";
6
6
 
7
- export function QRCode(props: { url: AsyncOp<string, unknown> }) {
7
+ type QRCodeProps = {
8
+ url: AsyncOp<string, unknown>;
9
+ className?: string;
10
+ };
11
+
12
+ export function QRCode(props: QRCodeProps) {
8
13
  const { url: result } = props;
9
14
  const { client } = useAppConfig();
10
15
 
11
16
  return (
12
- <div {...cx(code.container)}>
17
+ <div {...cx(props, code.container)}>
13
18
  {result.unpack(
14
19
  (url) => {
15
20
  const params = new URLSearchParams({ url });
@@ -2,13 +2,17 @@ import { style } from "@vanilla-extract/css";
2
2
 
3
3
  export const code = {
4
4
  container: style({
5
- display: "flex",
6
- alignItems: "center",
7
- justifyContent: "center",
8
- backgroundColor: "white",
9
- borderRadius: "0.5rem",
10
- maxInlineSize: "12rem",
11
- aspectRatio: "1",
5
+ "@layer": {
6
+ appkit: {
7
+ display: "flex",
8
+ alignItems: "center",
9
+ justifyContent: "center",
10
+ backgroundColor: "white",
11
+ borderRadius: "0.5rem",
12
+ maxInlineSize: "12rem",
13
+ aspectRatio: "1",
14
+ },
15
+ },
12
16
  }),
13
17
 
14
18
  image: style({
@@ -0,0 +1,58 @@
1
+ import preview from "../../.storybook/preview.tsx";
2
+ import { ShareButton } from "./ShareButton.tsx";
3
+ import { test } from "./test.css.ts";
4
+
5
+ const meta = preview.meta({
6
+ title: "Components/Share Button",
7
+ component: ShareButton,
8
+ tags: ["autodocs"],
9
+ args: {
10
+ shareLabel: "Share data",
11
+ copyLabel: "Copy to clipboard",
12
+ shareData: { url: "https://example.com", title: "Example URL" },
13
+ },
14
+ });
15
+
16
+ /**
17
+ * The default case.
18
+ */
19
+ export const Default = meta.story({
20
+ args: {},
21
+ });
22
+
23
+ /**
24
+ * Enforce use of copy to clipboard.
25
+ */
26
+ export const WebShareDisabled = meta.story({
27
+ args: {
28
+ webShare: false,
29
+ },
30
+ });
31
+
32
+ /**
33
+ * Allows overriding revert after milliseconds timer.
34
+ */
35
+ export const RevertAfterOverride = meta.story({
36
+ args: {
37
+ revertAfterMs: 5000,
38
+ },
39
+ });
40
+
41
+ /**
42
+ * Manually supplying disabled overrides internal button state.
43
+ */
44
+ export const Disabled = meta.story({
45
+ args: {
46
+ disabled: true,
47
+ },
48
+ });
49
+
50
+ /**
51
+ * Passes along arbitrary props to the underlying button component.
52
+ */
53
+ export const PassesProps = meta.story({
54
+ args: {
55
+ className: test,
56
+ style: { fontFamily: "cursive", padding: "1rem 2rem" },
57
+ },
58
+ });
@@ -1,11 +1,8 @@
1
1
  import { Button, type ButtonProps } from "@ariakit/react";
2
- import type { ReactNode } from "react";
2
+ import { useState, type ReactNode } from "react";
3
3
  import { useRevertingState } from "../use-reverting-state.ts";
4
4
 
5
- type AriakitButtonProps = Omit<
6
- ButtonProps,
7
- "children" | "onClick" | "disabled" | "value"
8
- >;
5
+ type AriakitButtonProps = Omit<ButtonProps, "children" | "onClick" | "value">;
9
6
 
10
7
  export type CopyToClipboardButtonProps = AriakitButtonProps & {
11
8
  value: string | null;
@@ -14,7 +11,7 @@ export type CopyToClipboardButtonProps = AriakitButtonProps & {
14
11
  };
15
12
 
16
13
  export function CopyToClipboardButton(props: CopyToClipboardButtonProps) {
17
- const { children, value, revertAfterMs, ...buttonProps } = props;
14
+ const { children, value, revertAfterMs, disabled, ...buttonProps } = props;
18
15
  const [copiedTimestamp, setCopiedTimestamp] = useRevertingState<
19
16
  number | null
20
17
  >(null, revertAfterMs);
@@ -22,7 +19,7 @@ export function CopyToClipboardButton(props: CopyToClipboardButtonProps) {
22
19
  return (
23
20
  <Button
24
21
  {...buttonProps}
25
- disabled={!value}
22
+ disabled={disabled || !value}
26
23
  onClick={async () => {
27
24
  if (value) {
28
25
  await navigator.clipboard.writeText(value);
@@ -42,7 +39,12 @@ export type WebShareButtonProps = AriakitButtonProps & {
42
39
  };
43
40
 
44
41
  export function WebShareButton(props: WebShareButtonProps) {
45
- const { shareData, children, revertAfterMs, ...buttonProps } = props;
42
+ const { shareData, children, revertAfterMs, disabled, ...buttonProps } =
43
+ props;
44
+
45
+ // Tracking sharing state to prevent duplicate invocations of
46
+ // the share dialog.
47
+ const [sharing, setSharing] = useState(false);
46
48
 
47
49
  const [copiedTimestamp, setCopiedTimestamp] = useRevertingState<
48
50
  number | null
@@ -51,14 +53,18 @@ export function WebShareButton(props: WebShareButtonProps) {
51
53
  return (
52
54
  <Button
53
55
  {...buttonProps}
54
- disabled={!shareData}
56
+ disabled={disabled || !shareData || sharing}
55
57
  onClick={async () => {
56
58
  if (shareData) {
57
59
  try {
60
+ setSharing(true);
61
+
58
62
  await navigator.share(shareData);
59
63
  setCopiedTimestamp(Date.now());
60
64
  } catch {
61
65
  console.info("Not shared");
66
+ } finally {
67
+ setSharing(false);
62
68
  }
63
69
  }
64
70
  }}
@@ -120,11 +126,16 @@ export function ShareButton(props: ShareButtonProps) {
120
126
  shareData,
121
127
  revertAfterMs = 1500,
122
128
  webShare = !!navigator.share,
129
+ ...buttonProps
123
130
  } = props;
124
131
 
125
132
  if (webShare) {
126
133
  return (
127
- <WebShareButton shareData={shareData} revertAfterMs={revertAfterMs}>
134
+ <WebShareButton
135
+ {...buttonProps}
136
+ shareData={shareData}
137
+ revertAfterMs={revertAfterMs}
138
+ >
128
139
  {shareLabel}
129
140
  </WebShareButton>
130
141
  );
@@ -132,6 +143,7 @@ export function ShareButton(props: ShareButtonProps) {
132
143
 
133
144
  return (
134
145
  <CopyToClipboardButton
146
+ {...buttonProps}
135
147
  revertAfterMs={revertAfterMs}
136
148
  value={shareData?.url ?? null}
137
149
  >
@@ -0,0 +1,3 @@
1
+ import { style } from "@vanilla-extract/css";
2
+
3
+ export const test = style({ backgroundColor: "rebeccapurple", color: "wheat" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indietabletop/appkit",
3
- "version": "5.0.0-2",
3
+ "version": "5.0.0-4",
4
4
  "description": "A collection of modules used in apps built by Indie Tabletop Club",
5
5
  "private": false,
6
6
  "type": "module",
@@ -29,14 +29,14 @@
29
29
  "react": "^18.0.0 || ^19.0.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@storybook/addon-docs": "^10.0.4",
33
- "@storybook/addon-links": "^10.0.4",
34
- "@storybook/react-vite": "^10.0.4",
32
+ "@storybook/addon-docs": "^10.0.7",
33
+ "@storybook/addon-links": "^10.0.7",
34
+ "@storybook/react-vite": "^10.0.7",
35
35
  "@types/react": "^19.1.8",
36
36
  "msw": "^2.11.2",
37
37
  "msw-storybook-addon": "^2.0.5",
38
38
  "np": "^10.1.0",
39
- "storybook": "^10.0.4",
39
+ "storybook": "^10.0.7",
40
40
  "typescript": "^5.8.2",
41
41
  "vite": "^6.3.5",
42
42
  "vitest": "^3.2.4"