@sdata/web-vue 3.19.0 → 3.20.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.
Files changed (57) hide show
  1. package/dist/sd.css +11 -0
  2. package/dist/sd.min.css +1 -1
  3. package/es/basic-crud-table/basic-crud-table.vue_vue_type_script_setup_true_lang.js +6 -4
  4. package/es/basic-crud-table/types.d.ts +2 -0
  5. package/es/card/card.d.ts +22 -0
  6. package/es/card/card.js +11 -2
  7. package/es/card/index.d.ts +39 -0
  8. package/es/cascader/base-cascader-panel.d.ts +3 -0
  9. package/es/cascader/base-cascader-panel.js +2 -1
  10. package/es/cascader/cascader-search-panel.d.ts +3 -0
  11. package/es/cascader/cascader-search-panel.js +2 -1
  12. package/es/cascader/cascader.vue_vue_type_script_setup_true_lang.js +10 -2
  13. package/es/cascader/types.d.ts +2 -0
  14. package/es/config-provider/config-provider.vue.d.ts +67 -0
  15. package/es/config-provider/config-provider.vue_vue_type_script_setup_true_lang.js +67 -1
  16. package/es/config-provider/context.d.ts +12 -0
  17. package/es/config-provider/index.d.ts +99 -0
  18. package/es/drawer/drawer.vue_vue_type_script_setup_true_lang.js +1 -1
  19. package/es/index.css +11 -0
  20. package/es/index.d.ts +2 -2
  21. package/es/list/index.d.ts +9 -0
  22. package/es/list/list.d.ts +7 -0
  23. package/es/list/list.js +8 -2
  24. package/es/modal/modal.vue_vue_type_script_setup_true_lang.js +1 -1
  25. package/es/qr-code/index.d.ts +18 -12
  26. package/es/qr-code/qr-code.vue.d.ts +5 -3
  27. package/es/qr-code/qr-code.vue_vue_type_script_setup_true_lang.js +5 -2
  28. package/es/select/index.d.ts +9 -0
  29. package/es/select/select-dropdown.vue.d.ts +3 -0
  30. package/es/select/select-dropdown.vue_vue_type_script_setup_true_lang.js +2 -4
  31. package/es/select/select.d.ts +7 -0
  32. package/es/select/select.js +10 -1
  33. package/es/sender/index.d.ts +1 -1
  34. package/es/sender/sender.vue_vue_type_script_setup_true_lang.js +117 -93
  35. package/es/sender/style/index.css +11 -0
  36. package/es/sender/style/index.scss +13 -0
  37. package/es/sender/types.d.ts +60 -3
  38. package/es/sender/use-speech.d.ts +12 -2
  39. package/es/sender/use-speech.js +293 -75
  40. package/es/spin/index.d.ts +15 -0
  41. package/es/spin/spin.d.ts +9 -0
  42. package/es/spin/spin.js +72 -10
  43. package/es/table/index.d.ts +51 -6
  44. package/es/table/interface.d.ts +3 -0
  45. package/es/table/table.d.ts +22 -3
  46. package/es/table/table.js +7 -2
  47. package/es/timeline/index.d.ts +39 -0
  48. package/es/timeline/timeline.d.ts +22 -0
  49. package/es/timeline/timeline.js +11 -2
  50. package/es/toolbar/toolbar.vue_vue_type_script_setup_true_lang.js +9 -5
  51. package/es/toolbar/types.d.ts +2 -0
  52. package/es/tree-select/tree-select.vue.d.ts +7 -0
  53. package/es/tree-select/tree-select.vue_vue_type_script_setup_true_lang.js +7 -1
  54. package/json/vetur-attributes.json +63 -3
  55. package/json/vetur-tags.json +20 -0
  56. package/json/web-types.json +163 -3
  57. package/package.json +1 -1
@@ -1,113 +1,331 @@
1
+ import _objectSpread2 from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/objectSpread2.js";
1
2
  import { useI18n } from "../locale/index.js";
2
3
  import _asyncToGenerator from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/asyncToGenerator.js";
3
- import { computed, shallowRef, toValue, watch } from "vue";
4
- import { usePermission, useSpeechRecognition, useUserMedia } from "@vueuse/core";
4
+ import { computed, onBeforeUnmount, onMounted, shallowRef, toValue, watch } from "vue";
5
5
  //#region components/sender/use-speech.ts
