@hanzogui/progress 2.0.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/LICENSE +21 -0
- package/dist/cjs/Progress.cjs +171 -0
- package/dist/cjs/Progress.native.js +178 -0
- package/dist/cjs/Progress.native.js.map +1 -0
- package/dist/cjs/index.cjs +18 -0
- package/dist/cjs/index.native.js +21 -0
- package/dist/cjs/index.native.js.map +1 -0
- package/dist/esm/Progress.mjs +144 -0
- package/dist/esm/Progress.mjs.map +1 -0
- package/dist/esm/Progress.native.js +148 -0
- package/dist/esm/Progress.native.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/index.mjs +2 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +2 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/jsx/Progress.mjs +144 -0
- package/dist/jsx/Progress.mjs.map +1 -0
- package/dist/jsx/Progress.native.js +178 -0
- package/dist/jsx/Progress.native.js.map +1 -0
- package/dist/jsx/index.js +2 -0
- package/dist/jsx/index.js.map +1 -0
- package/dist/jsx/index.mjs +2 -0
- package/dist/jsx/index.mjs.map +1 -0
- package/dist/jsx/index.native.js +21 -0
- package/dist/jsx/index.native.js.map +1 -0
- package/package.json +56 -0
- package/src/Progress.tsx +223 -0
- package/src/index.ts +1 -0
- package/types/Progress.d.ts +69 -0
- package/types/index.d.ts +2 -0
package/src/Progress.tsx
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// forked from Radix UI
|
|
2
|
+
// https://github.com/radix-ui/primitives/blob/main/packages/react/progress/src/Progress.tsx
|
|
3
|
+
|
|
4
|
+
import type { GetProps } from '@hanzogui/core'
|
|
5
|
+
import { getVariableValue, isWeb, styled } from '@hanzogui/core'
|
|
6
|
+
import type { Scope } from '@hanzogui/create-context'
|
|
7
|
+
import { createContextScope } from '@hanzogui/create-context'
|
|
8
|
+
import { getSize } from '@hanzogui/get-token'
|
|
9
|
+
import { withStaticProperties } from '@hanzogui/helpers'
|
|
10
|
+
import { YStack } from '@hanzogui/stacks'
|
|
11
|
+
import { useState } from 'react'
|
|
12
|
+
|
|
13
|
+
const PROGRESS_NAME = 'Progress'
|
|
14
|
+
|
|
15
|
+
const [createProgressContext, createProgressScope] = createContextScope(PROGRESS_NAME)
|
|
16
|
+
|
|
17
|
+
type ProgressContextValue = {
|
|
18
|
+
value: number | null
|
|
19
|
+
max: number
|
|
20
|
+
width: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const [ProgressProvider, useProgressContext] =
|
|
24
|
+
createProgressContext<ProgressContextValue>(PROGRESS_NAME)
|
|
25
|
+
|
|
26
|
+
/* -------------------------------------------------------------------------------------------------
|
|
27
|
+
* ProgressIndicator
|
|
28
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
29
|
+
|
|
30
|
+
const INDICATOR_NAME = 'ProgressIndicator'
|
|
31
|
+
|
|
32
|
+
export const ProgressIndicatorFrame = styled(YStack, {
|
|
33
|
+
name: INDICATOR_NAME,
|
|
34
|
+
|
|
35
|
+
variants: {
|
|
36
|
+
unstyled: {
|
|
37
|
+
false: {
|
|
38
|
+
height: '100%',
|
|
39
|
+
width: '100%',
|
|
40
|
+
backgroundColor: '$background',
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
} as const,
|
|
44
|
+
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
unstyled: process.env.HANZO_GUI_HEADLESS === '1',
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export type ProgressIndicatorProps = GetProps<typeof ProgressIndicatorFrame>
|
|
51
|
+
|
|
52
|
+
const ProgressIndicator = ProgressIndicatorFrame.styleable(function ProgressIndicator(
|
|
53
|
+
props: ScopedProps<ProgressIndicatorProps>,
|
|
54
|
+
forwardedRef
|
|
55
|
+
) {
|
|
56
|
+
const { __scopeProgress, transition, ...indicatorProps } = props
|
|
57
|
+
const context = useProgressContext(INDICATOR_NAME, __scopeProgress)
|
|
58
|
+
|
|
59
|
+
const progressRatio = (context.value ?? 0) / context.max
|
|
60
|
+
|
|
61
|
+
// indicator is 2x container width so bouncy animations can overshoot
|
|
62
|
+
// without visually extending past the right edge (parent has overflow:hidden)
|
|
63
|
+
// translateX percentage is relative to element's own width (200% of container)
|
|
64
|
+
// so we divide by 2 to get container-relative positioning:
|
|
65
|
+
// at 0%: x = -100% of element = -200% of container (fully hidden left)
|
|
66
|
+
// at 100%: x = -50% of element = -100% of container (right half visible)
|
|
67
|
+
let x: string | number
|
|
68
|
+
if (isWeb) {
|
|
69
|
+
// web: use percentage-based translateX for SSR-friendly rendering
|
|
70
|
+
// formula: -100% + (progressRatio * 50%) since translateX % is relative to element width
|
|
71
|
+
x = `${-100 + progressRatio * 50}%`
|
|
72
|
+
} else {
|
|
73
|
+
// native: use pixel-based transform (RN doesn't support percentage transforms reliably)
|
|
74
|
+
const baseWidth = context.width || 0
|
|
75
|
+
x = Math.ceil(-baseWidth * (2 - progressRatio))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<ProgressIndicatorFrame
|
|
80
|
+
data-state={getProgressState(context.value, context.max)}
|
|
81
|
+
data-value={context.value ?? undefined}
|
|
82
|
+
data-max={context.max}
|
|
83
|
+
x={x}
|
|
84
|
+
width="200%"
|
|
85
|
+
{...(!props.unstyled && {
|
|
86
|
+
animateOnly: ['transform'],
|
|
87
|
+
// on native, hide until we have width measurement
|
|
88
|
+
...(!isWeb && context.width === 0 && { opacity: 0 }),
|
|
89
|
+
})}
|
|
90
|
+
{...indicatorProps}
|
|
91
|
+
ref={forwardedRef}
|
|
92
|
+
transition={!isWeb && !context.width ? null : transition}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
/* ---------------------------------------------------------------------------------------------- */
|
|
98
|
+
|
|
99
|
+
function defaultGetValueLabel(value: number, max: number) {
|
|
100
|
+
return `${Math.round((value / max) * 100)}%`
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getProgressState(
|
|
104
|
+
value: number | undefined | null,
|
|
105
|
+
maxValue: number
|
|
106
|
+
): ProgressState {
|
|
107
|
+
return value == null ? 'indeterminate' : value === maxValue ? 'complete' : 'loading'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isNumber(value: any): value is number {
|
|
111
|
+
return typeof value === 'number'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isValidMaxNumber(max: any): max is number {
|
|
115
|
+
return isNumber(max) && !Number.isNaN(max) && max > 0
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isValidValueNumber(value: any, max: number): value is number {
|
|
119
|
+
return isNumber(value) && !Number.isNaN(value) && value <= max && value >= 0
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* -------------------------------------------------------------------------------------------------
|
|
123
|
+
* Progress
|
|
124
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
125
|
+
|
|
126
|
+
const DEFAULT_MAX = 100
|
|
127
|
+
|
|
128
|
+
type ScopedProps<P> = P & { __scopeProgress?: Scope }
|
|
129
|
+
|
|
130
|
+
type ProgressState = 'indeterminate' | 'complete' | 'loading'
|
|
131
|
+
|
|
132
|
+
export const ProgressFrame = styled(YStack, {
|
|
133
|
+
name: 'Progress',
|
|
134
|
+
|
|
135
|
+
variants: {
|
|
136
|
+
unstyled: {
|
|
137
|
+
false: {
|
|
138
|
+
borderRadius: 100_000,
|
|
139
|
+
overflow: 'hidden',
|
|
140
|
+
backgroundColor: '$background',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
size: {
|
|
145
|
+
'...size': (val) => {
|
|
146
|
+
const size = Math.round(getVariableValue(getSize(val)) * 0.25)
|
|
147
|
+
return {
|
|
148
|
+
height: size,
|
|
149
|
+
minWidth: getVariableValue(size) * 20,
|
|
150
|
+
width: '100%',
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
} as const,
|
|
155
|
+
|
|
156
|
+
defaultVariants: {
|
|
157
|
+
unstyled: process.env.HANZO_GUI_HEADLESS === '1',
|
|
158
|
+
},
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
export interface ProgressExtraProps {
|
|
162
|
+
value?: number | null | undefined
|
|
163
|
+
max?: number
|
|
164
|
+
getValueLabel?(value: number, max: number): string
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export type ProgressProps = GetProps<typeof ProgressFrame> & ProgressExtraProps
|
|
168
|
+
|
|
169
|
+
const Progress = withStaticProperties(
|
|
170
|
+
ProgressFrame.styleable<ProgressExtraProps>(function Progress(props, forwardedRef) {
|
|
171
|
+
const {
|
|
172
|
+
// @ts-expect-error
|
|
173
|
+
__scopeProgress,
|
|
174
|
+
value: valueProp,
|
|
175
|
+
max: maxProp,
|
|
176
|
+
getValueLabel = defaultGetValueLabel,
|
|
177
|
+
size = '$true',
|
|
178
|
+
...progressProps
|
|
179
|
+
} = props
|
|
180
|
+
|
|
181
|
+
const max = isValidMaxNumber(maxProp) ? maxProp : DEFAULT_MAX
|
|
182
|
+
const value = isValidValueNumber(valueProp, max) ? Math.round(valueProp) : null
|
|
183
|
+
const valueLabel = isNumber(value) ? getValueLabel(value, max) : undefined
|
|
184
|
+
|
|
185
|
+
// only needed for native where we can't use percentage-based transforms
|
|
186
|
+
const [width, setWidth] = useState(0)
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<ProgressProvider scope={__scopeProgress} value={value} max={max} width={width}>
|
|
190
|
+
<ProgressFrame
|
|
191
|
+
aria-valuemax={max}
|
|
192
|
+
aria-valuemin={0}
|
|
193
|
+
aria-valuenow={isNumber(value) ? value : undefined}
|
|
194
|
+
aria-valuetext={valueLabel}
|
|
195
|
+
// @ts-ignore
|
|
196
|
+
role="progressbar"
|
|
197
|
+
data-state={getProgressState(value, max)}
|
|
198
|
+
data-value={value ?? undefined}
|
|
199
|
+
data-max={max}
|
|
200
|
+
{...(progressProps.unstyled !== true && {
|
|
201
|
+
size,
|
|
202
|
+
})}
|
|
203
|
+
{...progressProps}
|
|
204
|
+
{...(!isWeb && {
|
|
205
|
+
onLayout: (e) => {
|
|
206
|
+
const newWidth = Math.round(e.nativeEvent.layout.width)
|
|
207
|
+
if (newWidth !== width) {
|
|
208
|
+
setWidth(newWidth)
|
|
209
|
+
}
|
|
210
|
+
progressProps.onLayout?.(e)
|
|
211
|
+
},
|
|
212
|
+
})}
|
|
213
|
+
ref={forwardedRef}
|
|
214
|
+
/>
|
|
215
|
+
</ProgressProvider>
|
|
216
|
+
)
|
|
217
|
+
}),
|
|
218
|
+
{
|
|
219
|
+
Indicator: ProgressIndicator,
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
export { createProgressScope, Progress, ProgressIndicator }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Progress'
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { GetProps } from '@gui/core';
|
|
2
|
+
declare const createProgressScope: import("@gui/create-context").CreateScope;
|
|
3
|
+
export declare const ProgressIndicatorFrame: import("@gui/core").GuiComponent<import("@gui/core").TamaDefer, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
4
|
+
unstyled?: boolean | undefined;
|
|
5
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
6
|
+
fullscreen?: boolean | undefined;
|
|
7
|
+
}, import("@gui/core").StaticConfigPublic>;
|
|
8
|
+
export type ProgressIndicatorProps = GetProps<typeof ProgressIndicatorFrame>;
|
|
9
|
+
declare const ProgressIndicator: import("@gui/core").GuiComponent<import("@gui/core").GetFinalProps<import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
10
|
+
unstyled?: boolean | undefined;
|
|
11
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
12
|
+
fullscreen?: boolean | undefined;
|
|
13
|
+
}>, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps & void, import("@gui/core").StackStyleBase, {
|
|
14
|
+
unstyled?: boolean | undefined;
|
|
15
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
16
|
+
fullscreen?: boolean | undefined;
|
|
17
|
+
}, import("@gui/core").StaticConfigPublic>;
|
|
18
|
+
export declare const ProgressFrame: import("@gui/core").GuiComponent<import("@gui/core").TamaDefer, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
19
|
+
unstyled?: boolean | undefined;
|
|
20
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
21
|
+
fullscreen?: boolean | undefined;
|
|
22
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
23
|
+
}, import("@gui/core").StaticConfigPublic>;
|
|
24
|
+
export interface ProgressExtraProps {
|
|
25
|
+
value?: number | null | undefined;
|
|
26
|
+
max?: number;
|
|
27
|
+
getValueLabel?(value: number, max: number): string;
|
|
28
|
+
}
|
|
29
|
+
export type ProgressProps = GetProps<typeof ProgressFrame> & ProgressExtraProps;
|
|
30
|
+
declare const Progress: import("react").ForwardRefExoticComponent<Omit<import("@gui/core").GetFinalProps<import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
31
|
+
unstyled?: boolean | undefined;
|
|
32
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
33
|
+
fullscreen?: boolean | undefined;
|
|
34
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
35
|
+
}>, keyof ProgressExtraProps> & ProgressExtraProps & import("react").RefAttributes<import("@gui/core").GuiElement>> & import("@gui/core").StaticComponentObject<Omit<import("@gui/core").GetFinalProps<import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
36
|
+
unstyled?: boolean | undefined;
|
|
37
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
38
|
+
fullscreen?: boolean | undefined;
|
|
39
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
40
|
+
}>, keyof ProgressExtraProps> & ProgressExtraProps, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps & ProgressExtraProps, import("@gui/core").StackStyleBase, {
|
|
41
|
+
unstyled?: boolean | undefined;
|
|
42
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
43
|
+
fullscreen?: boolean | undefined;
|
|
44
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
45
|
+
}, import("@gui/core").StaticConfigPublic> & Omit<import("@gui/core").StaticConfigPublic, "staticConfig" | "styleable"> & {
|
|
46
|
+
__tama: [Omit<import("@gui/core").GetFinalProps<import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
47
|
+
unstyled?: boolean | undefined;
|
|
48
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
49
|
+
fullscreen?: boolean | undefined;
|
|
50
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
51
|
+
}>, keyof ProgressExtraProps> & ProgressExtraProps, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps & ProgressExtraProps, import("@gui/core").StackStyleBase, {
|
|
52
|
+
unstyled?: boolean | undefined;
|
|
53
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
54
|
+
fullscreen?: boolean | undefined;
|
|
55
|
+
size?: import("@gui/core").SizeTokens | undefined;
|
|
56
|
+
}, import("@gui/core").StaticConfigPublic];
|
|
57
|
+
} & {
|
|
58
|
+
Indicator: import("@gui/core").GuiComponent<import("@gui/core").GetFinalProps<import("@gui/core").RNGuiViewNonStyleProps, import("@gui/core").StackStyleBase, {
|
|
59
|
+
unstyled?: boolean | undefined;
|
|
60
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
61
|
+
fullscreen?: boolean | undefined;
|
|
62
|
+
}>, import("@gui/core").GuiElement, import("@gui/core").RNGuiViewNonStyleProps & void, import("@gui/core").StackStyleBase, {
|
|
63
|
+
unstyled?: boolean | undefined;
|
|
64
|
+
elevation?: number | import("@gui/core").SizeTokens | undefined;
|
|
65
|
+
fullscreen?: boolean | undefined;
|
|
66
|
+
}, import("@gui/core").StaticConfigPublic>;
|
|
67
|
+
};
|
|
68
|
+
export { createProgressScope, Progress, ProgressIndicator };
|
|
69
|
+
//# sourceMappingURL=Progress.d.ts.map
|
package/types/index.d.ts
ADDED