@apify/ui-library 1.138.1 → 1.138.2-featpublictasks-2f3d3c.52

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.
Files changed (91) hide show
  1. package/dist/src/components/actor_example/actor_example.utils.d.ts +17 -0
  2. package/dist/src/components/actor_example/actor_example.utils.d.ts.map +1 -0
  3. package/dist/src/components/actor_example/actor_example.utils.js +41 -0
  4. package/dist/src/components/actor_example/actor_example.utils.js.map +1 -0
  5. package/dist/src/components/actor_example/actor_example_avatar.d.ts +13 -0
  6. package/dist/src/components/actor_example/actor_example_avatar.d.ts.map +1 -0
  7. package/dist/src/components/actor_example/actor_example_avatar.js +34 -0
  8. package/dist/src/components/actor_example/actor_example_avatar.js.map +1 -0
  9. package/dist/src/components/actor_example/actor_example_card.d.ts +28 -0
  10. package/dist/src/components/actor_example/actor_example_card.d.ts.map +1 -0
  11. package/dist/src/components/actor_example/actor_example_card.js +79 -0
  12. package/dist/src/components/actor_example/actor_example_card.js.map +1 -0
  13. package/dist/src/components/actor_example/actor_example_preview.d.ts +28 -0
  14. package/dist/src/components/actor_example/actor_example_preview.d.ts.map +1 -0
  15. package/dist/src/components/actor_example/actor_example_preview.js +208 -0
  16. package/dist/src/components/actor_example/actor_example_preview.js.map +1 -0
  17. package/dist/src/components/actor_example/actor_example_run_button.d.ts +33 -0
  18. package/dist/src/components/actor_example/actor_example_run_button.d.ts.map +1 -0
  19. package/dist/src/components/actor_example/actor_example_run_button.js +12 -0
  20. package/dist/src/components/actor_example/actor_example_run_button.js.map +1 -0
  21. package/dist/src/components/actor_example/actor_example_schema.d.ts +20 -0
  22. package/dist/src/components/actor_example/actor_example_schema.d.ts.map +1 -0
  23. package/dist/src/components/actor_example/actor_example_schema.js +27 -0
  24. package/dist/src/components/actor_example/actor_example_schema.js.map +1 -0
  25. package/dist/src/components/actor_example/actor_example_schema_field.d.ts +19 -0
  26. package/dist/src/components/actor_example/actor_example_schema_field.d.ts.map +1 -0
  27. package/dist/src/components/actor_example/actor_example_schema_field.js +33 -0
  28. package/dist/src/components/actor_example/actor_example_schema_field.js.map +1 -0
  29. package/dist/src/components/actor_example/actor_example_schema_field_badge.d.ts +9 -0
  30. package/dist/src/components/actor_example/actor_example_schema_field_badge.d.ts.map +1 -0
  31. package/dist/src/components/actor_example/actor_example_schema_field_badge.js +48 -0
  32. package/dist/src/components/actor_example/actor_example_schema_field_badge.js.map +1 -0
  33. package/dist/src/components/actor_example/actor_example_schema_legend.d.ts +10 -0
  34. package/dist/src/components/actor_example/actor_example_schema_legend.d.ts.map +1 -0
  35. package/dist/src/components/actor_example/actor_example_schema_legend.js +29 -0
  36. package/dist/src/components/actor_example/actor_example_schema_legend.js.map +1 -0
  37. package/dist/src/components/actor_example/actor_example_schema_property.d.ts +24 -0
  38. package/dist/src/components/actor_example/actor_example_schema_property.d.ts.map +1 -0
  39. package/dist/src/components/actor_example/actor_example_schema_property.js +122 -0
  40. package/dist/src/components/actor_example/actor_example_schema_property.js.map +1 -0
  41. package/dist/src/components/actor_example/index.d.ts +11 -0
  42. package/dist/src/components/actor_example/index.d.ts.map +1 -0
  43. package/dist/src/components/actor_example/index.js +11 -0
  44. package/dist/src/components/actor_example/index.js.map +1 -0
  45. package/dist/src/components/browser_window/browser_window.d.ts +20 -0
  46. package/dist/src/components/browser_window/browser_window.d.ts.map +1 -0
  47. package/dist/src/components/browser_window/browser_window.js +72 -0
  48. package/dist/src/components/browser_window/browser_window.js.map +1 -0
  49. package/dist/src/components/browser_window/index.d.ts +2 -0
  50. package/dist/src/components/browser_window/index.d.ts.map +1 -0
  51. package/dist/src/components/browser_window/index.js +2 -0
  52. package/dist/src/components/browser_window/index.js.map +1 -0
  53. package/dist/src/components/chip.d.ts.map +1 -1
  54. package/dist/src/components/chip.js +25 -2
  55. package/dist/src/components/chip.js.map +1 -1
  56. package/dist/src/components/collapsible_card/collapsible_card.d.ts +3 -2
  57. package/dist/src/components/collapsible_card/collapsible_card.d.ts.map +1 -1
  58. package/dist/src/components/collapsible_card/collapsible_card.js +2 -2
  59. package/dist/src/components/collapsible_card/collapsible_card.js.map +1 -1
  60. package/dist/src/components/floating/floating_component_base.d.ts +72 -1
  61. package/dist/src/components/floating/floating_component_base.d.ts.map +1 -1
  62. package/dist/src/components/floating/floating_component_base.js +66 -36
  63. package/dist/src/components/floating/floating_component_base.js.map +1 -1
  64. package/dist/src/components/floating/tooltip.d.ts +10 -3
  65. package/dist/src/components/floating/tooltip.d.ts.map +1 -1
  66. package/dist/src/components/floating/tooltip.js +24 -20
  67. package/dist/src/components/floating/tooltip.js.map +1 -1
  68. package/dist/src/components/index.d.ts +2 -0
  69. package/dist/src/components/index.d.ts.map +1 -1
  70. package/dist/src/components/index.js +2 -0
  71. package/dist/src/components/index.js.map +1 -1
  72. package/dist/tsconfig.build.tsbuildinfo +1 -1
  73. package/package.json +5 -5
  74. package/src/components/actor_example/actor_example.utils.ts +52 -0
  75. package/src/components/actor_example/actor_example_avatar.tsx +69 -0
  76. package/src/components/actor_example/actor_example_card.tsx +149 -0
  77. package/src/components/actor_example/actor_example_preview.tsx +295 -0
  78. package/src/components/actor_example/actor_example_run_button.tsx +61 -0
  79. package/src/components/actor_example/actor_example_schema.tsx +60 -0
  80. package/src/components/actor_example/actor_example_schema_field.tsx +87 -0
  81. package/src/components/actor_example/actor_example_schema_field_badge.tsx +66 -0
  82. package/src/components/actor_example/actor_example_schema_legend.tsx +49 -0
  83. package/src/components/actor_example/actor_example_schema_property.tsx +242 -0
  84. package/src/components/actor_example/index.ts +10 -0
  85. package/src/components/browser_window/browser_window.tsx +106 -0
  86. package/src/components/browser_window/index.ts +1 -0
  87. package/src/components/chip.tsx +27 -1
  88. package/src/components/collapsible_card/collapsible_card.tsx +6 -4
  89. package/src/components/floating/floating_component_base.tsx +89 -47
  90. package/src/components/floating/tooltip.tsx +53 -25
  91. package/src/components/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apify/ui-library",