6
+ var DEFAULT_PROCESSOR_NAME = "sd-sender-audio-processor";
7
+ var DEFAULT_BUFFER_SIZE = 4096;
8
+ var resolveBufferSize = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(128, Math.floor(value)) : DEFAULT_BUFFER_SIZE;
9
+ var WORKLET_SOURCE = `
10
+ class SdSenderAudioProcessor extends AudioWorkletProcessor {
11
+ constructor(options) {
12
+ super();
13
+ this.bufferSize = Math.max(128, options.processorOptions?.bufferSize || ${DEFAULT_BUFFER_SIZE});
14
+ this.buffer = new Float32Array(this.bufferSize);
15
+ this.offset = 0;
16
+ this.port.onmessage = (event) => {
17
+ if (event.data?.type !== 'flush') return;
18
+ if (this.offset > 0) {
19
+ const chunk = this.buffer.slice(0, this.offset);
20
+ this.port.postMessage(chunk.buffer, [chunk.buffer]);
21
+ this.offset = 0;
22
+ }
23
+ this.port.postMessage({ type: 'flushed' });
24
+ };
25
+ }
26
+
27
+ process(inputs) {
28
+ const channels = inputs[0];
29
+ if (!channels?.length) return true;
30
+ const frames = channels[0].length;
31
+ for (let frame = 0; frame < frames; frame += 1) {
32
+ let sample = 0;
33
+ for (let channel = 0; channel < channels.length; channel += 1) {
34
+ sample += channels[channel][frame] || 0;
35
+ }
36
+ this.buffer[this.offset] = sample / channels.length;
37
+ this.offset += 1;
38
+ if (this.offset === this.bufferSize) {
39
+ const chunk = this.buffer;
40
+ this.port.postMessage(chunk.buffer, [chunk.buffer]);
41
+ this.buffer = new Float32Array(this.bufferSize);
42
+ this.offset = 0;
43
+ }
44
+ }
45
+ return true;
46
+ }
47
+ }
48
+ registerProcessor('${DEFAULT_PROCESSOR_NAME}', SdSenderAudioProcessor);
49
+ `;
6
50
  var isPermissionDeniedError = (error) => error instanceof DOMException ? error.name === "NotAllowedError" || error.name === "SecurityError" : error instanceof Error && (error.name === "NotAllowedError" || error.name === "SecurityError");
