@sproutsocial/seeds-react-loader 1.0.28 → 1.0.30

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/dist/esm/index.js CHANGED
@@ -9,11 +9,14 @@ import styled, { css, keyframes } from "styled-components";
9
9
  import { COMMON } from "@sproutsocial/seeds-react-system-props";
10
10
  import { visuallyHidden } from "@sproutsocial/seeds-react-mixins";
11
11
  var spin = keyframes`
12
- from {
12
+ 0% {
13
13
  transform: rotate(0deg);
14
14
  }
15
- to {
16
- transform: rotate(360deg);
15
+ 50% {
16
+ transform: rotate(540deg);
17
+ }
18
+ 100% {
19
+ transform: rotate(1080deg);
17
20
  }
18
21
  `;
19
22
  var delayAnimation = keyframes`
@@ -33,18 +36,10 @@ var getContainerSize = (size) => css`
33
36
  `;
34
37
  var getSpinnerSize = (size) => {
35
38
  const borderWidth = Math.round(size * 0.1);
36
- const half = Math.round(size / 2);
37
39
  return css`
38
40
  width: ${size}px;
39
41
  height: ${size}px;
40
- clip: rect(${half}px, ${size}px, ${size}px, 0);
41
-
42
- &::before,
43
- &::after {
44
- width: ${size}px;
45
- height: ${size}px;
46
- border-width: ${borderWidth}px;
47
- }
42
+ border-width: ${borderWidth}px;
48
43
  `;
49
44
  };
50
45
  var Container = styled.div.attrs({
@@ -55,10 +50,8 @@ var Container = styled.div.attrs({
55
50
  padding: 0;
56
51
 
57
52
  animation: ${(props) => props.delay ? css`
58
- ${spin} 3s infinite linear, ${delayAnimation} 2s 1
59
- ` : css`
60
- ${spin} 3s infinite linear
61
- `};
53
+ ${delayAnimation} 2s 1
54
+ ` : "none"};
62
55
 
63
56
  ${(props) => props.small ? getContainerSize(20) : getContainerSize(40)}
64
57
  ${COMMON}
@@ -67,28 +60,13 @@ var Spinner = styled.span`
67
60
  position: absolute;
68
61
  inset: 0;
69
62
  margin: auto;
70
- animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
71
-
72
- &::before,
73
- &::after {
74
- content: "";
75
- position: absolute;
76
- inset: 0;
77
- margin: auto;
78
- box-sizing: border-box;
79
- border-style: solid;
80
- border-radius: 100%;
81
- }
82
-
83
- &::before {
84
- border-color: transparent;
85
- border-top-color: ${({ theme }) => theme.colors.neutral[0]};
86
- animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
87
- }
88
-
89
- &::after {
90
- border-color: ${({ theme }) => theme.colors.neutral[600]};
91
- }
63
+ box-sizing: border-box;
64
+ border-style: solid;
65
+ border-color: ${({ theme }) => theme.colors.container.border.decorative.neutral};
66
+ border-top-color: ${({ theme }) => theme.colors.container.border.base};
67
+ border-left-color: ${({ theme }) => theme.colors.container.border.base};
68
+ border-radius: 100%;
69
+ animation: ${spin} 3s cubic-bezier(0.77, 0, 0.175, 1) infinite;
92
70
 
93
71
  ${(props) => props.small ? getSpinnerSize(20) : getSpinnerSize(40)}
94
72
  `;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Loader.tsx","../../src/LoaderHybrid.tsx","../../src/styles.ts","../../src/LoaderTailwind.tsx","../../src/index.ts"],"sourcesContent":["import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions, the clipped half, and the arc/ring\n// border widths all scale together so the arc clips correctly at every size.\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n const half = Math.round(size / 2);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n clip: rect(${half}px, ${size}px, ${size}px, 0);\n\n &::before,\n &::after {\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n }\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 3s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 3s infinite linear\n `};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — the bright rotating arc (&::before) and faint arc ring\n// (&::after), both clipped to the bottom half. Hidden from assistive tech\n// via aria-hidden in markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n &::before,\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-radius: 100%;\n }\n\n &::before {\n border-color: transparent;\n border-top-color: ${({ theme }) => theme.colors.neutral[0]};\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n }\n\n &::after {\n border-color: ${({ theme }) => theme.colors.neutral[600]};\n }\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — hosts the ::before (arc) pseudo-element;\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n","import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n"],"mappings":"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","names":["React","React","jsx","jsxs","React","jsx","React"]}