3
- "version": "1.138.1",
3
+ "version": "1.138.2-featpublictasks-2f3d3c.52+68e3cd48a51",
4
4
  "description": "React UI library used by apify.com",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -20,14 +20,14 @@
20
20
  "type-check": "tsc --noEmit",
21
21
  "generate-theme": "node src/codemods/generate_typograpy_tokens_files.mjs && pnpm run lint:fix",
22
22
  "build-color-tokens": "node src/design_system/colors/build_color_tokens.js",
23
- "postpublish": "pnpm run clean"
23
+ "postpublish": "rimraf ./dist"
24
24
  },
25
25
  "//": [
26
26
  "Storybook for the components lives in a separate package ui-storybook.",
27
27
  "It's not nice, but helps us to get around the problem of multiple react instances."
28
28
  ],
29
29
  "dependencies": {
30
- "@apify/ui-icons": "^1.38.1",
30
+ "@apify/ui-icons": "^1.38.2-featpublictasks-2f3d3c.52+68e3cd48a51",
31
31
  "@floating-ui/react": "^0.27.19",
32
32
  "@floating-ui/react-dom": "^2.1.8",
33
33
  "@radix-ui/react-checkbox": "^1.3.3",
@@ -58,7 +58,7 @@
58
58
  "styled-components": "^6.1.19"
59
59
  },
