Skip to content

Commit

Permalink
feat: new hook package: @utopia-utils/vueuse
Browse files Browse the repository at this point in the history
  • Loading branch information
GreatAuk committed Nov 6, 2024
1 parent 35a1752 commit 893d044
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 23 deletions.
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cSpell.words": [
"deepmerge"
"deepmerge",
"typecheck"
]
}
}
1 change: 1 addition & 0 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@utopia-utils/core": "workspace:*",
"@utopia-utils/vueuse": "workspace:^",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},
Expand Down
33 changes: 12 additions & 21 deletions example/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
<script setup lang="ts">
import { ref } from 'vue'
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { loadScript } from '@utopia-utils/core'
import { useSmsCountdown } from '@utopia-utils/vueuse'
let unload_
let able = ref(false)
const foo = () => {
console.log('[9]-App.vue', unload_)
unload_()
}
const loadScript_ = () => {
const { unload } = loadScript('https://unpkg.com/browse/[email protected]/index.js', {
appendPosition: 'body',
onStatusChange: (status) => {
console.log(status)
},
})
unload_ = unload
}
const { counts, startCountdown, text, canSend, stopCountdown } = useSmsCountdown({
sendAble: able,
startText: 'send',
durationText: 'x秒重取'
})
</script>

<template>
<header>
<button @click="loadScript_">load</button>
<button @click="foo">unload</button>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />

<button @click="able = !able">Toggle {{ able }}</button>
<h1>{{ counts }}{{ canSend }}</h1>
<button @click="stopCountdown">stop</button>
<button @click="() => startCountdown()">{{ text }}</button>
<div class="wrapper">
<HelloWorld msg="You did it!" />

<nav>
<RouterLink to="/">Home</RouterLink>
</nav>
Expand Down
48 changes: 48 additions & 0 deletions packages/vueuse/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "@utopia-utils/vueuse",
"type": "module",
"version": "0.3.23",
"description": "Collection of common vue hooks",
"author": "Utopia <https://github.com/GreatAuk>",
"license": "MIT",
"homepage": "https://github.com/GreatAuk/utopia-utils#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/GreatAuk/utopia-utils.git",
"directory": "packages/vueuse"
},
"bugs": {
"url": "https://github.com/GreatAuk/utopia-utils/issues"
},
"keywords": [
"utils",
"vue hooks",
"vueuse"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.js"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint .",
"start": "tsx src/index.ts",
"test": "vitest",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"vue": "^3.2.47"
},
"dependencies": {},
"devDependencies": {
"vue": "^3.2.47"
}
}
1 change: 1 addition & 0 deletions packages/vueuse/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useSmsCountdown'
29 changes: 29 additions & 0 deletions packages/vueuse/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Ref } from 'vue'

/**
* Maybe it's a ref, or a plain value
*
* ```ts
* type MaybeRef<T> = T | Ref<T>
* ```
*/
export type MaybeRef<T> = T | Ref<T>

/**
* Maybe it's a ref, or a plain value, or a getter function
*
* ```ts
* type MaybeRefOrGetter<T> = (() => T) | T | Ref<T> | ComputedRef<T>
* ```
*/
export type MaybeRefOrGetter<T> = MaybeRef<T> | (() => T)

/**
* Void function
*/
export type Fn = () => void

/**
* Any function
*/
export type AnyFn = (...args: any[]) => any
81 changes: 81 additions & 0 deletions packages/vueuse/src/useSmsCountdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { computed, ref } from 'vue'

import type { MaybeRef } from '../types'
import { toValue, tryOnScopeDispose } from '../utils'

export interface UseSmsCountdownOptions {
/**
* 倒计时总共的时间(s)
* @default 60s
*/
totalSecond?: number
/** 是否可发送
* @default true
*/
sendAble?: MaybeRef<boolean>
/**
* 开始倒计时的文本
* @default '获取验证码'
*/
startText?: string
/**
* 倒计时期间的提示语,必须带有字符 "%s",这里的 "%s",将会被倒计的秒数替代
* @default '%s秒后重发'
*/
durationText?: string
}

export function useSmsCountdown(options?: UseSmsCountdownOptions) {
const { totalSecond = 60, sendAble = true, startText = '获取验证码', durationText = 'x秒后重发' } = options || {}

if (totalSecond <= 0 && totalSecond % 1 !== 0)
throw new Error('倒计时的时间应该为一个正整数!')

const counts = ref(totalSecond)

/** 是否可以发送验证码,由外部转入的 sendAble 和当前的秒数共同确定 */
const canSend = computed(() => {
return toValue(sendAble) && counts.value === totalSecond
})

const text = computed(() => {
if (counts.value === totalSecond)
return startText

return durationText.replace(/%s/i, counts.value.toString())
})

let intervalId: ReturnType<typeof setInterval> | null = null

function startCountdown() {
if (!canSend.value)
return

counts.value--
intervalId = setInterval(() => {
counts.value--
if (counts.value <= 0) {
counts.value = totalSecond
clearInterval(intervalId!)
}
}, 1000)
}

/**
* 停止计时
*/
function stopCountdown() {
intervalId && clearInterval(intervalId)
intervalId = null
counts.value = totalSecond
}
tryOnScopeDispose(stopCountdown)

return {
counts,
canSend,
text,
startCountdown,
stopCountdown,
}
}
2 changes: 2 additions & 0 deletions packages/vueuse/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './tryOnScopeDispose'
export * from './toValue'
11 changes: 11 additions & 0 deletions packages/vueuse/src/utils/toValue/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { unref } from 'vue'
import type { AnyFn, MaybeRefOrGetter } from '../../types'

/**
* Get the value of value/ref/getter.
*/
export function toValue<T>(r: MaybeRefOrGetter<T>): T {
return typeof r === 'function'
? (r as AnyFn)()
: unref(r)
}
15 changes: 15 additions & 0 deletions packages/vueuse/src/utils/tryOnScopeDispose/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getCurrentScope, onScopeDispose } from 'vue'
import type { Fn } from '../../types'

/**
* Call onScopeDispose() if it's inside an effect scope lifecycle, if not, do nothing
*
* @param fn
*/
export function tryOnScopeDispose(fn: Fn) {
if (getCurrentScope()) {
onScopeDispose(fn)
return true
}
return false
}
14 changes: 14 additions & 0 deletions packages/vueuse/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from 'tsup'

export default defineConfig((options) => { // The options here is derived from CLI flags.
return {
entry: {
index: 'src/index.ts',
},
sourcemap: true,
clean: true,
dts: true,
format: ['cjs', 'esm'],
minify: !options.watch,
}
})
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 893d044

Please sign in to comment.