1
+ {"version":3,"sources":["../../src/Loader.tsx","../../src/LoaderHybrid.tsx","../../src/styles.ts","../../src/LoaderTailwind.tsx","../../src/index.ts"],"sourcesContent":["import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n 0% {\n transform: rotate(0deg);\n }\n 50% {\n transform: rotate(540deg);\n }\n 100% {\n transform: rotate(1080deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions and the ring border width scale\n// together (4px @40 / 2px @20).\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${delayAnimation} 2s 1\n `\n : \"none\"};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — Exploration 1 \"Border Top Spinner\": a single bordered ring\n// with a faint full border and accented top/left borders, rotating once per\n// 3s cycle. No pseudo-elements. Hidden from assistive tech via aria-hidden in\n// markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-color: ${({ theme }) =>\n theme.colors.container.border.decorative.neutral};\n border-top-color: ${({ theme }) => theme.colors.container.border.base};\n border-left-color: ${({ theme }) => theme.colors.container.border.base};\n border-radius: 100%;\n animation: ${spin} 3s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — the single bordered ring (Exploration 1);\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n","import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n"],"mappings":";AAAA,OAAOA,YAAW;;;ACKlB,OAAOC,YAAW;;;ACLlB,OAAO,UAAU,KAAK,iBAAiB;AACvC,SAAS,cAAc;AACvB,SAAS,sBAAsB;AAG/B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYb,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAavB,IAAM,mBAAmB,CAAC,SAAiB;AAAA,WAChC,IAAI;AAAA,YACH,IAAI;AAAA;AAKhB,IAAM,iBAAiB,CAAC,SAAiB;AACvC,QAAM,cAAc,KAAK,MAAM,OAAO,GAAG;AAEzC,SAAO;AAAA,aACI,IAAI;AAAA,cACH,IAAI;AAAA,oBACE,WAAW;AAAA;AAE/B;AAOA,IAAM,YAAY,OAAO,IAAI,MAA6B;AAAA,EACxD,WAAW;AACb,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,eAKc,CAAC,UACZ,MAAM,QACF;AAAA,YACI,cAAc;AAAA,YAElB,MAAM;AAAA;AAAA,IAEV,CAAC,UAAW,MAAM,QAAQ,iBAAiB,EAAE,IAAI,iBAAiB,EAAE,CAAE;AAAA,IACtE,MAAM;AAAA;AAOH,IAAM,UAAU,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAMZ,CAAC,EAAE,MAAM,MACvB,MAAM,OAAO,UAAU,OAAO,WAAW,OAAO;AAAA,sBAC9B,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,UAAU,OAAO,IAAI;AAAA,uBAChD,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,UAAU,OAAO,IAAI;AAAA;AAAA,eAEzD,IAAI;AAAA;AAAA,IAEf,CAAC,UAAW,MAAM,QAAQ,eAAe,EAAE,IAAI,eAAe,EAAE,CAAE;AAAA;AAG/D,IAAM,OAAO,OAAO;AAAA,IACvB,cAAc;AAAA;AAGlB,IAAO,iBAAQ;;;ACzFf,OAAO,WAAW;AAmDZ,SAGE,KAHF;AA/CN,SAAS,MACJ,QACK;AACR,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAO;AAEZ,QAAI,OAAO,UAAU,UAAU;AAC7B,cAAQ,KAAK,KAAK;AAAA,IACpB,WAAW,OAAO,UAAU,UAAU;AACpC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,YAAI,OAAO;AACT,kBAAQ,KAAK,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK,GAAG;AACzB;AAEO,IAAM,iBAAiB,MAAM;AAAA,EAClC,CACE;AAAA,IACE,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ;AAAA;AAAA;AAAA,IAGR,OAAO,SAAS;AAAA,IAChB,KAAK,CAAC;AAAA,IACN;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,UAAU;AAAA,MACd;AAAA,MACA,EAAE,mBAAmB,SAAS,QAAQ;AAAA,MACtC,EAAE,sBAAsB,MAAM;AAAA,MAC9B;AAAA,IACF;AAEA;AAAA;AAAA;AAAA,MAGE,qBAAC,SAAI,kBAAe,IAAI,GAAG,MAAM,WAAW,SAAS,KAAW,GAAG,IAGjE;AAAA,4BAAC,UAAK,WAAU,wBAAuB,eAAY,QAAO;AAAA,QAE1D,oBAAC,UAAK,WAAU,gCACb,kBAAQ,WACX;AAAA,SACF;AAAA;AAAA,EAEJ;AACF;AAEA,eAAe,cAAc;;;AF5D7B,SAAS,sBAAsB;AAevB,SASE,OAAAC,MATF,QAAAC,aAAA;AAbR,IAAM,eAAeC,OAAM;AAAA,EACzB,CAAC,OAAO,QAAQ;AACd,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA,GAAG;AAAA,IACL,IAAI;AAEJ,QAAI,eAAe,IAAI,GAAG;AACxB,aACE,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS;AAAA,UAChB,MAAM,UAAU;AAAA,UAChB;AAAA,UACA,kBAAe;AAAA,UACf;AAAA,UACC,GAAG;AAAA,UACH,GAAG;AAAA,UAEJ;AAAA,4BAAAD,KAAC,WAAQ,OAAO,SAAS,SAAS,eAAY,QAAO;AAAA,YACrD,gBAAAA,KAAC,QAAM,kBAAQ,WAAU;AAAA;AAAA;AAAA,MAC3B;AAAA,IAEJ;AAGA,WAAO,gBAAAA,KAAC,kBAAgB,GAAG,OAAO,KAAU;AAAA,EAC9C;AACF;AAEA,aAAa,cAAc;AAC3B,IAAO,uBAAQ;;;ADnCG,gBAAAG,YAAA;AADlB,IAAM,SAASC,OAAM;AAAA,EACnB,CAAC,OAAO,QAAQ,gBAAAD,KAAC,wBAAc,GAAG,OAAO,KAAU;AACrD;AAEA,OAAO,cAAc;AACrB,IAAO,iBAAQ;;;AIZf,IAAO,gBAAQ;","names":["React","React","jsx","jsxs","React","jsx","React"]}
package/dist/index.js CHANGED
@@ -46,11 +46,14 @@ var import_styled_components = __toESM(require("styled-components"));
46
46
  var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