60
60
  "devDependencies": {
61
- "@apify-packages/types": "^3.353.1",
61
+ "@apify-packages/types": "^3.353.2-featpublictasks-2f3d3c.52+68e3cd48a51",
62
62
  "@storybook/react-vite": "^10.3.5",
63
63
  "@types/hast": "^3.0.4",
64
64
  "@types/lodash": "^4.14.200",
@@ -72,5 +72,5 @@
72
72
  "src",
73
73
  "style"
74
74
  ],
75
- "gitHead": "1c034549fdf20fd6d95643da8db6940cd850bfdc"
75
+ "gitHead": "68e3cd48a5189a18f916ad456f1f15d2767d3382"
76
76
  }
@@ -0,0 +1,52 @@
1
+ import type { IconComponent } from '@apify/ui-icons';
2
+ import { AnyIcon, BooleanIcon, MenuIcon, NumberIcon, ObjectIcon, TextIcon } from '@apify/ui-icons';
3
+
4
+ import type { ActorExampleSchemaFieldType } from '@apify-packages/types';
5
+
6
+ export type ActorExampleSchemaPropertyConfig = {
7
+ Icon: IconComponent;
8
+ title: string;
9
+ backgroundColor: string;
10
+ textColor: string;
11
+ };
12
+
13
+ /**
14
+ * Per-type display config used by the schema field components (badge icon, hover tooltip,
15
+ * background/text colors). The `var(--actor-schema-field-*)` references are declared on the
16
+ * `ActorExampleSchemaFieldBadgeWrapper` styled component itself (with a dark-mode override via
17
+ * `html[data-theme='dark'] &`), so the vars are scoped to the badge — no globals are injected.
18
+ */
19
+ export const actorExampleSchemaPropertyMap: Record<ActorExampleSchemaFieldType, ActorExampleSchemaPropertyConfig> = {
20
+ string: {
21
+ Icon: TextIcon,
22
+ title: 'Text',
23
+ backgroundColor: 'var(--actor-schema-field-string-background)',
24
+ textColor: 'var(--actor-schema-field-string-text)',
25
+ },
26
+ number: {
27
+ Icon: NumberIcon,
28
+ title: 'Number',
29
+ backgroundColor: 'var(--actor-schema-field-number-background)',
30
+ textColor: 'var(--actor-schema-field-number-text)',
31
+ },
32
+ boolean: {
33
+ Icon: BooleanIcon,
34
+ title: 'Boolean',
35
+ backgroundColor: 'var(--actor-schema-field-boolean-background)',
36
+ textColor: 'var(--actor-schema-field-boolean-text)',
37
+ },
38
+ array: {
39
+ Icon: MenuIcon,
40
+ title: 'List',
41
+ backgroundColor: 'var(--actor-schema-field-array-background)',
42
+ textColor: 'var(--actor-schema-field-array-text)',
43
+ },
44
+ object: {
45
+ Icon: ObjectIcon,
46
+ title: 'Object',
47
+ backgroundColor: 'var(--actor-schema-field-object-background)',
48
+ textColor: 'var(--actor-schema-field-object-text)',
49
+ },
50
+ };
51
+
52
+ export const actorExampleSchemaPropertyDefaultIcon = AnyIcon;
@@ -0,0 +1,69 @@
1
+ import { clsx } from 'clsx';
2
+ import type React from 'react';
3
+ import { useCallback, useMemo } from 'react';
4
+ import styled from 'styled-components';
5
+
6
+ import { useSharedUiDependencies } from '../../ui_dependency_provider.js';
7
+
8
+ const DEFAULT_SIZE = 36;
9
+
10
+ export const actorExampleAvatarClassNames = {
11
+ ROOT: 'actor-example-avatar',
12
+ };
13
+
14
+ const ActorExampleAvatarWrapper = styled.img`
15
+ flex-shrink: 0;
16
+ object-fit: cover;
17
+ `;
18
+
19
+ export type ActorExampleAvatarProps = {
20
+ title?: string;
21
+ name: string;
22
+ pictureUrl?: string;
23
+ fallbackPictureUrl: string;
24
+ size?: number;
25
+ className?: string;
26
+ };
27
+
28
+ export const ActorExampleAvatar = ({
29
+ title,
30
+ name,
31
+ pictureUrl,
32
+ fallbackPictureUrl,
33
+ size = DEFAULT_SIZE,
34
+ className,
35
+ }: ActorExampleAvatarProps) => {
36
+ const { generateProxyImageUrl } = useSharedUiDependencies();
37
+
38
+ // Mirrors apify-web's `<Image defaultSrc=…>` behaviour: if the primary src fails to load,
39
+ // swap in the fallback so the `<img>` element is always present.
40
+ const handlePictureError = useCallback(
41
+ (event: React.SyntheticEvent<HTMLImageElement>) => {
42
+ if (!event.currentTarget.src.endsWith(fallbackPictureUrl)) {
43
+ // eslint-disable-next-line no-param-reassign
44
+ event.currentTarget.src = fallbackPictureUrl;
45
+ // eslint-disable-next-line no-param-reassign
46
+ event.currentTarget.srcset = fallbackPictureUrl;
47
+ }
48
+ },
49
+ [fallbackPictureUrl],
50
+ );
51
+
52
+ const proxyPictureUrl = useMemo(() => {
53
+ if (!generateProxyImageUrl || !pictureUrl) return pictureUrl;
54
+ return generateProxyImageUrl(pictureUrl, { resize: { height: size * 2, width: size * 2 } });
55
+ }, [pictureUrl, generateProxyImageUrl, size]);
56
+
57
+ const src = proxyPictureUrl || fallbackPictureUrl;
58
+
59
+ return (
60
+ <ActorExampleAvatarWrapper
61
+ src={src}
62
+ alt={title ?? name}
63
+ width={size}
64
+ height={size}
65
+ className={clsx(actorExampleAvatarClassNames.ROOT, className)}
66
+ onError={handlePictureError}
67
+ />
68
+ );
69
+ };
@@ -0,0 +1,149 @@
1
+ import { clsx } from 'clsx';
2
+ import styled from 'styled-components';
3
+
4
+ import { ExternalLinkIcon } from '@apify/ui-icons';
5
+
6
+ import { theme } from '../../design_system/theme.js';
7
+ import { Badge } from '../badge.js';
8
+ import { Box } from '../box.js';
9
+ import { Button } from '../button.js';
10
+ import { Heading } from '../text/index.js';
11
+ import { ActorExampleRunButton, ActorExampleRunButtonLightningIcon } from './actor_example_run_button.js';
12
+
13
+ export const actorExampleCardClassNames = {
14
+ ROOT: 'actor-example-card',
15
+ CONTENT: 'actor-example-card__content',
16
+ BADGE: 'actor-example-card__badge',
17
+ TITLE: 'actor-example-card__title',
18
+ FOOTER: 'actor-example-card__footer',
19
+ RUN_BUTTON: 'actor-example-card__run-button',
20
+ DETAIL_BUTTON: 'actor-example-card__detail-button',
21
+ };
22
+
23
+ const ActorExampleCardWrapper = styled(Box)`
24
+ min-width: 0;
25
+ background-color: ${theme.color.neutral.background};
26
+ border: 1px solid ${theme.color.neutral.border};
27
+ border-radius: ${theme.radius.radius12};
28
+ overflow: hidden;
29
+
30
+ .${actorExampleCardClassNames.CONTENT} {
31
+ padding: ${theme.space.space16};
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: ${theme.space.space8};
35
+ }
36
+
37
+ .${actorExampleCardClassNames.BADGE} {
38
+ min-width: 0;
39
+ max-width: 100%;
40
+
41
+ span {
42
+ min-width: 0;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ white-space: nowrap;
46
+ }
47
+ }
48
+
49
+ .${actorExampleCardClassNames.TITLE} {
50
+ min-height: 7.2rem; /* 3 lines of text with line-height 2.4rem */
51
+ overflow: hidden;
52
+ text-overflow: ellipsis;
53
+ display: -webkit-box;
54
+ -webkit-line-clamp: 3;
55
+ -webkit-box-orient: vertical;
56
+ }
57
+
58
+ .${actorExampleCardClassNames.FOOTER} {
59
+ padding: ${theme.space.space12} ${theme.space.space16};
60
+ background-color: ${theme.color.neutral.backgroundMuted};
61
+ display: grid;
62
+ grid-template-columns: repeat(2, minmax(0, 1fr));
63
+ gap: ${theme.space.space4};
64
+ }
65
+
66
+ .${actorExampleCardClassNames.RUN_BUTTON}, .${actorExampleCardClassNames.DETAIL_BUTTON} {
67
+ min-width: 0;
68
+
69
+ span {
70
+ min-width: 0;
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ white-space: nowrap;
74
+ }
75
+
76
+ svg {
77
+ flex-shrink: 0;
78
+ }
79
+ }
80
+ `;
81
+
82
+ export type ActorExampleCardProps = {
83
+ title: string;
84
+ badge?: string;
85
+ /** Owning actor's id — flows into the run button's analytics event. */
86
+ actorId: string;
87
+ runUrl: string;
88
+ runLabel: string;
89
+ /**
90
+ * Tracking element id for the run button (e.g. `actorInfo.examples.run`). The card's
91
+ * `title` is forwarded as `cardTitle` automatically.
92
+ */
93
+ runTrackingElement: string;
94
+ detailUrl?: string;
95
+ detailLabel: string;
96
+ detailOpensInNewTab?: boolean;
97
+ className?: string;
98
+ };
99
+
100
+ export const ActorExampleCard = ({
101
+ title,
102
+ badge,
103
+ actorId,
104
+ runUrl,
105
+ runLabel,
106
+ runTrackingElement,
107
+ detailUrl,
108
+ detailLabel,
109
+ detailOpensInNewTab = true,
110
+ className,
111
+ }: ActorExampleCardProps) => (
112
+ <ActorExampleCardWrapper className={clsx(actorExampleCardClassNames.ROOT, className)}>
113
+ <Box className={actorExampleCardClassNames.CONTENT}>
114
+ {badge && (
115
+ <Badge size="extra_small" variant="primary_blue" className={actorExampleCardClassNames.BADGE}>
116
+ {badge}
117
+ </Badge>
118
+ )}
119
+ <Heading as="h2" className={actorExampleCardClassNames.TITLE}>
120
+ {title}
121
+ </Heading>
122
+ </Box>
123
+ <Box className={actorExampleCardClassNames.FOOTER}>
124
+ <ActorExampleRunButton
125
+ size="small"
126
+ variant="secondary"
127
+ actorId={actorId}
128
+ consoleUrl={runUrl}
129
+ trackingProps={{ element: runTrackingElement, cardTitle: title }}
130
+ className={actorExampleCardClassNames.RUN_BUTTON}
131
+ LeftIcon={ActorExampleRunButtonLightningIcon}
132
+ >
133
+ {runLabel}
134
+ </ActorExampleRunButton>
135
+ {detailUrl && detailLabel && (
136
+ <Button
137
+ size="small"
138
+ variant="tertiary"
139
+ to={detailUrl}
140
+ target={detailOpensInNewTab ? '_blank' : undefined}
141
+ className={actorExampleCardClassNames.DETAIL_BUTTON}
142
+ RightIcon={ExternalLinkIcon}
143
+ >
144
+ {detailLabel}
145
+ </Button>
146
+ )}
147
+ </Box>
148
+ </ActorExampleCardWrapper>
149
+ );
@@ -0,0 +1,295 @@
1
+ import styled, { keyframes } from 'styled-components';
2
+
3
+ import { theme } from '../../design_system/theme.js';
4
+ import { Heading, Text } from '../text/index.js';
5
+ import { ActorExampleAvatar } from './actor_example_avatar.js';
6
+
7
+ const MAX_VISIBLE_FIELDS = 4;
8
+ const BADGE_PICTURE_SIZE = 36;
9
+
10
+ export const actorExamplePreviewClassNames = {
11
+ CONTAINER: 'actor-example-preview',
12
+ CONTENT: 'actor-example-preview__content',
13
+ BADGE: 'actor-example-preview__badge',
14
+ BADGE_PICTURE: 'actor-example-preview__badge-picture',
15
+ BADGE_CONTENT: 'actor-example-preview__badge-content',
16
+ BADGE_NAME: 'actor-example-preview__badge-name',
17
+ BADGE_TECHNICAL_NAME: 'actor-example-preview__badge-technical-name',
18
+ SCHEMA: 'actor-example-preview__schema',
19
+ SCHEMA_FIELD: 'actor-example-preview__schema-field',
20
+ SCHEMA_FIELD_NAME: 'actor-example-preview__schema-field-name',
21
+ SCHEMA_MORE: 'actor-example-preview__schema-more',
22
+ };
23
+
24
+ const VISUAL_SEPARATOR_SIZE = '0.6rem';
25
+ const VISUAL_CONNECTOR_LENGTH = '2.4rem';
26
+
27
+ const noisePulse = keyframes`
28
+ 0%, 100% { opacity: 0.1; }
29
+ 50% { opacity: 0.4; }
30
+ `;
31
+
32
+ const ActorExamplePreviewWrapper = styled.div<{ $noiseImageUrl: string }>`
33
+ @property --noise-cx {
34
+ syntax: '<percentage>';
35
+ inherits: false;
36
+ initial-value: 50%;
37
+ }
38
+
39
+ @property --noise-cy {
40
+ syntax: '<percentage>';
41
+ inherits: false;
42
+ initial-value: 50%;
43
+ }
44
+
45
+ position: relative;
46
+ display: flex;
47
+ flex-direction: column;
48
+ align-items: center;
49
+ justify-content: center;
50
+ flex-shrink: 0;
51
+
52
+ &::before {
53
+ content: '';
54
+ position: absolute;
55
+ top: -${theme.space.space32};
56
+ bottom: 0;
57
+ inset-inline: -${theme.space.space32};
58
+ background-image: url(${({ $noiseImageUrl }) => $noiseImageUrl});
59
+ background-position: center;
60
+ background-repeat: repeat;
61
+ mask-image:
62
+ linear-gradient(to bottom, transparent, black 30%, black 70%, transparent),
63
+ linear-gradient(to right, transparent, black 30%, black 70%, transparent);
64
+ mask-composite: intersect;
65
+ -webkit-mask-image:
66
+ linear-gradient(to bottom, transparent, black 30%, black 70%, transparent),
67
+ linear-gradient(to right, transparent, black 30%, black 70%, transparent);
68
+ -webkit-mask-composite: destination-in;
69
+ animation: ${noisePulse} 4s ease-in-out infinite;
70
+ opacity: 0.1;
71
+ pointer-events: none;
72
+
73
+ @media ${theme.device.desktop} {
74
+ inset: 0;
75
+ mask-image: radial-gradient(circle at var(--noise-cx) var(--noise-cy), black 30%, transparent 70%);
76
+ -webkit-mask-image: radial-gradient(circle at var(--noise-cx) var(--noise-cy), black 30%, transparent 70%);
77
+ }
78
+
79
+ @media (prefers-reduced-motion: reduce) {
80
+ animation: none;
81
+ mask-image: none;
82
+ -webkit-mask-image: none;
83
+ }
84
+ }
85
+
86
+ .${actorExamplePreviewClassNames.CONTENT} {
87
+ max-width: 100%;
88
+ display: flex;
89
+ gap: ${theme.space.space32};
90
+ flex-direction: column;
91
+ align-items: center;
92
+ justify-content: center;
93
+ }
94
+
95
+ .${actorExamplePreviewClassNames.BADGE} {
96
+ width: 100%;
97
+ min-width: 0;
98
+ padding: ${theme.space.space12};
99
+ border: 1px solid ${theme.color.neutral.separatorSubtle};
100
+ border-radius: ${theme.radius.radius12};
101
+ background-color: ${theme.color.neutral.background};
102
+ display: flex;
103
+ gap: ${theme.space.space8};
104
+ align-items: center;
105
+ position: relative;
106
+ }
107
+
108
+ .${actorExamplePreviewClassNames.BADGE_PICTURE} {
109
+ border: 1px solid ${theme.color.neutral.separatorSubtle};
110
+ border-radius: ${theme.radius.radius8};
111
+ flex-shrink: 0;
112
+ object-fit: cover;
113
+ }
114
+
115
+ .${actorExamplePreviewClassNames.BADGE_CONTENT} {
116
+ min-width: 0;
117
+ display: flex;
118
+ flex-direction: column;
119
+ flex-grow: 1;
120
+ }
121
+
122
+ .${actorExamplePreviewClassNames.BADGE_NAME}, .${actorExamplePreviewClassNames.BADGE_TECHNICAL_NAME} {
123
+ text-overflow: ellipsis;
124
+ white-space: nowrap;
125
+ overflow: hidden;
126
+ }
127
+
128
+ .${actorExamplePreviewClassNames.SCHEMA} {
129
+ width: 100%;
130
+ min-width: 0;
131
+ padding: ${theme.space.space8};
132
+ border: 1px solid ${theme.color.primary.text};
133
+ border-radius: ${theme.radius.radius12};
134
+ background-color: ${theme.color.neutral.background};
135
+ display: flex;
136
+ flex-direction: column;
137
+ align-items: center;
138
+ gap: ${theme.space.space4};
139
+ position: relative;
140
+
141
+ &::before {
142
+ bottom: calc(100% + 0.5rem);
143
+ left: calc(50% - 0.5px);
144
+ width: 1px;
145
+ height: ${VISUAL_CONNECTOR_LENGTH};
146
+ background-color: ${theme.color.primary.text};
147
+ content: '';
148
+ display: block;
149
+ position: absolute;
150
+ }
151
+
152
+ &::after {
153
+ bottom: calc(100% + 0.5rem);
154
+ left: calc(50% - (${VISUAL_SEPARATOR_SIZE} / 2));
155
+ margin: 0 auto;
156
+ width: ${VISUAL_SEPARATOR_SIZE};
157
+ height: ${VISUAL_CONNECTOR_LENGTH};
158
+ border-block: 1px solid ${theme.color.primary.text};
159
+ box-sizing: border-box;
160
+ content: '';
161
+ display: block;
162
+ position: absolute;
163
+ }
164
+ }
165
+
166
+ .${actorExamplePreviewClassNames.SCHEMA_FIELD} {
167
+ width: 100%;
168
+ padding: ${theme.space.space4};
169
+ border: 1px solid ${theme.color.primary.borderSubtle};
170
+ border-radius: ${theme.radius.radius6};
171
+ display: flex;
172
+ gap: ${theme.space.space8};
173
+ align-items: center;
174
+ justify-content: center;
175
+ }
176
+
177
+ .${actorExamplePreviewClassNames.SCHEMA_FIELD_NAME} {
178
+ min-width: 0;
179
+ text-overflow: ellipsis;
180
+ white-space: nowrap;
181
+ overflow: hidden;
182
+ }
183
+
184
+ .${actorExamplePreviewClassNames.SCHEMA_MORE} {
185
+ height: 2.4rem;
186
+ margin-top: 2rem;
187
+ padding: 0 ${theme.space.space8};
188
+ border: 1px solid ${theme.color.primary.borderSubtle};
189
+ border-radius: ${theme.radius.radius12};
190
+ background-color: ${theme.color.primary.background};
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+ position: relative;
195
+
196
+ &::before {
197
+ top: calc(-1px - 1.6rem);
198
+ left: calc(50% - 0.5px);
199
+ width: 1px;
200
+ height: 0.8rem;
201
+ background-color: ${theme.color.primary.text};
202
+ content: '';
203
+ display: block;
204
+ position: absolute;
205
+ }
206
+ }
207
+ `;
208
+
209
+ export type ActorExamplePreviewField = {
210
+ name: string;
211
+ title?: string;
212
+ };
213
+
214
+ export type ActorExamplePreviewProps = {
215
+ actorName: string;
216
+ actorTitle: string | undefined;
217
+ actorPictureUrl: string | undefined;
218
+ actorPictureFallbackUrl: string;
219
+ outputFields: ActorExamplePreviewField[];
220
+ staticWebUrl: string;
221
+ username: string;
222
+ };
223
+
224
+ export const ActorExamplePreview = ({
225
+ actorName,
226
+ actorTitle,
227
+ actorPictureUrl,
228
+ actorPictureFallbackUrl,
229
+ outputFields,
230
+ staticWebUrl,
231
+ username,
232
+ }: ActorExamplePreviewProps) => {
233
+ const visibleFields = outputFields.slice(0, MAX_VISIBLE_FIELDS);
234
+ const remainingCount = Math.max(0, outputFields.length - MAX_VISIBLE_FIELDS);
235
+
236
+ return (
237
+ <ActorExamplePreviewWrapper
238
+ className={actorExamplePreviewClassNames.CONTAINER}
239
+ $noiseImageUrl={`${staticWebUrl}/img/pattern/noise.svg`}
240
+ >
241
+ <div className={actorExamplePreviewClassNames.CONTENT}>
242
+ <div className={actorExamplePreviewClassNames.BADGE}>
243
+ <ActorExampleAvatar
244
+ title={actorTitle}
245
+ name={actorName}
246
+ pictureUrl={actorPictureUrl}
247
+ fallbackPictureUrl={actorPictureFallbackUrl}
248
+ size={BADGE_PICTURE_SIZE}
249
+ className={actorExamplePreviewClassNames.BADGE_PICTURE}
250
+ />
251
+ <div className={actorExamplePreviewClassNames.BADGE_CONTENT}>
252
+ <Heading as="span" type="titleS" className={actorExamplePreviewClassNames.BADGE_NAME}>
253
+ {actorTitle ?? actorName}
254
+ </Heading>
255
+ <Text
256
+ as="span"
257
+ type="code"
258
+ size="small"
259
+ color={theme.color.neutral.textSubtle}
260
+ className={actorExamplePreviewClassNames.BADGE_TECHNICAL_NAME}
261
+ >
262
+ {username}/{actorName}
263
+ </Text>
264
+ </div>
265
+ </div>
266
+ {visibleFields.length > 0 && (
267
+ <div className={actorExamplePreviewClassNames.SCHEMA}>
268
+ {visibleFields.map((field) => (
269
+ <div key={field.name} className={actorExamplePreviewClassNames.SCHEMA_FIELD}>
270
+ <Text
271
+ as="span"
272
+ size="small"
273
+ color={theme.color.neutral.text}
274
+ className={actorExamplePreviewClassNames.SCHEMA_FIELD_NAME}
275
+ >
276
+ {field.title ?? field.name}
277
+ </Text>
278
+ </div>
279
+ ))}
280
+ {remainingCount > 0 && (
281
+ <Text
282
+ as="span"
283
+ size="small"
284
+ color={theme.color.primary.text}
285
+ className={actorExamplePreviewClassNames.SCHEMA_MORE}
286
+ >
287
+ +{remainingCount} {remainingCount === 1 ? 'field' : 'fields'}
288
+ </Text>
289
+ )}
290
+ </div>
291
+ )}
292
+ </div>
293
+ </ActorExamplePreviewWrapper>
294
+ );
295
+ };
@@ -0,0 +1,61 @@
1
+ import type React from 'react';
2
+
3
+ import { type IconProps, LightningIcon } from '@apify/ui-icons';
4
+
5
+ import { theme } from '../../design_system/theme.js';
6
+ import { type ButtonProps, Button } from '../button.js';
7
+ import type { SharedTextProps } from '../text/text_shared.js';
8
+
9
+ /**
10
+ * Brand-coloured lightning icon — the standard leading slot for the card variant of the run
11
+ * button. Hero-style call sites typically render without an icon; pass this explicitly via
12
+ * `LeftIcon` when you want it.
13
+ */
14
+ export const ActorExampleRunButtonLightningIcon = (props: IconProps) => (
15
+ <LightningIcon {...props} color={theme.color.primary.action} />
16
+ );
17
+
18
+ /**
19
+ * "Run example" CTA shared by the actor-detail Examples tab (apify-core Console) and the
20
+ * public actor-example landing/hero pages (apify-web). Encapsulates two universal defaults
21
+ * for this CTA — `target="_self"` so external `console.apify.com` links don't open in a new
22
+ * tab, and `hideExternalIcon` so the lightning leading icon (when present) is the only
23
+ * action affordance — and pins the analytics shape to `{ element, cardTitle, actorId }`.
24
+ *
25
+ * Tracking flows through the host app's `trackClick` (apify-web's analytics, console's
26
+ * client tracker) via the underlying `Button`.
27
+ */
28
+ export type ActorExampleRunButtonProps = Pick<ButtonProps, 'size' | 'variant' | 'LeftIcon' | 'RightIcon'> & {
29
+ actorId: string;
30
+ consoleUrl: string;
31
+ trackingProps: {
32
+ element: string;
33
+ cardTitle: string;
34
+ };
35
+ textSize?: SharedTextProps['size'];
36
+ className?: string;
37
+ children: React.ReactNode;
38
+ };
39
+
40
+ export const ActorExampleRunButton = ({
41
+ actorId,
42
+ consoleUrl,
43
+ size = 'extraLarge',
44
+ variant = 'primary',
45
+ trackingProps,
46
+ children,
47
+ ...buttonProps
48
+ }: ActorExampleRunButtonProps) => (
49
+ <Button
50
+ {...buttonProps}
51
+ to={consoleUrl}
52
+ size={size}
53
+ variant={variant}
54
+ target="_self"
55
+ hideExternalIcon
56
+ trackingId={trackingProps.element}
57
+ trackingData={{ cardTitle: trackingProps.cardTitle, actorId }}
58
+ >
59
+ {children}
60
+ </Button>
61
+ );