Skip to content

Commit

Permalink
chore: seperate progress components (#231)
Browse files Browse the repository at this point in the history
* chore: seperate progress components

* Update packages/components/progress/src/progressIndicator.ts

Co-authored-by: Mehmet <hi@productdevbook.com>

* chore: constant

* Update packages/components/progress/src/constants.ts
  • Loading branch information
dammy001 committed Jul 20, 2023
1 parent 261f4f1 commit 05d44cf
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 159 deletions.
3 changes: 3 additions & 0 deletions packages/components/progress/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PROGRESS_NAME = 'OkuProgress'
export const DEFAULT_MAX = 100
export const INDICATOR_NAME = 'OkuProgressIndicator'
21 changes: 9 additions & 12 deletions packages/components/progress/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
export {
OkuProgress,
OkuProgressIndicator,
createProgressScope,
} from './progress'
export { OkuProgress, createProgressScope } from './progress'

export type{
ProgressProps,
ProgressIndicatorProps,
ProgressElement,
ProgressIndicatorElement,
ProgressRef,
export type { ProgressProps, ProgressElement, ProgressRef } from './progress'

export { OkuProgressIndicator } from './progressIndicator'

export type {
ProgressIndicatorRef,
} from './progress'
ProgressIndicatorElement,
ProgressIndicatorProps,
} from './progressIndicator'
205 changes: 58 additions & 147 deletions packages/components/progress/src/progress.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
import type { ElementType, MergeProps, PrimitiveProps, RefElement } from '@oku-ui/primitive'
import type { ElementType, MergeProps, RefElement } from '@oku-ui/primitive'
import { Primitive } from '@oku-ui/primitive'
import type { Scope } from '@oku-ui/provide'
import { createProvideScope } from '@oku-ui/provide'
import type { ComputedRef, PropType } from 'vue'
import { computed, defineComponent, h, toRefs } from 'vue'
import { useRef } from '@oku-ui/use-composable'
import {
defaultGetValueLabel,
getInvalidMaxError,
getInvalidValueError,
getProgressState,
isNumber,
isValidMaxNumber,
isValidValueNumber,
} from './utils'
import { DEFAULT_MAX, PROGRESS_NAME } from './constants'
import type { ProgressIndicatorProps } from '.'

// ---------- Progress ---------- //

type ProgressContextValue = { value: ComputedRef<number | null> | null; max: ComputedRef<number> }
type ProgressContextValue = {
value: ComputedRef<number | null> | null
max: ComputedRef<number>
}

type ProgressElement = ElementType<'div'>
type ProgressState = 'indeterminate' | 'complete' | 'loading'

interface ProgressProps {
value?: number | null
Expand All @@ -19,11 +33,8 @@ interface ProgressProps {
scopeProgress?: Scope
}

// ---constants---
const PROGRESS_NAME = 'Progress'
const DEFAULT_MAX = 100

const [createProgressContext, createProgressScope] = createProvideScope(PROGRESS_NAME)
const [createProgressContext, createProgressScope]
= createProvideScope(PROGRESS_NAME)

const [progressProvider, useProgressContext]
= createProgressContext<ProgressContextValue>(PROGRESS_NAME)
Expand Down Expand Up @@ -51,9 +62,7 @@ const Progress = defineComponent({
},
setup(props, { attrs, slots, expose }) {
const { value, max, getValueLabel, scopeProgress } = toRefs(props)
const {
...progressProps
} = attrs as ProgressElement
const { ...progressProps } = attrs as ProgressElement

// propstype check
if (max.value && !isValidMaxNumber(max.value))
Expand All @@ -64,28 +73,41 @@ const Progress = defineComponent({

const { $el, newRef } = useRef<HTMLDivElement>()

const maxProp = computed(() => isValidMaxNumber(max.value) ? max.value : DEFAULT_MAX)
const valueProp = computed(() => isValidValueNumber(value.value, maxProp.value) ? value.value : null)
const valueLabel = computed(() => isNumber(valueProp.value) ? getValueLabel.value(valueProp.value, maxProp.value) : undefined)

const originalReturn = () => h(
Primitive.div,
{
'aria-valuemax': maxProp.value,
'aria-valuemin': 0,
'aria-valuenow': isNumber(valueProp.value) ? valueProp.value : undefined,
'aria-valuetext': valueLabel.value,
'role': 'progressbar',
'data-state': computed(() => getProgressState(maxProp.value, valueProp.value)).value,
'data-value': valueProp.value ?? undefined,
'data-max': maxProp.value,
...progressProps,
'ref': newRef,
},
{
default: () => slots.default?.(),
},
const maxProp = computed(() =>
isValidMaxNumber(max.value) ? max.value : DEFAULT_MAX,
)
const valueProp = computed(() =>
isValidValueNumber(value.value, maxProp.value) ? value.value : null,
)
const valueLabel = computed(() =>
isNumber(valueProp.value)
? getValueLabel.value(valueProp.value, maxProp.value)
: undefined,
)

const originalReturn = () =>
h(
Primitive.div,
{
'aria-valuemax': maxProp.value,
'aria-valuemin': 0,
'aria-valuenow': isNumber(valueProp.value)
? valueProp.value
: undefined,
'aria-valuetext': valueLabel.value,
'role': 'progressbar',
'data-state': computed(() =>
getProgressState(maxProp.value, valueProp.value),
).value,
'data-value': valueProp.value ?? undefined,
'data-max': maxProp.value,
...progressProps,
'ref': newRef,
},
{
default: () => slots.default?.(),
},
)

expose({
inferRef: $el,
Expand All @@ -103,125 +125,14 @@ const Progress = defineComponent({
},
})

// ---function---

function defaultGetValueLabel(value: number, max: number) {
return `${Math.round((value / max) * 100)}%`
}

function isNumber(value: any): value is number {
return typeof value === 'number'
}

function isValidMaxNumber(max: any): max is number {
return (
isNumber(max)
&& !Number.isNaN(max)
&& max > 0
)
}

function isValidValueNumber(value: any, max: number): value is number {
return (
isNumber(value)
&& !Number.isNaN(value)
&& value <= max
&& value >= 0
)
}

function getProgressState(maxValue: number, value?: number | null): ProgressState {
return value == null ? 'indeterminate' : value === maxValue ? 'complete' : 'loading'
}

function getInvalidMaxError(propValue: string) {
return `Invalid prop \`max\` of value \`${propValue}\` supplied to \`${PROGRESS_NAME}\`. Only numbers greater than 0 are valid max values. Defaulting to \`${DEFAULT_MAX}\`.`
}

function getInvalidValueError(propValue: string) {
return `Invalid prop \`value\` of value \`${propValue}\` supplied to \`${PROGRESS_NAME}\`. The \`value\` prop must be:
- a positive number
- less than the value passed to \`max\` (or ${DEFAULT_MAX} if no \`max\` prop is set)
- \`null\` if the progress is indeterminate.
Defaulting to \`null\`.`
}

// ---------- ProgressIndicator

// ---constants---

const INDICATOR_NAME = 'ProgressIndicator'

// ---component---
type ProgressIndicatorElement = ElementType<'div'>
interface ProgressIndicatorProps extends PrimitiveProps {
scopeProgress?: Scope
}

const ProgressIndicator = defineComponent({
name: INDICATOR_NAME,
inheritAttrs: true,
props: {
scopeProgress: {
type: Object as unknown as PropType<Scope>,
required: false,
},
},
setup(props, { attrs, slots, expose }) {
const { scopeProgress } = props
const {
...indicatorProps
} = attrs as ProgressIndicatorProps

const { $el, newRef } = useRef<HTMLDivElement>()

const context = useProgressContext(INDICATOR_NAME, scopeProgress)

expose({
inferRef: $el,
})

const originalReturn = () => h(
'div',
{
'data-state': getProgressState(context.value.max.value, context.value.value?.value),
'data-value': context.value.value?.value ?? undefined,
'data-max': context.value.max.value,
...indicatorProps,
'ref': newRef,
},
{
default: () => slots.default?.(),
})

return originalReturn as unknown as {
innerRef: ProgressIndicatorElement
}
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
type _OkuProgressProps = MergeProps<ProgressProps, ProgressIndicatorProps>
type _OkuProgressIndicatorProps = MergeProps<ProgressIndicatorProps, PrimitiveProps>

type ProgressRef = RefElement<typeof Progress>
type ProgressIndicatorRef = RefElement<typeof ProgressIndicator>

const OkuProgress = Progress as typeof Progress & (new () => { $props: _OkuProgressProps })
const OkuProgressIndicator = ProgressIndicator as typeof ProgressIndicator & (new () => { $props: _OkuProgressIndicatorProps })
const OkuProgress = Progress as typeof Progress &
(new () => { $props: _OkuProgressProps })

export {
createProgressScope,
OkuProgress,
OkuProgressIndicator,
}
export { createProgressScope, OkuProgress, useProgressContext }

export type {
ProgressProps,
ProgressIndicatorProps,
ProgressElement,
ProgressIndicatorElement,
ProgressRef,
ProgressIndicatorRef,
}
export type { ProgressProps, ProgressElement, ProgressRef }
83 changes: 83 additions & 0 deletions packages/components/progress/src/progressIndicator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { PropType } from 'vue'
import { defineComponent, h } from 'vue'
import type {
ElementType,
MergeProps,
PrimitiveProps,
RefElement,
} from '@oku-ui/primitive'
import type { Scope } from '@oku-ui/provide'
import { useRef } from '@oku-ui/use-composable'
import { getProgressState } from './utils'
import { useProgressContext } from './progress'
import { INDICATOR_NAME } from './constants'

// ---component---
type ProgressIndicatorElement = ElementType<'div'>

interface ProgressIndicatorProps extends PrimitiveProps {
scopeProgress?: Scope
}

const ProgressIndicator = defineComponent({
name: INDICATOR_NAME,
inheritAttrs: true,
props: {
scopeProgress: {
type: Object as unknown as PropType<Scope>,
required: false,
},
},
setup(props, { attrs, slots, expose }) {
const { scopeProgress } = props
const { ...indicatorProps } = attrs as ProgressIndicatorProps

const { $el, newRef } = useRef<HTMLDivElement>()

const context = useProgressContext(INDICATOR_NAME, scopeProgress)

expose({
inferRef: $el,
})

const originalReturn = () =>
h(
'div',
{
'data-state': getProgressState(
context.value.max.value,
context.value.value?.value,
),
'data-value': context.value.value?.value ?? undefined,
'data-max': context.value.max.value,
...indicatorProps,
'ref': newRef,
},
{
default: () => slots.default?.(),
},
)

return originalReturn as unknown as {
innerRef: ProgressIndicatorElement
}
},
})

type _OkuProgressIndicatorProps = MergeProps<
ProgressIndicatorProps,
PrimitiveProps
>

const OkuProgressIndicator = ProgressIndicator as typeof ProgressIndicator &
(new () => { $props: _OkuProgressIndicatorProps })

type ProgressIndicatorRef = RefElement<typeof ProgressIndicator>

export { OkuProgressIndicator }

export type {
ProgressIndicatorProps,
ProgressIndicatorElement,
ProgressIndicatorRef,
}
Loading

0 comments on commit 05d44cf

Please sign in to comment.