47
47
  var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
48
48
  var spin = import_styled_components.keyframes`
49
- from {
49
+ 0% {
50
50
  transform: rotate(0deg);
51
51
  }
52
- to {
53
- transform: rotate(360deg);
52
+ 50% {
53
+ transform: rotate(540deg);
54
+ }
55
+ 100% {
56
+ transform: rotate(1080deg);
54
57
  }
55
58
  `;
56
59
  var delayAnimation = import_styled_components.keyframes`
@@ -70,18 +73,10 @@ var getContainerSize = (size) => import_styled_components.css`
70
73
  `;
71
74
  var getSpinnerSize = (size) => {
72
75
  const borderWidth = Math.round(size * 0.1);
73
- const half = Math.round(size / 2);
74
76
  return import_styled_components.css`
75
77
  width: ${size}px;
76
78
  height: ${size}px;
77
- clip: rect(${half}px, ${size}px, ${size}px, 0);
78
-
79
- &::before,
80
- &::after {
81
- width: ${size}px;
82
- height: ${size}px;
83
- border-width: ${borderWidth}px;
84
- }
79
+ border-width: ${borderWidth}px;
85
80
  `;
86
81
  };
87
82
  var Container = import_styled_components.default.div.attrs({
@@ -92,10 +87,8 @@ var Container = import_styled_components.default.div.attrs({
92
87
  padding: 0;
93
88
 
94
89
  animation: ${(props) => props.delay ? import_styled_components.css`