7
- function useSpeech(allowSpeech, onTranscript) {
51
+ function useSpeech(allowSpeech, options) {
8
52
  const { t } = useI18n();
9
53
  const config = computed(() => toValue(allowSpeech));
10
- const controlled = computed(() => typeof config.value === "object" && typeof config.value.recording === "boolean");
11
- const forceBreak = shallowRef(false);
54
+ const controlled = computed(() => typeof config.value === "object" && typeof config.value.recording === "boolean" && typeof config.value.onRecordingChange === "function");
55
+ const supported = shallowRef(false);
56
+ const internalRecording = shallowRef(false);
12
57
  const requesting = shallowRef(false);
13
- const microphoneAuthorized = shallowRef(false);
14
- const permissionRequestError = shallowRef();
15
- const permissionDeniedByRequest = shallowRef(false);
16
- const permission = usePermission("microphone");
17
- const { isSupported: isUserMediaSupported, start: requestMicrophone, stop: stopMicrophone } = useUserMedia({ constraints: {
18
- audio: true,
19
- video: false
20
- } });
21
- const { isSupported: isSpeechSupported, isListening, isFinal, result, error: recognitionError, start: startRecognition, stop: stopRecognition } = useSpeechRecognition({
22
- continuous: false,
23
- interimResults: false,
24
- lang: () => typeof navigator === "undefined" ? "zh-CN" : navigator.language || "zh-CN"
25
- });
26
- const permissionDenied = computed(() => permission.value === "denied" || permissionDeniedByRequest.value);
27
- const recording = computed(() => controlled.value && typeof config.value === "object" ? Boolean(config.value.recording) : isListening.value);
28
- const available = computed(() => Boolean(config.value) && (controlled.value || Boolean(isSpeechSupported.value && !permissionDenied.value)));
58
+ const captureError = shallowRef();
59
+ const stream = shallowRef();
60
+ const audioContext = shallowRef();
61
+ const sourceNode = shallowRef();
62
+ const workletNode = shallowRef();
63
+ const socket = shallowRef();
64
+ const startedAt = shallowRef(0);
65
+ const chunks = shallowRef(0);
66
+ const sequence = shallowRef(0);
67
+ let generatedWorkletUrl;
68
+ let resolveWorkletFlush;
69
+ let disposed = false;
70
+ const recording = computed(() => controlled.value && typeof config.value === "object" ? Boolean(config.value.recording) : internalRecording.value);
71
+ const available = computed(() => Boolean(config.value) && (controlled.value || supported.value) && !(captureError.value && isPermissionDeniedError(captureError.value.error)));
29
72
  const statusText = computed(() => {
30
- var _permissionRequestErr;
73
+ var _captureError$value;
31
74
  if (requesting.value) return t("sender.speech.requestingPermission");
32
- if (permissionDenied.value) return t("sender.speech.permissionDenied");
33
- if (!controlled.value && !isSpeechSupported.value) return t("sender.speech.unsupported");
75
+ if (captureError.value && isPermissionDeniedError(captureError.value.error)) return t("sender.speech.permissionDenied");
76
+ if (!controlled.value && !supported.value) return t("sender.speech.unsupported");
34
77
  if (recording.value) return t("sender.speech.stop");
35
- if (((_permissionRequestErr = permissionRequestError.value) === null || _permissionRequestErr === void 0 ? void 0 : _permissionRequestErr.name) === "NotFoundError") return t("sender.speech.noMicrophone");
36
- if (permissionRequestError.value) return t("sender.speech.microphoneUnavailable");
37
- const recognitionErrorCode = recognitionError.value && "error" in recognitionError.value ? recognitionError.value.error : void 0;
38
- if (recognitionErrorCode === "audio-capture") return t("sender.speech.noMicrophone");
39
- if (recognitionErrorCode === "no-speech") return t("sender.speech.noSpeech");
40
- if (recognitionError.value) return t("sender.speech.recognitionFailed");
78
+ if (((_captureError$value = captureError.value) === null || _captureError$value === void 0 ? void 0 : _captureError$value.error.name) === "NotFoundError") return t("sender.speech.noMicrophone");
79
+ if (captureError.value) return t("sender.speech.microphoneUnavailable");
41
80
  return t("sender.speech.start");
42
81
  });
43
- const ensureMicrophonePermission = function() {
82
+ const resolveAudioContext = () => {
83
+ var _window$AudioContext;
84
+ const AudioContextConstructor = (_window$AudioContext = window.AudioContext) !== null && _window$AudioContext !== void 0 ? _window$AudioContext : window.webkitAudioContext;
85
+ if (!AudioContextConstructor) throw new Error("AudioContext is not supported");
86
+ return new AudioContextConstructor();
87
+ };
88
+ const resolveWorkletUrl = () => {
89
+ var _generatedWorkletUrl;
90
+ if (typeof config.value === "object" && config.value.workletUrl) return config.value.workletUrl;
91
+ (_generatedWorkletUrl = generatedWorkletUrl) !== null && _generatedWorkletUrl !== void 0 || (generatedWorkletUrl = URL.createObjectURL(new Blob([WORKLET_SOURCE], { type: "text/javascript" })));
92
+ return generatedWorkletUrl;
93
+ };
94
+ const connectTransport = (url, protocols) => new Promise((resolve, reject) => {
95
+ const nextSocket = protocols ? new WebSocket(url, protocols) : new WebSocket(url);
96
+ let opened = false;
97
+ socket.value = nextSocket;
98
+ nextSocket.binaryType = "arraybuffer";
99
+ nextSocket.onopen = (event) => {
100
+ opened = true;
101
+ options.onTransportOpen({
102
+ event,
103
+ socket: nextSocket
104
+ });
105
+ resolve(nextSocket);
106
+ };
107
+ nextSocket.onmessage = (event) => options.onTransportMessage({
108
+ event,
109
+ socket: nextSocket
110
+ });
111
+ nextSocket.onclose = (event) => options.onTransportClose({
112
+ event,
113
+ socket: nextSocket
114
+ });
115
+ nextSocket.onerror = () => {
116
+ const errorEvent = {
117
+ error: /* @__PURE__ */ new Error(`Unable to connect to speech URL: ${url}`),
118
+ phase: "transport"
119
+ };
120
+ if (opened) options.onError(errorEvent);
121
+ else reject(errorEvent.error);
122
+ };
123
+ });
124
+ const flushWorklet = () => new Promise((resolve) => {
125
+ var _workletNode$value;
126
+ const port = (_workletNode$value = workletNode.value) === null || _workletNode$value === void 0 ? void 0 : _workletNode$value.port;
127
+ if (!port) {
128
+ resolve();
129
+ return;
130
+ }
131
+ const timeout = window.setTimeout(resolve, 100);
132
+ resolveWorkletFlush = () => {
133
+ window.clearTimeout(timeout);
134
+ resolveWorkletFlush = void 0;
135
+ resolve();
136
+ };
137
+ port.postMessage({ type: "flush" });
138
+ });
139
+ const releaseResources = function() {
44
140
  var _ref = _asyncToGenerator(function* () {
45
- if (microphoneAuthorized.value || permission.value === "granted" || !isUserMediaSupported.value) return true;
141
+ var _workletNode$value2, _sourceNode$value, _stream$value, _audioContext$value, _audioContext$value2;
142
+ (_workletNode$value2 = workletNode.value) === null || _workletNode$value2 === void 0 || _workletNode$value2.disconnect();
143
+ (_sourceNode$value = sourceNode.value) === null || _sourceNode$value === void 0 || _sourceNode$value.disconnect();
144
+ (_stream$value = stream.value) === null || _stream$value === void 0 || _stream$value.getTracks().forEach((track) => track.stop());
145
+ if (((_audioContext$value = audioContext.value) === null || _audioContext$value === void 0 ? void 0 : _audioContext$value.state) !== "closed") yield (_audioContext$value2 = audioContext.value) === null || _audioContext$value2 === void 0 ? void 0 : _audioContext$value2.close();
146
+ if (socket.value && (socket.value.readyState === WebSocket.OPEN || socket.value.readyState === WebSocket.CONNECTING)) socket.value.close(1e3, "speech-ended");
147
+ workletNode.value = void 0;
148
+ sourceNode.value = void 0;
149
+ stream.value = void 0;
150
+ audioContext.value = void 0;
151
+ socket.value = void 0;
152
+ });
153
+ return function releaseResources() {
154
+ return _ref.apply(this, arguments);
155
+ };
156
+ }();
157
+ const stopCapture = function() {
158
+ var _ref2 = _asyncToGenerator(function* (reason = "manual") {
159
+ var _socket$value, _config$value$onRecor, _config$value;
160
+ if (!internalRecording.value) return;
161
+ yield flushWorklet();
162
+ const endedAt = performance.now();
163
+ internalRecording.value = false;
164
+ if (((_socket$value = socket.value) === null || _socket$value === void 0 ? void 0 : _socket$value.readyState) === WebSocket.OPEN && typeof config.value === "object" && config.value.sendMetadata !== false) socket.value.send(JSON.stringify({
165
+ type: "end",
166
+ reason
167
+ }));
168
+ yield releaseResources();
169
+ if (typeof config.value === "object") (_config$value$onRecor = (_config$value = config.value).onRecordingChange) === null || _config$value$onRecor === void 0 || _config$value$onRecor.call(_config$value, false);
170
+ options.onEnd({
171
+ source: "capture",
172
+ reason,
173
+ startedAt: startedAt.value,
174
+ endedAt,
175
+ duration: endedAt - startedAt.value,
176
+ chunks: chunks.value
177
+ });
178
+ });
179
+ return function stopCapture() {
180
+ return _ref2.apply(this, arguments);
181
+ };
182
+ }();
183
+ const startCapture = function() {
184
+ var _ref3 = _asyncToGenerator(function* () {
185
+ if (requesting.value || internalRecording.value || !available.value) return;
46
186
  requesting.value = true;
47
- permissionRequestError.value = void 0;
187
+ captureError.value = void 0;
188
+ let phase = "permission";
48
189
  try {
49
- yield requestMicrophone();
50
- microphoneAuthorized.value = true;
51
- permissionDeniedByRequest.value = false;
52
- return true;
190
+ var _speechConfig$audioCo, _speechConfig$process, _socket$value3, _speechConfig$onRecor;
191
+ const speechConfig = typeof config.value === "object" ? config.value : void 0;
192
+ const bufferSize = resolveBufferSize(speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.bufferSize);
193
+ stream.value = yield navigator.mediaDevices.getUserMedia({
194
+ audio: (_speechConfig$audioCo = speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.audioConstraints) !== null && _speechConfig$audioCo !== void 0 ? _speechConfig$audioCo : true,
195
+ video: false
196
+ });
197
+ if (disposed) {
198
+ yield releaseResources();
199
+ return;
200
+ }
201
+ if (speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.url) {
202
+ phase = "transport";
203
+ yield connectTransport(speechConfig.url, speechConfig.protocols);
204
+ if (disposed) {
205
+ yield releaseResources();
206
+ return;
207
+ }
208
+ }
209
+ phase = "audioContext";
210
+ const nextAudioContext = resolveAudioContext();
211
+ audioContext.value = nextAudioContext;
212
+ phase = "audioWorklet";
213
+ yield nextAudioContext.audioWorklet.addModule(resolveWorkletUrl());
214
+ if (disposed) {
215
+ yield releaseResources();
216
+ return;
217
+ }
218
+ const processorName = (_speechConfig$process = speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.processorName) !== null && _speechConfig$process !== void 0 ? _speechConfig$process : DEFAULT_PROCESSOR_NAME;
219
+ const nextWorkletNode = new AudioWorkletNode(nextAudioContext, processorName, {
220
+ numberOfInputs: 1,
221
+ numberOfOutputs: 0,
222
+ channelCount: 1,
223
+ processorOptions: _objectSpread2(_objectSpread2({}, speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.processorOptions), {}, { bufferSize })
224
+ });
225
+ workletNode.value = nextWorkletNode;
226
+ sourceNode.value = nextAudioContext.createMediaStreamSource(stream.value);
227
+ nextWorkletNode.port.onmessage = (event) => {
228
+ var _socket$value2;
229
+ if (!(event.data instanceof ArrayBuffer) && "type" in event.data) {
230
+ if (event.data.type === "flushed") resolveWorkletFlush === null || resolveWorkletFlush === void 0 || resolveWorkletFlush();
231
+ return;
232
+ }
233
+ if (!internalRecording.value) return;
234
+ const buffer = event.data;
235
+ const dataEvent = {
236
+ buffer,
237
+ sampleRate: nextAudioContext.sampleRate,
238
+ sequence: sequence.value,
239
+ timestamp: performance.now()
240
+ };
241
+ sequence.value += 1;
242
+ chunks.value += 1;
243
+ options.onData(dataEvent);
244
+ if (((_socket$value2 = socket.value) === null || _socket$value2 === void 0 ? void 0 : _socket$value2.readyState) === WebSocket.OPEN) socket.value.send(buffer);
245
+ };
246
+ sourceNode.value.connect(nextWorkletNode);
247
+ startedAt.value = performance.now();
248
+ chunks.value = 0;
249
+ sequence.value = 0;
250
+ internalRecording.value = true;
251
+ if (((_socket$value3 = socket.value) === null || _socket$value3 === void 0 ? void 0 : _socket$value3.readyState) === WebSocket.OPEN && (speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.sendMetadata) !== false) socket.value.send(JSON.stringify({
252
+ type: "start",
253
+ format: "pcm-f32",
254
+ channels: 1,
255
+ sampleRate: nextAudioContext.sampleRate,
256
+ bufferSize
257
+ }));
258
+ speechConfig === null || speechConfig === void 0 || (_speechConfig$onRecor = speechConfig.onRecordingChange) === null || _speechConfig$onRecor === void 0 || _speechConfig$onRecor.call(speechConfig, true);
259
+ options.onStart({
260
+ source: "capture",
261
+ startedAt: startedAt.value,
262
+ stream: stream.value,
263
+ audioContext: nextAudioContext,
264
+ sampleRate: nextAudioContext.sampleRate
265
+ });
53
266
  } catch (error) {
54
- const normalizedError = error instanceof Error ? error : new Error(String(error));
55
- permissionRequestError.value = normalizedError;
56
- permissionDeniedByRequest.value = isPermissionDeniedError(error);
57
- return false;
267
+ const errorEvent = {
268
+ error: error instanceof Error ? error : new Error(String(error)),
269
+ phase
270
+ };
271
+ captureError.value = errorEvent;
272
+ options.onError(errorEvent);
273
+ yield releaseResources();
58
274
  } finally {
59
- stopMicrophone();
60
275
  requesting.value = false;
61
276
  }
62
277
  });
63
- return function ensureMicrophonePermission() {
64
- return _ref.apply(this, arguments);
278
+ return function startCapture() {
279
+ return _ref3.apply(this, arguments);
65
280
  };
66
281
  }();
67
282
  const trigger = function() {
68
- var _ref2 = _asyncToGenerator(function* (breakRecording = false) {
283
+ var _ref4 = _asyncToGenerator(function* (breakRecording = false) {
69
284
  if (breakRecording && !recording.value) return;
70
- forceBreak.value = breakRecording;
71
285
  if (controlled.value && typeof config.value === "object") {
72
- config.value.onRecordingChange(!recording.value);
73
- return;
74
- }
75
- if (recording.value) {
76
- stopRecognition();
77
- if (typeof config.value === "object") config.value.onRecordingChange(false);
286
+ var _config$value$onRecor2, _config$value2;
287
+ const nextRecording = !recording.value;
288
+ (_config$value$onRecor2 = (_config$value2 = config.value).onRecordingChange) === null || _config$value$onRecor2 === void 0 || _config$value$onRecor2.call(_config$value2, nextRecording);
289
+ const now = performance.now();
290
+ if (nextRecording) {
291
+ startedAt.value = now;
292
+ options.onStart({
293
+ source: "controlled",
294
+ startedAt: now
295
+ });
296
+ } else options.onEnd({
297
+ source: "controlled",
298
+ reason: "controlled",
299
+ startedAt: startedAt.value,
300
+ endedAt: now,
301
+ duration: now - startedAt.value,
302
+ chunks: 0
303
+ });
78
304
  return;
79
305
  }
80
- if (requesting.value || !available.value) return;
81
- if (!(yield ensureMicrophonePermission())) return;
82
- result.value = "";
83
- recognitionError.value = void 0;
84
- startRecognition();
85
- if (typeof config.value === "object") config.value.onRecordingChange(true);
306
+ if (internalRecording.value) yield stopCapture();
307
+ else yield startCapture();
86
308
  });
87
309
  return function trigger() {
88
- return _ref2.apply(this, arguments);
310
+ return _ref4.apply(this, arguments);
89
311
  };
90
312
  }();
91
- watch(result, (transcript) => {
92
- if (!transcript || !isFinal.value) return;
93
- if (!forceBreak.value) onTranscript(transcript);
94
- forceBreak.value = false;
95
- });
96
- watch(recognitionError, (nextError) => {
97
- if (nextError && "error" in nextError && (nextError.error === "not-allowed" || nextError.error === "service-not-allowed")) permissionDeniedByRequest.value = true;
313
+ onMounted(() => {
314
+ var _navigator$mediaDevic;
315
+ supported.value = Boolean(typeof ((_navigator$mediaDevic = navigator.mediaDevices) === null || _navigator$mediaDevic === void 0 ? void 0 : _navigator$mediaDevic.getUserMedia) === "function" && typeof AudioContext !== "undefined" && typeof AudioWorkletNode !== "undefined");
98
316
  });
99
- watch(permission, (nextPermission) => {
100
- if (nextPermission === "granted") {
101
- microphoneAuthorized.value = true;
102
- permissionDeniedByRequest.value = false;
103
- } else if (nextPermission === "denied") {
104
- microphoneAuthorized.value = false;
105
- if (isListening.value) stopRecognition();
106
- }
317
+ watch(() => Boolean(config.value), (enabled) => {
318
+ if (!enabled) stopCapture();
107
319
  });
108
320
  watch(controlled, (nextControlled) => {
109
- if (nextControlled && isListening.value) stopRecognition();
321
+ if (nextControlled) stopCapture();
110
322
  });
323
+ onBeforeUnmount(_asyncToGenerator(function* () {
324
+ disposed = true;
325
+ if (internalRecording.value) yield stopCapture("unmount");
326
+ else yield releaseResources();
327
+ if (generatedWorkletUrl) URL.revokeObjectURL(generatedWorkletUrl);
328
+ }));
111
329
  return {
112
330
  available,
113
331
  recording,
@@ -7,6 +7,10 @@ declare const Spin: {
7
7
  type: NumberConstructor;
8
8
  };
9
9
  loading: BooleanConstructor;
10
+ delay: {
11
+ type: (BooleanConstructor | NumberConstructor)[];
12
+ default: boolean;
13
+ };
10
14
  dot: BooleanConstructor;
11
15
  tip: StringConstructor;
12
16
  hideIcon: {
@@ -16,6 +20,7 @@ declare const Spin: {
16
20
  }>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, import("vue").PublicProps, {
17
21
  dot: boolean;
18
22
  loading: boolean;
23
+ delay: number | boolean;
19
24
  hideIcon: boolean;
20
25
  }, true, {}, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
21
26
  P: {};
@@ -29,6 +34,10 @@ declare const Spin: {
29
34
  type: NumberConstructor;
30
35
  };
31
36
  loading: BooleanConstructor;
37
+ delay: {
38
+ type: (BooleanConstructor | NumberConstructor)[];
39
+ default: boolean;
40
+ };
32
41
  dot: BooleanConstructor;
33
42
  tip: StringConstructor;
34
43
  hideIcon: {
@@ -38,6 +47,7 @@ declare const Spin: {
38
47
  }>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, {
39
48
  dot: boolean;
40
49
  loading: boolean;
50
+ delay: number | boolean;
41
51
  hideIcon: boolean;
42
52
  }>;
43
53
  __isFragment?: never;
@@ -48,6 +58,10 @@ declare const Spin: {
48
58
  type: NumberConstructor;
49
59
  };
50
60
  loading: BooleanConstructor;
61
+ delay: {
62
+ type: (BooleanConstructor | NumberConstructor)[];
63
+ default: boolean;
64
+ };
51
65
  dot: BooleanConstructor;
52
66
  tip: StringConstructor;
53
67
  hideIcon: {
@@ -57,6 +71,7 @@ declare const Spin: {
57
71
  }>> & Readonly<{}>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, {
58
72
  dot: boolean;
59
73
  loading: boolean;
74
+ delay: number | boolean;
60
75
  hideIcon: boolean;
61
76
  }, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps & {
62
77
  install: (app: App, options?: SDOptions) => void;
package/es/spin/spin.d.ts CHANGED
@@ -3,6 +3,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
3
3
  type: NumberConstructor;
4
4
  };
5
5
  loading: BooleanConstructor;
6
+ delay: {
7
+ type: (BooleanConstructor | NumberConstructor)[];
8
+ default: boolean;
9
+ };
6
10
  dot: BooleanConstructor;
7
11
  tip: StringConstructor;
8
12
  hideIcon: {
@@ -14,6 +18,10 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
14
18
  type: NumberConstructor;
15
19
  };
16
20
  loading: BooleanConstructor;
21
+ delay: {
22
+ type: (BooleanConstructor | NumberConstructor)[];
23
+ default: boolean;
24
+ };
17
25
  dot: BooleanConstructor;
18
26
  tip: StringConstructor;
19
27
  hideIcon: {
@@ -23,6 +31,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
23
31
  }>> & Readonly<{}>, {
24
32
  dot: boolean;
25
33
  loading: boolean;
34
+ delay: number | boolean;
26
35
  hideIcon: boolean;
27
36
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
28
37
  export default _default;
package/es/spin/spin.js CHANGED
@@ -2,8 +2,10 @@ import { configProviderInjectionKey } from "../config-provider/context.js";
2
2
  import { getPrefixCls } from "../_utils/global-config.js";
3
3
  import { getFirstComponent } from "../_utils/vue-utils.js";
4
4
  import IconLoading from "../icon/icon-loading/index.js";
5
+ import { useConfigProviderProp } from "../_hooks/use-config-provider-prop.js";
5
6
  import dot_loading_default from "./dot-loading.js";
6
- import { Fragment, cloneVNode, computed, createVNode, defineComponent, inject } from "vue";
7
+ import { Fragment, cloneVNode, computed, createVNode, defineComponent, inject, shallowRef, toRef } from "vue";
8
+ import { watchDebounced } from "@vueuse/core";
7
9
  //#region components/spin/spin.tsx
8
10
  var spin_default = /* @__PURE__ */ defineComponent({
9
11
  name: "Spin",
@@ -19,6 +21,14 @@ var spin_default = /* @__PURE__ */ defineComponent({
19
21
  */
20
22
  loading: Boolean,
21
23
  /**
24
+ * @zh 加载指示器显示前的延迟时间。设置为 `true` 时延迟 400ms
25
+ * @en Delay before showing the loading indicator. Uses 400ms when set to `true`
26
+ */
27
+ delay: {
28
+ type: [Boolean, Number],
29
+ default: false
30
+ },
31
+ /**
22
32
  * @zh 是否使用点类型的动画
23
33
  * @en Whether to use dot type animation
24
34
  */
@@ -55,9 +65,61 @@ var spin_default = /* @__PURE__ */ defineComponent({
55
65
  setup(props, { slots }) {
56
66
  const prefixCls = getPrefixCls("spin");
57
67
  const configCtx = inject(configProviderInjectionKey, void 0);
68
+ const { mergedValue: mergedLoading } = useConfigProviderProp(toRef(props, "loading"), {
69
+ propNames: ["loading"],
70
+ getGlobalValue: (ctx) => {
71
+ var _ctx$spinProps;
72
+ return ctx === null || ctx === void 0 || (_ctx$spinProps = ctx.spinProps) === null || _ctx$spinProps === void 0 ? void 0 : _ctx$spinProps.loading;
73
+ }
74
+ });
75
+ const { mergedValue: mergedDelay } = useConfigProviderProp(toRef(props, "delay"), {
76
+ propNames: ["delay"],
77
+ getGlobalValue: (ctx) => {
78
+ var _ctx$spinProps2;
79
+ return ctx === null || ctx === void 0 || (_ctx$spinProps2 = ctx.spinProps) === null || _ctx$spinProps2 === void 0 ? void 0 : _ctx$spinProps2.delay;
80
+ }
81
+ });
82
+ const { mergedValue: mergedSize } = useConfigProviderProp(toRef(props, "size"), {
83
+ propNames: ["size"],
84
+ getGlobalValue: (ctx) => {
85
+ var _ctx$spinProps3;
86
+ return ctx === null || ctx === void 0 || (_ctx$spinProps3 = ctx.spinProps) === null || _ctx$spinProps3 === void 0 ? void 0 : _ctx$spinProps3.size;
87
+ }
88
+ });
89
+ const { mergedValue: mergedDot } = useConfigProviderProp(toRef(props, "dot"), {
90
+ propNames: ["dot"],
91
+ getGlobalValue: (ctx) => {
92
+ var _ctx$spinProps4;
93
+ return ctx === null || ctx === void 0 || (_ctx$spinProps4 = ctx.spinProps) === null || _ctx$spinProps4 === void 0 ? void 0 : _ctx$spinProps4.dot;
94
+ }
95
+ });
96
+ const { mergedValue: mergedTip } = useConfigProviderProp(toRef(props, "tip"), {
97
+ propNames: ["tip"],
98
+ getGlobalValue: (ctx) => {
99
+ var _ctx$spinProps5;
100
+ return ctx === null || ctx === void 0 || (_ctx$spinProps5 = ctx.spinProps) === null || _ctx$spinProps5 === void 0 ? void 0 : _ctx$spinProps5.tip;
101
+ }
102
+ });
103
+ const { mergedValue: mergedHideIcon } = useConfigProviderProp(toRef(props, "hideIcon"), {
104
+ propNames: ["hideIcon", "hide-icon"],
105
+ getGlobalValue: (ctx) => {
106
+ var _ctx$spinProps6;
107
+ return ctx === null || ctx === void 0 || (_ctx$spinProps6 = ctx.spinProps) === null || _ctx$spinProps6 === void 0 ? void 0 : _ctx$spinProps6.hideIcon;
108
+ }
109
+ });
110
+ const delayTime = computed(() => mergedDelay.value === true ? 400 : Math.max(0, Number(mergedDelay.value) || 0));
111
+ const requestedLoading = computed(() => slots.default ? Boolean(mergedLoading.value) : true);
112
+ const delayedLoading = shallowRef(false);
113
+ watchDebounced(requestedLoading, (loading) => {
114
+ delayedLoading.value = loading;
115
+ }, {
116
+ debounce: delayTime,
117
+ immediate: true
118
+ });
119
+ const activeLoading = computed(() => delayTime.value > 0 ? Boolean(requestedLoading.value && delayedLoading.value) : requestedLoading.value);
58
120
  const cls = computed(() => [prefixCls, {
59
- [`${prefixCls}-loading`]: props.loading,
60
- [`${prefixCls}-with-tip`]: props.tip && !slots.default
121
+ [`${prefixCls}-loading`]: slots.default ? activeLoading.value : Boolean(mergedLoading.value),
122
+ [`${prefixCls}-with-tip`]: mergedTip.value && !slots.default
61
123
  }]);
62
124
  const renderIcon = () => {
63
125
  if (slots.icon) {
@@ -65,28 +127,28 @@ var spin_default = /* @__PURE__ */ defineComponent({
65
127
  if (iconVNode) return cloneVNode(iconVNode, { spin: true });
66
128
  }
67
129
  if (slots.element) return slots.element();
68
- if (props.dot) return createVNode(dot_loading_default, { "size": props.size }, null);
130
+ if (mergedDot.value) return createVNode(dot_loading_default, { "size": mergedSize.value }, null);
69
131
  if (configCtx === null || configCtx === void 0 ? void 0 : configCtx.slots.loading) return configCtx.slots.loading();
70
132
  return createVNode(IconLoading, {
71
133
  "spin": true,
72
- "size": props.size
134
+ "size": mergedSize.value
73
135
  }, null);
74
136
  };
75
137
  const renderSpinIcon = () => {
76
138
  var _slots$tip, _slots$tip2, _slots$tip3;
77
- const style = props.size ? { fontSize: `${props.size}px` } : void 0;
78
- const hasTip = Boolean((_slots$tip = slots.tip) !== null && _slots$tip !== void 0 ? _slots$tip : props.tip);
79
- return createVNode(Fragment, null, [!props.hideIcon && createVNode("div", {
139
+ const style = mergedSize.value ? { fontSize: `${mergedSize.value}px` } : void 0;
140
+ const hasTip = Boolean((_slots$tip = slots.tip) !== null && _slots$tip !== void 0 ? _slots$tip : mergedTip.value);
141
+ return createVNode(Fragment, null, [!mergedHideIcon.value && createVNode("div", {
80
142
  "class": `${prefixCls}-icon`,
81
143
  "style": style,
82
144
  "aria-hidden": "true"
83
- }, [renderIcon()]), hasTip && createVNode("div", { "class": `${prefixCls}-tip` }, [(_slots$tip2 = (_slots$tip3 = slots.tip) === null || _slots$tip3 === void 0 ? void 0 : _slots$tip3.call(slots)) !== null && _slots$tip2 !== void 0 ? _slots$tip2 : props.tip])]);
145
+ }, [renderIcon()]), hasTip && createVNode("div", { "class": `${prefixCls}-tip` }, [(_slots$tip2 = (_slots$tip3 = slots.tip) === null || _slots$tip3 === void 0 ? void 0 : _slots$tip3.call(slots)) !== null && _slots$tip2 !== void 0 ? _slots$tip2 : mergedTip.value])]);
84
146
  };
85
147
  return () => createVNode("div", {
86
148
  "role": "status",
87
149
  "aria-live": "polite",
88
150
  "class": cls.value
89
- }, [slots.default ? createVNode(Fragment, null, [slots.default(), props.loading && createVNode("div", { "class": `${prefixCls}-mask` }, [createVNode("div", { "class": `${prefixCls}-mask-icon` }, [renderSpinIcon()])])]) : renderSpinIcon()]);
151
+ }, [slots.default ? createVNode(Fragment, null, [slots.default(), activeLoading.value && createVNode("div", { "class": `${prefixCls}-mask` }, [createVNode("div", { "class": `${prefixCls}-mask-icon` }, [renderSpinIcon()])])]) : activeLoading.value ? renderSpinIcon() : null]);
90
152
  }
91
153
  });
92
154
  //#endregion