95
- ${spin} 3s infinite linear, ${delayAnimation} 2s 1
96
- ` : import_styled_components.css`
97
- ${spin} 3s infinite linear
98
- `};
90
+ ${delayAnimation} 2s 1
91
+ ` : "none"};
99
92
 
100
93
  ${(props) => props.small ? getContainerSize(20) : getContainerSize(40)}
101
94
  ${import_seeds_react_system_props.COMMON}
@@ -104,28 +97,13 @@ var Spinner = import_styled_components.default.span`
104
97
  position: absolute;
105
98
  inset: 0;
106
99
  margin: auto;
107
- animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
108
-
109
- &::before,
110
- &::after {
111
- content: "";
112
- position: absolute;
113
- inset: 0;
114
- margin: auto;
115
- box-sizing: border-box;
116
- border-style: solid;
117
- border-radius: 100%;
118
- }
119
-
120
- &::before {
121
- border-color: transparent;
122
- border-top-color: ${({ theme }) => theme.colors.neutral[0]};
123
- animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
124
- }
125
-
126
- &::after {
127
- border-color: ${({ theme }) => theme.colors.neutral[600]};
128
- }
100
+ box-sizing: border-box;
101
+ border-style: solid;
102
+ border-color: ${({ theme }) => theme.colors.container.border.decorative.neutral};
103
+ border-top-color: ${({ theme }) => theme.colors.container.border.base};
104
+ border-left-color: ${({ theme }) => theme.colors.container.border.base};
105
+ border-radius: 100%;
106
+ animation: ${spin} 3s cubic-bezier(0.77, 0, 0.175, 1) infinite;
129
107
 
130
108
  ${(props) => props.small ? getSpinnerSize(20) : getSpinnerSize(40)}
131
109
  `;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Loader.tsx","../src/LoaderHybrid.tsx","../src/styles.ts","../src/LoaderTailwind.tsx"],"sourcesContent":["import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n","import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions, the clipped half, and the arc/ring\n// border widths all scale together so the arc clips correctly at every size.\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n const half = Math.round(size / 2);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n clip: rect(${half}px, ${size}px, ${size}px, 0);\n\n &::before,\n &::after {\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n }\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${spin} 3s infinite linear, ${delayAnimation} 2s 1\n `\n : css`\n ${spin} 3s infinite linear\n `};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — the bright rotating arc (&::before) and faint arc ring\n// (&::after), both clipped to the bottom half. Hidden from assistive tech\n// via aria-hidden in markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n &::before,\n &::after {\n content: \"\";\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-radius: 100%;\n }\n\n &::before {\n border-color: transparent;\n border-top-color: ${({ theme }) => theme.colors.neutral[0]};\n animation: ${spin} 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n }\n\n &::after {\n border-color: ${({ theme }) => theme.colors.neutral[600]};\n }\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — hosts the ::before (arc) pseudo-element;\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n"],"mappings":"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","names":["import_react","import_react","styled","React","import_seeds_react_system_props","import_jsx_runtime","React","import_jsx_runtime","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Loader.tsx","../src/LoaderHybrid.tsx","../src/styles.ts","../src/LoaderTailwind.tsx"],"sourcesContent":["import Loader from \"./Loader\";\n\nexport default Loader;\nexport { Loader };\nexport * from \"./LoaderTypes\";\n","import React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport LoaderHybrid from \"./LoaderHybrid\";\n\n/**\n * Loader component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst Loader = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => <LoaderHybrid {...props} ref={ref} />\n);\n\nLoader.displayName = \"Loader\";\nexport default Loader;\n","/**\n * Hybrid Loader Component\n * Automatically chooses between Tailwind and styled-components based on props\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\nimport Container, { Spinner, Text } from \"./styles\"; // Styled-components version\nimport { LoaderTailwind } from \"./LoaderTailwind\"; // Tailwind version\nimport { hasStyledProps } from \"@sproutsocial/seeds-react-system-props\";\n\nconst LoaderHybrid = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (props, ref) => {\n const {\n size = \"large\",\n text = \"\",\n delay = true,\n color = \"dark\",\n qa,\n ...rest\n } = props;\n\n if (hasStyledProps(rest)) {\n return (\n <Container\n small={size === \"small\"}\n dark={color === \"dark\"}\n delay={delay}\n data-qa-loader=\"\"\n ref={ref}\n {...qa}\n {...rest}\n >\n <Spinner small={size === \"small\"} aria-hidden=\"true\" />\n <Text>{text || \"Loading\"}</Text>\n </Container>\n );\n }\n\n // Use Tailwind version (preferred - better performance)\n return <LoaderTailwind {...props} ref={ref} />;\n }\n);\n\nLoaderHybrid.displayName = \"Loader\";\nexport default LoaderHybrid;\n","import styled, { css, keyframes } from \"styled-components\";\nimport { COMMON } from \"@sproutsocial/seeds-react-system-props\";\nimport { visuallyHidden } from \"@sproutsocial/seeds-react-mixins\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\nconst spin = keyframes`\n 0% {\n transform: rotate(0deg);\n }\n 50% {\n transform: rotate(540deg);\n }\n 100% {\n transform: rotate(1080deg);\n }\n`;\n\nconst delayAnimation = keyframes`\n 0% {\n opacity: 0;\n }\n 80% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n`;\n\n// Size for the container box.\nconst getContainerSize = (size: number) => css`\n width: ${size}px;\n height: ${size}px;\n`;\n\n// Size for the visual spinner: dimensions and the ring border width scale\n// together (4px @40 / 2px @20).\nconst getSpinnerSize = (size: number) => {\n const borderWidth = Math.round(size * 0.1);\n\n return css`\n width: ${size}px;\n height: ${size}px;\n border-width: ${borderWidth}px;\n `;\n};\n\ninterface TypeLoaderContainer extends Pick<TypeLoaderProps, \"delay\"> {\n small?: boolean;\n dark?: boolean;\n}\n\nconst Container = styled.div.attrs<{ className: string }>({\n className: \"Loader\",\n})<TypeLoaderContainer>`\n position: relative;\n margin: 0 auto;\n padding: 0;\n\n animation: ${(props) =>\n props.delay\n ? css`\n ${delayAnimation} 2s 1\n `\n : \"none\"};\n\n ${(props) => (props.small ? getContainerSize(20) : getContainerSize(40))}\n ${COMMON}\n`;\n\n// Visual spinner — Exploration 1 \"Border Top Spinner\": a single bordered ring\n// with a faint full border and accented top/left borders, rotating once per\n// 3s cycle. No pseudo-elements. Hidden from assistive tech via aria-hidden in\n// markup.\nexport const Spinner = styled.span<{ small?: boolean }>`\n position: absolute;\n inset: 0;\n margin: auto;\n box-sizing: border-box;\n border-style: solid;\n border-color: ${({ theme }) =>\n theme.colors.container.border.decorative.neutral};\n border-top-color: ${({ theme }) => theme.colors.container.border.base};\n border-left-color: ${({ theme }) => theme.colors.container.border.base};\n border-radius: 100%;\n animation: ${spin} 3s cubic-bezier(0.77, 0, 0.175, 1) infinite;\n\n ${(props) => (props.small ? getSpinnerSize(20) : getSpinnerSize(40))}\n`;\n\nexport const Text = styled.div<Pick<TypeLoaderProps, \"text\">>`\n ${visuallyHidden}\n`;\n\nexport default Container;\n","/**\n * Tailwind CSS implementation of Loader component\n * Uses Seeds loader CSS classes from loader.css\n */\n\nimport React from \"react\";\nimport type { TypeLoaderProps } from \"./LoaderTypes\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const LoaderTailwind = React.forwardRef<HTMLDivElement, TypeLoaderProps>(\n (\n {\n size = \"large\",\n text = \"\",\n delay = true,\n // color is accepted but intentionally unused — it is a visual no-op in the\n // styled-components implementation too, so the API stays unchanged.\n color: _color = \"dark\",\n qa = {},\n className,\n ...rest\n }: TypeLoaderProps,\n ref: React.Ref<HTMLDivElement>\n ) => {\n const classes = cn(\n \"seeds-loader\",\n { \"seeds-loader-sm\": size === \"small\" },\n { \"seeds-loader-delay\": delay },\n className\n );\n\n return (\n // QA default precedes {...rest} so a consumer-provided data-qa-loader\n // (via {...rest} or the qa prop) overrides the default.\n <div data-qa-loader=\"\" {...rest} className={classes} ref={ref} {...qa}>\n {/* visual spinner — the single bordered ring (Exploration 1);\n hidden from assistive tech. */}\n <span className=\"seeds-loader-spinner\" aria-hidden=\"true\" />\n {/* a11y label — must stay discoverable by getByText(\"Loading\"). */}\n <span className=\"seeds-loader-visually-hidden\">\n {text || \"Loading\"}\n </span>\n </div>\n );\n }\n);\n\nLoaderTailwind.displayName = \"LoaderTailwind\";\n"],"mappings":"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","names":["import_react","import_react","styled","React","import_seeds_react_system_props","import_jsx_runtime","React","import_jsx_runtime","React"]}
package/dist/loader.css CHANGED
@@ -13,17 +13,22 @@
13
13
  * <div class="seeds-loader seeds-loader-sm">...</div> (small)
14
14
  * <div class="seeds-loader">...</div> (no delay)
15
15
  *
16
- * The visual is a thin rotating arc (--color-neutral-0) over a faint arc ring
17
- * (--color-neutral-600), both clipped to the bottom half by the spinner span.
16
+ * The visual is Exploration 1 "Border Top Spinner": a single bordered ring
17
+ * element (--color-container-border-decorative-neutral) whose top and left
18
+ * borders are accented (--color-container-border-base), rotating once per
19
+ * 3s cycle. No pseudo-elements, no clip.
18
20
  */
19
21
 
20
22
  @layer components {
21
23
  @keyframes seeds-loader-spin {
22
- from {
24
+ 0% {
23
25
  transform: rotate(0deg);
24
26
  }
25
- to {
26
- transform: rotate(360deg);
27
+ 50% {
28
+ transform: rotate(540deg);
29
+ }
30
+ 100% {
31
+ transform: rotate(1080deg);
27
32
  }
28
33
  }
29
34
 
@@ -39,83 +44,44 @@
39
44
  }
40
45
  }
41
46
 
42
- /* Base (large / 40px) — slow outer rotation hosting the spinner */
47
+ /* Base (large / 40px) — layout only; the spinner element carries rotation. */
43
48
  .seeds-loader {
44
49
  position: relative;
45
50
  margin: 0 auto;
46
51
  padding: 0;
47
52
  width: 40px;
48
53
  height: 40px;
49
- animation: seeds-loader-spin 3s infinite linear;
50
54
  }
51
55
 
52
- /* Visual spinner clipped to bottom half, faster eased rotation */
56
+ /* Visual spinner IS the bordered ring faint full border with accented
57
+ top/left, rotating once per 3s cycle. */
53
58
  .seeds-loader-spinner {
54
- position: absolute;
55
- inset: 0;
56
- margin: auto;
57
- width: 40px;
58
- height: 40px;
59
- clip: rect(20px, 40px, 40px, 0);
60
- animation: seeds-loader-spin 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
61
- }
62
-
63
- /* Bright arc */
64
- .seeds-loader-spinner::before {
65
- content: "";
66
- position: absolute;
67
- inset: 0;
68
- margin: auto;
69
- width: 40px;
70
- height: 40px;
71
- box-sizing: border-box;
72
- border: 4px solid transparent;
73
- border-top-color: var(--color-neutral-0);
74
- border-radius: 100%;
75
- animation: seeds-loader-spin 1.5s cubic-bezier(0.77, 0, 0.175, 1) infinite;
76
- }
77
-
78
- /* Faint arc ring (static) — clipped to bottom half alongside the bright arc */
79
- .seeds-loader-spinner::after {
80
- content: "";
81
59
  position: absolute;
82
60
  inset: 0;
83
61
  margin: auto;
84
62
  width: 40px;
85
63
  height: 40px;
86
64
  box-sizing: border-box;
87
- border: 4px solid var(--color-neutral-600);
65
+ border: 4px solid var(--color-container-border-decorative-neutral);
66
+ border-top-color: var(--color-container-border-base);
67
+ border-left-color: var(--color-container-border-base);
88
68
  border-radius: 100%;
69
+ animation: seeds-loader-spin 3s cubic-bezier(0.77, 0, 0.175, 1) infinite;
89
70
  }
90
71
 
91
- /* When animations are disabled, the faint arc still renders so the loader
92
- is never invisible. */
93
- .no-cssanimations .seeds-loader-spinner::after {
94
- border-color: var(--color-neutral-600);
95
- }
96
-
97
- /* Delay variant — fades the whole spinner in alongside the outer rotation */
72
+ /* Delay variant fades the spinner in; rotation lives on the spinner. */
98
73
  .seeds-loader-delay {
99
- animation:
100
- seeds-loader-spin 3s infinite linear,
101
- seeds-loader-delay-anim 2s 1;
74
+ animation: seeds-loader-delay-anim 2s 1;
102
75
  }
103
76
 
104
- /* Small (20px) — scale size, border width, and the clip rect together */
77
+ /* Small (20px) — scale size and border width together. */
105
78
  .seeds-loader-sm,
106
- .seeds-loader-sm .seeds-loader-spinner,
107
- .seeds-loader-sm .seeds-loader-spinner::before,
108
- .seeds-loader-sm .seeds-loader-spinner::after {
79
+ .seeds-loader-sm .seeds-loader-spinner {
109
80
  width: 20px;
110
81
  height: 20px;
111
82
  }
112
83
 
113
84
  .seeds-loader-sm .seeds-loader-spinner {
114
- clip: rect(10px, 20px, 20px, 0);
115
- }
116
-
117
- .seeds-loader-sm .seeds-loader-spinner::before,
118
- .seeds-loader-sm .seeds-loader-spinner::after {
119
85
  border-width: 2px;
120
86
  }
121
87
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-loader",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Seeds React Loader",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -29,9 +29,9 @@
29
29
  "test:watch": "jest --watch --coverage=false"
30
30
  },
31
31
  "dependencies": {
32
- "@sproutsocial/seeds-react-theme": "^4.3.0",
33
- "@sproutsocial/seeds-react-system-props": "^3.1.3",
34
- "@sproutsocial/seeds-react-mixins": "^4.3.11"
32
+ "@sproutsocial/seeds-react-theme": "^4.3.1",
33
+ "@sproutsocial/seeds-react-system-props": "^3.1.4",
34
+ "@sproutsocial/seeds-react-mixins": "^4.3.12"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/react": "^18.0.0",