mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
Migrate from Eslint/Prettier/Stylelint to Biome (#5794)
This commit is contained in:
parent
d408570373
commit
78299475ae
544 changed files with 7527 additions and 8168 deletions
|
|
@ -1,8 +0,0 @@
|
|||
node_modules
|
||||
lib
|
||||
dist
|
||||
coverage
|
||||
test/lib/**
|
||||
test/endtoend/*/build
|
||||
examples/svelte-example/public/build/
|
||||
bundle-legacy.js
|
||||
506
.eslintrc.js
506
.eslintrc.js
|
|
@ -1,506 +0,0 @@
|
|||
/* eslint-disable quote-props */
|
||||
|
||||
'use strict'
|
||||
|
||||
const svgPresentationAttributes = [
|
||||
'alignment-baseline', 'baseline-shift', 'class', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolatio', 'color-interpolatio-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'transform', 'transform-origin', 'unicode-bidi', 'vector-effect', 'visibility', 'word-spacing', 'writing-mod',
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['transloadit', 'prettier'],
|
||||
env: {
|
||||
es6: true,
|
||||
jest: true,
|
||||
node: true,
|
||||
// extra:
|
||||
browser: true,
|
||||
},
|
||||
globals: {
|
||||
globalThis: true,
|
||||
hexo: true,
|
||||
window: true,
|
||||
},
|
||||
plugins: [
|
||||
'jest',
|
||||
'markdown',
|
||||
'node',
|
||||
'prefer-import',
|
||||
'promise',
|
||||
'react',
|
||||
// extra:
|
||||
'jsdoc',
|
||||
'no-only-tests',
|
||||
'unicorn',
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
ecmaVersion: 2022,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
// transloadit rules we are actually ok with in the uppy repo
|
||||
'import/extensions': 'off',
|
||||
'object-shorthand': ['error', 'always'],
|
||||
'strict': 'off',
|
||||
'key-spacing': 'off',
|
||||
'max-classes-per-file': ['error', 2],
|
||||
'react/no-unknown-property': ['error', {
|
||||
ignore: svgPresentationAttributes,
|
||||
}],
|
||||
|
||||
'import/no-unresolved': 'off',
|
||||
|
||||
// rules we want to enforce
|
||||
'array-callback-return': 'error',
|
||||
'func-names': 'error',
|
||||
'import/no-dynamic-require': 'error',
|
||||
'import/no-extraneous-dependencies': 'error',
|
||||
'max-len': 'error',
|
||||
'no-empty': 'error',
|
||||
'no-bitwise': 'error',
|
||||
'no-continue': 'error',
|
||||
'no-lonely-if': 'error',
|
||||
'no-nested-ternary': 'error',
|
||||
'no-restricted-properties': 'error',
|
||||
'no-return-assign': 'error',
|
||||
'no-underscore-dangle': 'error',
|
||||
'no-unused-expressions': 'error',
|
||||
'no-unused-vars': 'error',
|
||||
'no-useless-concat': 'error',
|
||||
'no-var': 'error',
|
||||
'node/handle-callback-err': 'error',
|
||||
'prefer-destructuring': 'error',
|
||||
'prefer-spread': 'error',
|
||||
'unicorn/prefer-node-protocol': 'error',
|
||||
|
||||
'react/button-has-type': 'error',
|
||||
'react/forbid-prop-types': 'error',
|
||||
'react/no-access-state-in-setstate': 'error',
|
||||
'react/no-array-index-key': 'error',
|
||||
'react/no-deprecated': 'error',
|
||||
'react/no-this-in-sfc': 'error',
|
||||
'react/no-will-update-set-state': 'error',
|
||||
'react/prefer-stateless-function': 'error',
|
||||
'react/require-default-props': ['error', {
|
||||
forbidDefaultForRequired: true,
|
||||
functions: 'ignore',
|
||||
}],
|
||||
'react/sort-comp': 'error',
|
||||
'react/static-property-placement': 'off',
|
||||
'react/style-prop-object': 'error',
|
||||
|
||||
// accessibility
|
||||
'jsx-a11y/alt-text': 'error',
|
||||
'jsx-a11y/anchor-has-content': 'error',
|
||||
'jsx-a11y/click-events-have-key-events': 'error',
|
||||
'jsx-a11y/control-has-associated-label': 'error',
|
||||
'jsx-a11y/label-has-associated-control': 'error',
|
||||
'jsx-a11y/media-has-caption': 'error',
|
||||
'jsx-a11y/mouse-events-have-key-events': 'error',
|
||||
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
|
||||
'jsx-a11y/no-noninteractive-element-interactions': 'error',
|
||||
'jsx-a11y/no-static-element-interactions': 'error',
|
||||
|
||||
// jsdoc
|
||||
'jsdoc/check-alignment': 'error',
|
||||
'jsdoc/check-examples': 'off', // cannot yet be supported for ESLint 8, see https://github.com/eslint/eslint/issues/14745
|
||||
'jsdoc/check-param-names': 'error',
|
||||
'jsdoc/check-syntax': 'error',
|
||||
'jsdoc/check-tag-names': ['error', { jsxTags: true }],
|
||||
'jsdoc/check-types': 'error',
|
||||
'jsdoc/valid-types': 'error',
|
||||
'jsdoc/check-indentation': ['off'],
|
||||
},
|
||||
|
||||
settings: {
|
||||
'import/core-modules': ['tsd'],
|
||||
react: {
|
||||
pragma: 'h',
|
||||
},
|
||||
jsdoc: {
|
||||
mode: 'typescript',
|
||||
},
|
||||
polyfills: [
|
||||
'Promise',
|
||||
'fetch',
|
||||
'Object.assign',
|
||||
'document.querySelector',
|
||||
],
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'*.jsx',
|
||||
'*.tsx',
|
||||
'packages/@uppy/react-native/**/*.js',
|
||||
],
|
||||
parser: 'espree',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
name: '__filename',
|
||||
message: 'Use import.meta.url instead',
|
||||
},
|
||||
{
|
||||
name: '__dirname',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'exports',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'module',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'require',
|
||||
message: 'Use import instead',
|
||||
},
|
||||
{
|
||||
name: 'JSX',
|
||||
message: 'Use h.JSX.Element, ComponentChild, or ComponentChildren from Preact',
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
message: 'Import the value instead of relying on global.React.',
|
||||
},
|
||||
],
|
||||
'import/extensions': ['error', 'ignorePackages'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.mjs',
|
||||
'e2e/clients/**/*.js',
|
||||
'examples/aws-companion/*.js',
|
||||
'examples/aws-php/*.js',
|
||||
'examples/bundled/*.js',
|
||||
'examples/custom-provider/client/*.js',
|
||||
'examples/digitalocean-spaces/*.js',
|
||||
'examples/multiple-instances/*.js',
|
||||
'examples/node-xhr/*.js',
|
||||
'examples/php-xhr/*.js',
|
||||
'examples/python-xhr/*.js',
|
||||
'examples/react-example/*.js',
|
||||
'examples/redux/*.js',
|
||||
'examples/transloadit/*.js',
|
||||
'examples/transloadit-markdown-bin/*.js',
|
||||
'examples/xhr-bundle/*.js',
|
||||
'private/dev/*.js',
|
||||
'private/release/*.js',
|
||||
'private/remark-lint-uppy/*.js',
|
||||
'packages/@uppy/!(companion|react-native)/**/*.js',
|
||||
],
|
||||
parser: 'espree',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: false,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'import/named': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
|
||||
'import/no-named-as-default': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
|
||||
'import/no-named-as-default-member': 'off', // Disabled because that rule tries and fails to parse JSX dependencies.
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
name: '__filename',
|
||||
message: 'Use import.meta.url instead',
|
||||
},
|
||||
{
|
||||
name: '__dirname',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'exports',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'module',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'require',
|
||||
message: 'Use import instead',
|
||||
},
|
||||
{
|
||||
name: 'JSX',
|
||||
message: 'Use h.JSX.Element, ComponentChild, or ComponentChildren from Preact',
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
message: 'Import the value instead of relying on global.React.',
|
||||
},
|
||||
],
|
||||
'import/extensions': ['error', 'ignorePackages'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/uppy/*.mjs'],
|
||||
rules: {
|
||||
'import/first': 'off',
|
||||
'import/newline-after-import': 'off',
|
||||
'import/no-extraneous-dependencies': ['error', {
|
||||
devDependencies: true,
|
||||
}],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/@uppy/*/types/*.d.ts',
|
||||
],
|
||||
rules : {
|
||||
'import/no-unresolved': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/@uppy/dashboard/src/components/**/*.jsx',
|
||||
],
|
||||
rules: {
|
||||
'react/destructuring-assignment': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
// Those need looser rules, and cannot be made part of the stricter rules above.
|
||||
// TODO: update those to more modern code when switch to ESM is complete
|
||||
'examples/react-native-expo/*.js',
|
||||
'examples/svelte-example/**/*.js',
|
||||
'examples/sveltekit/**/*.js',
|
||||
'examples/vue/**/*.js',
|
||||
'examples/vue3/**/*.js',
|
||||
],
|
||||
rules: {
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'varsIgnorePattern': 'React',
|
||||
},
|
||||
],
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['./packages/@uppy/companion/**/*.js'],
|
||||
rules: {
|
||||
'no-underscore-dangle': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'bin/**.js',
|
||||
'bin/**.mjs',
|
||||
'examples/**/*.cjs',
|
||||
'examples/**/*.js',
|
||||
'packages/@uppy/companion/test/**/*.js',
|
||||
'test/**/*.js',
|
||||
'test/**/*.ts',
|
||||
'*.test.js',
|
||||
'*.test.ts',
|
||||
'*.test-d.ts',
|
||||
'*.test-d.tsx',
|
||||
'postcss.config.js',
|
||||
'.eslintrc.js',
|
||||
'private/**/*.js',
|
||||
'private/**/*.mjs',
|
||||
],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'import/no-extraneous-dependencies': ['error', {
|
||||
devDependencies: true,
|
||||
}],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: [
|
||||
'packages/@uppy/locales/src/*.js',
|
||||
'packages/@uppy/locales/template.js',
|
||||
],
|
||||
rules: {
|
||||
camelcase: ['off'],
|
||||
'quote-props': ['error', 'as-needed', { 'numbers': true }],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: ['test/endtoend/*/*.mjs', 'test/endtoend/*/*.ts'],
|
||||
rules: {
|
||||
// we mostly import @uppy stuff in these files.
|
||||
'import/no-extraneous-dependencies': ['off'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/endtoend/*/*.js'],
|
||||
env: {
|
||||
mocha: true,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: ['packages/@uppy/react/src/**/*.js'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': ['error', {
|
||||
peerDependencies: true,
|
||||
}],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
files: ['**/*.md', '*.md'],
|
||||
processor: 'markdown/markdown',
|
||||
},
|
||||
{
|
||||
files: ['**/*.md/*.js', '**/*.md/*.javascript'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'react/destructuring-assignment': 'off',
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
name: '__filename',
|
||||
message: 'Use import.meta.url instead',
|
||||
},
|
||||
{
|
||||
name: '__dirname',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'exports',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'module',
|
||||
message: 'Not available in ESM',
|
||||
},
|
||||
{
|
||||
name: 'require',
|
||||
message: 'Use import instead',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.md/*.ts', '**/*.md/*.typescript', '**/*.tsx', '**/*.md/*.tsx'],
|
||||
excludedFiles: ['examples/angular-example/**/*.ts', 'packages/@uppy/angular/**/*.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
'no-extra-semi': 'off',
|
||||
'no-restricted-syntax': ['error', {
|
||||
selector: 'ImportDeclaration[source.value=/^@uppy\\x2F[a-z-0-9]+\\x2F/]:not([source.value=/^@uppy\\x2Futils\\x2F/]):not([source.value=/\\.(js|css)$/])',
|
||||
message: 'Use ".js" file extension for import type declarations from a different package',
|
||||
}, {
|
||||
selector: 'ImportDeclaration[source.value=/^@uppy\\x2Futils\\x2Flib\\x2F.+\\.[mc]?[jt]sx?$/]',
|
||||
message: 'Do not use file extension when importing from @uppy/utils',
|
||||
},
|
||||
{
|
||||
selector: 'ImportDeclaration[source.value=/^@uppy\\x2F[a-z-0-9]+\\x2Fsrc\\x2F/]',
|
||||
message: 'Importing from "src/" is not allowed. Import from root or from "lib/" if you must.',
|
||||
}],
|
||||
'import/extensions': ['error', 'ignorePackages'],
|
||||
'import/prefer-default-export': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-extra-semi': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/@uppy/*/src/**/*.ts'],
|
||||
excludedFiles: ['packages/@uppy/**/*.test.ts', 'packages/@uppy/core/src/mocks/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.md/*.*'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'no-console': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/react/*.md/*.js', '**/react.md/*.js', '**/react-*.md/*.js', '**/react/**/*.test-d.tsx'],
|
||||
settings: {
|
||||
react: { pragma: 'React' },
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/react/**/*.test-d.tsx'],
|
||||
rules: {
|
||||
'import/extensions': 'off',
|
||||
'import/no-useless-path-segments': 'off',
|
||||
'no-alert': 'off',
|
||||
'no-inner-declarations': 'off',
|
||||
'no-lone-blocks': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/@uppy/svelte/**',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['e2e/**/*.ts'],
|
||||
extends: ['plugin:cypress/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['e2e/**/*.ts', 'e2e/**/*.js', 'e2e/**/*.jsx', 'e2e/**/*.mjs'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'no-console': 'off',
|
||||
'no-only-tests/no-only-tests': 'error',
|
||||
'no-unused-expressions': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["packages/@uppy/vue/**"],
|
||||
rules: {
|
||||
'react-hooks/rules-of-hooks': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
4
.github/workflows/linters.yml
vendored
4
.github/workflows/linters.yml
vendored
|
|
@ -38,6 +38,4 @@ jobs:
|
|||
corepack yarn workspaces focus @uppy/angular @uppy-example/angular
|
||||
@uppy-example/react-native-expo @uppy/react-native @uppy-dev/build
|
||||
- name: Run linter
|
||||
run: corepack yarn run lint
|
||||
- name: Run Prettier
|
||||
run: corepack yarn run format:check-diff
|
||||
run: corepack yarn run check:ci
|
||||
|
|
|
|||
5
.github/workflows/release-candidate.yml
vendored
5
.github/workflows/release-candidate.yml
vendored
|
|
@ -1,7 +1,8 @@
|
|||
name: Release candidate
|
||||
on:
|
||||
push:
|
||||
branches: release
|
||||
branches:
|
||||
- release
|
||||
|
||||
env:
|
||||
YARN_ENABLE_GLOBAL_CACHE: false
|
||||
|
|
@ -50,7 +51,7 @@ jobs:
|
|||
releases.json | xargs git add
|
||||
- name: Update contributors table
|
||||
run:
|
||||
corepack yarn contributors:save && corepack yarn prettier -w README.md
|
||||
corepack yarn contributors:save
|
||||
&& git add README.md
|
||||
- name: Update CDN URLs
|
||||
run:
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
node_modules/
|
||||
*.js
|
||||
*.jsx
|
||||
*.cjs
|
||||
*.mjs
|
||||
!private/js2ts/*
|
||||
!examples/svelte-example/*
|
||||
*.lock
|
||||
CHANGELOG.md
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/** @type {import("prettier").Config} */
|
||||
module.exports = {
|
||||
proseWrap: 'always',
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
semi: false,
|
||||
experimentalTernaries: true,
|
||||
overrides: [
|
||||
{
|
||||
files: 'packages/@uppy/angular/**',
|
||||
options: {
|
||||
semi: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['tsconfig.json'],
|
||||
options: {
|
||||
parser: 'jsonc',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
website/src/_posts/201*
|
||||
website/src/_posts/2020-*
|
||||
website/src/_posts/2021-0*
|
||||
examples/
|
||||
CHANGELOG.md
|
||||
CHANGELOG.next.md
|
||||
BACKLOG.md
|
||||
node_modules/
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-standard-scss",
|
||||
"stylelint-config-rational-order"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null,
|
||||
"scss/at-rule-no-unknown": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["biomejs.biome"]
|
||||
}
|
||||
20
.vscode/uppy.code-workspace
vendored
20
.vscode/uppy.code-workspace
vendored
|
|
@ -1,29 +1,23 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "..",
|
||||
},
|
||||
"path": ".."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.activeForeground": "#ffffff",
|
||||
"titleBar.activeBackground": "#ff009d",
|
||||
"titleBar.activeBackground": "#ff009d"
|
||||
},
|
||||
"search.exclude": {
|
||||
"website/public/": true,
|
||||
"node_modules/": true,
|
||||
"website/node_modules/": true,
|
||||
"dist/": true,
|
||||
"lib/": true,
|
||||
"package-lock.json": true,
|
||||
"website/package-lock.json": true,
|
||||
"yarn-error.log": true,
|
||||
"website/.deploy_git": true,
|
||||
"npm-debug.log": true,
|
||||
"website/npm-debug.log": true,
|
||||
"website/debug.log": true,
|
||||
"nohup.out": true,
|
||||
"yarn.lock": true,
|
||||
},
|
||||
},
|
||||
"yarn.lock": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
.vscode/uppy.code-workspace.bak
vendored
29
.vscode/uppy.code-workspace.bak
vendored
|
|
@ -1,29 +0,0 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"workbench.colorCustomizations": {
|
||||
"titleBar.activeForeground": "#ffffff",
|
||||
"titleBar.activeBackground": "#ff009d",
|
||||
},
|
||||
"search.exclude": {
|
||||
"website/public/": true,
|
||||
"node_modules/": true,
|
||||
"website/node_modules/": true,
|
||||
"dist/": true,
|
||||
"lib/": true,
|
||||
"package-lock.json": true,
|
||||
"website/package-lock.json": true,
|
||||
"yarn-error.log": true,
|
||||
"website/.deploy_git": true,
|
||||
"npm-debug.log": true,
|
||||
"website/npm-debug.log": true,
|
||||
"website/debug.log": true,
|
||||
"nohup.out": true,
|
||||
"yarn.lock": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
diff --git a/index.js b/index.js
|
||||
index a20646d922945004cb737918ef6b6d063bb3c2a4..a44863e9555abdaa569f309b1197fddc8dd244a5 100644
|
||||
--- a/index.js
|
||||
+++ b/index.js
|
||||
@@ -147,7 +147,7 @@ Hook.prototype.log = function log(lines, exit) {
|
||||
* @api private
|
||||
*/
|
||||
Hook.prototype.initialize = function initialize() {
|
||||
- ['git', 'npm'].forEach(function each(binary) {
|
||||
+ ['git', 'corepack'].forEach(function each(binary) {
|
||||
try { this[binary] = which.sync(binary); }
|
||||
catch (e) {}
|
||||
}, this);
|
||||
@@ -159,9 +159,9 @@ Hook.prototype.initialize = function initialize() {
|
||||
if (!this.npm) {
|
||||
try {
|
||||
process.env.PATH += path.delimiter + path.dirname(process.env._);
|
||||
- this.npm = which.sync('npm');
|
||||
+ this.npm = which.sync('corepack');
|
||||
} catch (e) {
|
||||
- return this.log(this.format(Hook.log.binary, 'npm'), 0);
|
||||
+ return this.log(this.format(Hook.log.binary, 'corepack'), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ Hook.prototype.run = function runner() {
|
||||
// this doesn't have the required `isAtty` information that libraries use to
|
||||
// output colors resulting in script output that doesn't have any color.
|
||||
//
|
||||
- spawn(hooked.npm, ['run', script, '--silent'], {
|
||||
+ spawn(hooked.npm, ['yarn', script], {
|
||||
env: process.env,
|
||||
cwd: hooked.root,
|
||||
stdio: [0, 1, 2]
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import fs from 'node:fs/promises'
|
||||
import { existsSync } from 'node:fs'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
|
|
@ -13,7 +13,10 @@ const rootDir = path.resolve(scriptDir, '..')
|
|||
// source:
|
||||
const COMPONENTS_DIR = path.join(rootDir, 'packages/@uppy/components/src')
|
||||
// destinations:
|
||||
const REACT_DIR = path.join(rootDir, 'packages/@uppy/react/src/headless/generated')
|
||||
const REACT_DIR = path.join(
|
||||
rootDir,
|
||||
'packages/@uppy/react/src/headless/generated',
|
||||
)
|
||||
const VUE_DIR = path.join(rootDir, 'packages/@uppy/vue/src/headless/generated')
|
||||
const SVELTE_DIR = path.join(
|
||||
rootDir,
|
||||
|
|
@ -222,7 +225,10 @@ try {
|
|||
const reactIndexContent = reactComponents
|
||||
.map((name) => `export { default as ${name} } from './${name}.js'`)
|
||||
.join('\n')
|
||||
await fs.writeFile(path.join(REACT_DIR, 'index.ts'), `${reactIndexContent}\n`)
|
||||
await fs.writeFile(
|
||||
path.join(REACT_DIR, 'index.ts'),
|
||||
`${reactIndexContent}\n`,
|
||||
)
|
||||
console.log(`\nExporting React components from ${REACT_DIR}`)
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +244,10 @@ try {
|
|||
const svelteIndexContent = svelteComponents
|
||||
.map((name) => `export { default as ${name} } from './${name}.svelte'`)
|
||||
.join('\n')
|
||||
await fs.writeFile(path.join(SVELTE_DIR, 'index.ts'), `${svelteIndexContent}\n`)
|
||||
await fs.writeFile(
|
||||
path.join(SVELTE_DIR, 'index.ts'),
|
||||
`${svelteIndexContent}\n`,
|
||||
)
|
||||
console.log(`Exporting Svelte components from ${SVELTE_DIR}`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,49 +16,50 @@ const { mkdir, writeFile } = fs.promises
|
|||
const cwd = process.cwd()
|
||||
let chalk
|
||||
|
||||
function handleErr (err) {
|
||||
function handleErr(err) {
|
||||
console.error(chalk.red('✗ Error:'), chalk.red(err.message))
|
||||
}
|
||||
|
||||
async function compileCSS () {
|
||||
({ default:chalk } = await import('chalk'))
|
||||
async function compileCSS() {
|
||||
;({ default: chalk } = await import('chalk'))
|
||||
const files = await glob('packages/{,@uppy/}*/src/style.scss')
|
||||
|
||||
for (const file of files) {
|
||||
const importedFiles = new Set()
|
||||
const scssResult = await renderScss({
|
||||
file,
|
||||
importer (url, from, done) {
|
||||
resolve(url, {
|
||||
basedir: path.dirname(from),
|
||||
filename: from,
|
||||
extensions: ['.scss'],
|
||||
}, (err, resolved) => {
|
||||
if (err) {
|
||||
done(err)
|
||||
return
|
||||
}
|
||||
importer(url, from, done) {
|
||||
resolve(
|
||||
url,
|
||||
{
|
||||
basedir: path.dirname(from),
|
||||
filename: from,
|
||||
extensions: ['.scss'],
|
||||
},
|
||||
(err, resolved) => {
|
||||
if (err) {
|
||||
done(err)
|
||||
return
|
||||
}
|
||||
|
||||
const realpath = fs.realpathSync(resolved)
|
||||
const realpath = fs.realpathSync(resolved)
|
||||
|
||||
if (importedFiles.has(realpath)) {
|
||||
done({ contents: '' })
|
||||
return
|
||||
}
|
||||
importedFiles.add(realpath)
|
||||
if (importedFiles.has(realpath)) {
|
||||
done({ contents: '' })
|
||||
return
|
||||
}
|
||||
importedFiles.add(realpath)
|
||||
|
||||
done({ file: realpath })
|
||||
})
|
||||
done({ file: realpath })
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const plugins = [
|
||||
autoprefixer,
|
||||
postcssLogical(),
|
||||
postcssDirPseudoClass(),
|
||||
]
|
||||
const postcssResult = await postcss(plugins)
|
||||
.process(scssResult.css, { from: file })
|
||||
const plugins = [autoprefixer, postcssLogical(), postcssDirPseudoClass()]
|
||||
const postcssResult = await postcss(plugins).process(scssResult.css, {
|
||||
from: file,
|
||||
})
|
||||
postcssResult.warnings().forEach((warn) => {
|
||||
console.warn(warn.toString())
|
||||
})
|
||||
|
|
@ -79,9 +80,10 @@ async function compileCSS () {
|
|||
chalk.magenta(path.relative(cwd, outfile)),
|
||||
)
|
||||
|
||||
const minifiedResult = await postcss([
|
||||
cssnano({ safe: true }),
|
||||
]).process(postcssResult.css, { from: outfile })
|
||||
const minifiedResult = await postcss([cssnano({ safe: true })]).process(
|
||||
postcssResult.css,
|
||||
{ from: outfile },
|
||||
)
|
||||
minifiedResult.warnings().forEach((warn) => {
|
||||
console.warn(warn.toString())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import fetchContrib from 'github-contributors-list/lib/contributors.js'
|
||||
import layoutStrategy from 'github-contributors-list/lib/strategies/layout_strategies/json.js'
|
||||
import sortStrategy from 'github-contributors-list/lib/strategies/sort_strategies/sort_desc.js'
|
||||
import filterStrategy from 'github-contributors-list/lib/strategies/filter_strategies/login.js'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import fs from 'node:fs/promises'
|
||||
import process from 'node:process'
|
||||
import fetchContrib from 'github-contributors-list/lib/contributors.js'
|
||||
import filterStrategy from 'github-contributors-list/lib/strategies/filter_strategies/login.js'
|
||||
import layoutStrategy from 'github-contributors-list/lib/strategies/layout_strategies/json.js'
|
||||
import sortStrategy from 'github-contributors-list/lib/strategies/sort_strategies/sort_desc.js'
|
||||
|
||||
const README_FILE_NAME = new URL('../README.md', import.meta.url)
|
||||
|
||||
|
|
|
|||
67
biome.json
Normal file
67
biome.json
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": [
|
||||
"packages/**",
|
||||
"examples/**",
|
||||
"bin/**",
|
||||
"e2e/**",
|
||||
"private/**",
|
||||
"!packages/**/{dist,lib}/**",
|
||||
"!node_modules",
|
||||
"!.svelte-kit",
|
||||
"!packages/@uppy/components/src/input.css"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"formatWithErrors": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"includes": ["!**/*.vue", "!**/*.svelte"],
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnusedImports": "off",
|
||||
"noUnusedFunctionParameters": "off",
|
||||
"noUnusedVariables": {
|
||||
"level": "error",
|
||||
"options": {
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off"
|
||||
},
|
||||
"a11y": {
|
||||
"noSvgWithoutTitle": "off"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"semicolons": "asNeeded"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import AwsS3Multipart from '@uppy/aws-s3'
|
||||
import { Uppy } from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import AwsS3Multipart from '@uppy/aws-s3'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import AwsS3 from '@uppy/aws-s3'
|
||||
import { Uppy } from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import AwsS3 from '@uppy/aws-s3'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Compressor from '@uppy/compressor'
|
||||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Compressor from '@uppy/compressor'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
const enc = new TextEncoder('utf-8')
|
||||
async function sign (secret, body) {
|
||||
async function sign(secret, body) {
|
||||
const algorithm = { name: 'HMAC', hash: 'SHA-384' }
|
||||
|
||||
const key = await crypto.subtle.importKey('raw', enc.encode(secret), algorithm, false, ['sign', 'verify'])
|
||||
const signature = await crypto.subtle.sign(algorithm.name, key, enc.encode(body))
|
||||
return `sha384:${Array.from(new Uint8Array(signature), x => x.toString(16).padStart(2, '0')).join('')}`
|
||||
const key = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
enc.encode(secret),
|
||||
algorithm,
|
||||
false,
|
||||
['sign', 'verify'],
|
||||
)
|
||||
const signature = await crypto.subtle.sign(
|
||||
algorithm.name,
|
||||
key,
|
||||
enc.encode(body),
|
||||
)
|
||||
return `sha384:${Array.from(new Uint8Array(signature), (x) => x.toString(16).padStart(2, '0')).join('')}`
|
||||
}
|
||||
function getExpiration (future) {
|
||||
function getExpiration(future) {
|
||||
return new Date(Date.now() + future)
|
||||
.toISOString()
|
||||
.replace('T', ' ')
|
||||
|
|
@ -20,12 +30,10 @@ function getExpiration (future) {
|
|||
* @param {object} params
|
||||
* @returns {{ params: string, signature?: string }}
|
||||
*/
|
||||
export default async function generateSignatureIfSecret (secret, params) {
|
||||
export default async function generateSignatureIfSecret(secret, params) {
|
||||
let signature
|
||||
if (secret) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
params.auth.expires = getExpiration(5 * 60 * 1000)
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
params = JSON.stringify(params)
|
||||
signature = await sign(secret, params)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import Url from '@uppy/url'
|
|||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
||||
function onShouldRetry (err, retryAttempt, options, next) {
|
||||
function onShouldRetry(err, retryAttempt, options, next) {
|
||||
if (err?.originalResponse?.getStatus() === 418) {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import ScreenCapture from '@uppy/screen-capture'
|
||||
import GoldenRetriever from '@uppy/golden-retriever'
|
||||
import ImageEditor from '@uppy/image-editor'
|
||||
import DropTarget from '@uppy/drop-target'
|
||||
import Audio from '@uppy/audio'
|
||||
import Compressor from '@uppy/compressor'
|
||||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import DropTarget from '@uppy/drop-target'
|
||||
import GoldenRetriever from '@uppy/golden-retriever'
|
||||
import ImageEditor from '@uppy/image-editor'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import ScreenCapture from '@uppy/screen-capture'
|
||||
import Webcam from '@uppy/webcam'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -21,16 +21,16 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Uppy from '@uppy/core'
|
||||
import Tus from '@uppy/tus'
|
||||
import {
|
||||
UppyContextProvider,
|
||||
Dropzone,
|
||||
FilesList,
|
||||
FilesGrid,
|
||||
FilesList,
|
||||
UploadButton,
|
||||
UppyContextProvider,
|
||||
} from '@uppy/vue'
|
||||
import Vue from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Uppy } from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
import Unsplash from '@uppy/unsplash'
|
||||
import Url from '@uppy/url'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
@ -10,7 +10,10 @@ import '@uppy/dashboard/dist/style.css'
|
|||
const companionUrl = 'http://localhost:3020'
|
||||
const uppy = new Uppy()
|
||||
.use(Dashboard, { target: '#app', inline: true })
|
||||
.use(XHRUpload, { endpoint: 'https://xhr-server.herokuapp.com/upload', limit: 6 })
|
||||
.use(XHRUpload, {
|
||||
endpoint: 'https://xhr-server.herokuapp.com/upload',
|
||||
limit: 6,
|
||||
})
|
||||
.use(Url, { target: Dashboard, companionUrl })
|
||||
.use(Unsplash, { target: Dashboard, companionUrl })
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
/** biome-ignore-all lint/nursery/useUniqueElementIds: it's fine */
|
||||
import Uppy from '@uppy/core'
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
import React, { useState } from 'react'
|
||||
import { Dashboard, DashboardModal, DragDrop } from '@uppy/react'
|
||||
import ThumbnailGenerator from '@uppy/thumbnail-generator'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import ThumbnailGenerator from '@uppy/thumbnail-generator'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default defineConfig({
|
|||
baseUrl: 'http://localhost:1234',
|
||||
specPattern: 'cypress/integration/*.spec.ts',
|
||||
|
||||
setupNodeEvents (on) {
|
||||
setupNodeEvents(on) {
|
||||
// implement node event listeners here
|
||||
installLogsPrinter(on)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ import deepFreeze from 'deep-freeze'
|
|||
* Default store + deepFreeze on setState to make sure nothing is mutated accidentally
|
||||
*/
|
||||
class DeepFrozenStore {
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.state = {}
|
||||
this.callbacks = []
|
||||
}
|
||||
|
||||
getState () {
|
||||
getState() {
|
||||
return this.state
|
||||
}
|
||||
|
||||
setState (patch) {
|
||||
setState(patch) {
|
||||
const prevState = { ...this.state }
|
||||
const nextState = deepFreeze({ ...this.state, ...patch })
|
||||
|
||||
|
|
@ -24,24 +24,21 @@ class DeepFrozenStore {
|
|||
this._publish(prevState, nextState, patch)
|
||||
}
|
||||
|
||||
subscribe (listener) {
|
||||
subscribe(listener) {
|
||||
this.callbacks.push(listener)
|
||||
return () => {
|
||||
// Remove the listener.
|
||||
this.callbacks.splice(
|
||||
this.callbacks.indexOf(listener),
|
||||
1,
|
||||
)
|
||||
this.callbacks.splice(this.callbacks.indexOf(listener), 1)
|
||||
}
|
||||
}
|
||||
|
||||
_publish (...args) {
|
||||
_publish(...args) {
|
||||
this.callbacks.forEach((listener) => {
|
||||
listener(...args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default function defaultStore () {
|
||||
export default function defaultStore() {
|
||||
return new DeepFrozenStore()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,14 +201,12 @@ describe('Dashboard with @uppy/aws-s3-multipart', () => {
|
|||
cy.wait('@post')
|
||||
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(300) // Wait an arbitrary amount of time as a user would do.
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
|
||||
cy.wait('@get')
|
||||
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(300) // Wait an arbitrary amount of time as a user would do.
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Transloadit from '@uppy/transloadit'
|
||||
import type Uppy from '@uppy/core'
|
||||
import type Transloadit from '@uppy/transloadit'
|
||||
|
||||
function getPlugin<M = any, B = any>(uppy: Uppy<M, B>) {
|
||||
return uppy.getPlugin<Transloadit<M, B>>('Transloadit')!
|
||||
|
|
@ -367,7 +367,6 @@ describe('Dashboard with Transloadit', () => {
|
|||
cy.wait('@firstUpload')
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(300) // Wait an arbitrary amount of time as a user would do.
|
||||
|
||||
cy.get('button[data-cy=togglePauseResume]').click()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
runRemoteUrlImageUploadTest,
|
||||
runRemoteUnsplashUploadTest,
|
||||
runRemoteUrlImageUploadTest,
|
||||
} from './reusable-tests.ts'
|
||||
|
||||
// NOTE: we have to use different files to upload per test
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
interceptCompanionUrlMetaRequest,
|
||||
interceptCompanionUrlRequest,
|
||||
runRemoteUrlImageUploadTest,
|
||||
runRemoteUnsplashUploadTest,
|
||||
runRemoteUrlImageUploadTest,
|
||||
} from './reusable-tests.ts'
|
||||
|
||||
describe('Dashboard with XHR', () => {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ describe('@uppy/react', () => {
|
|||
it('should render Drag & Drop in React and create a thumbail with @uppy/thumbnail-generator', () => {
|
||||
const spy = cy.spy()
|
||||
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore fix me
|
||||
cy.window().then(({ uppy }) => uppy.on('thumbnail:generated', spy))
|
||||
cy.get('@dragdrop-input').selectFile(
|
||||
|
|
@ -63,7 +62,6 @@ describe('@uppy/react', () => {
|
|||
{ force: true },
|
||||
)
|
||||
// not sure how I can accurately wait for the thumbnail
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(1000).then(() => expect(spy).to.be.called)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
createFakeFile: typeof createFakeFile
|
||||
}
|
||||
}
|
||||
|
|
@ -20,11 +19,9 @@ export function createFakeFile(
|
|||
b64?: string,
|
||||
): File {
|
||||
if (!b64) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
b64 =
|
||||
'PHN2ZyB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+CiAgPGNpcmNsZSBjeD0iNjAiIGN5PSI2MCIgcj0iNTAiLz4KPC9zdmc+Cg=='
|
||||
}
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
if (!type) type = 'image/svg+xml'
|
||||
|
||||
// https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import './commands.ts'
|
|||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
import installLogsCollector from 'cypress-terminal-report/src/installLogsCollector.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,37 @@
|
|||
#!/usr/bin/env node
|
||||
import prompts from 'prompts'
|
||||
import fs from 'node:fs/promises'
|
||||
import prompts from 'prompts'
|
||||
|
||||
/**
|
||||
* Utility function that strips indentation from multi-line strings.
|
||||
* Inspired from https://github.com/dmnd/dedent.
|
||||
*/
|
||||
function dedent (strings, ...parts) {
|
||||
function dedent(strings, ...parts) {
|
||||
const nonSpacingChar = /\S/m.exec(strings[0])
|
||||
if (nonSpacingChar == null) return ''
|
||||
|
||||
const indent = nonSpacingChar.index - strings[0].lastIndexOf('\n', nonSpacingChar.index) - 1
|
||||
const dedentEachLine = str => str.split('\n').map((line, i) => line.slice(i && indent)).join('\n')
|
||||
let returnLines = dedentEachLine(strings[0].slice(nonSpacingChar.index), indent)
|
||||
const indent =
|
||||
nonSpacingChar.index -
|
||||
strings[0].lastIndexOf('\n', nonSpacingChar.index) -
|
||||
1
|
||||
const dedentEachLine = (str) =>
|
||||
str
|
||||
.split('\n')
|
||||
.map((line, i) => line.slice(i && indent))
|
||||
.join('\n')
|
||||
let returnLines = dedentEachLine(
|
||||
strings[0].slice(nonSpacingChar.index),
|
||||
indent,
|
||||
)
|
||||
for (let i = 1; i < strings.length; i++) {
|
||||
returnLines += String(parts[i - 1]) + dedentEachLine(strings[i], indent)
|
||||
}
|
||||
return returnLines
|
||||
}
|
||||
|
||||
const packageNames = await fs.readdir(new URL('../packages/@uppy', import.meta.url))
|
||||
const packageNames = await fs.readdir(
|
||||
new URL('../packages/@uppy', import.meta.url),
|
||||
)
|
||||
const unwantedPackages = ['core', 'companion', 'redux-dev-tools', 'utils']
|
||||
|
||||
const { name } = await prompts({
|
||||
|
|
@ -39,9 +51,10 @@ const { packages } = await prompts({
|
|||
.map((pkg) => ({ title: pkg, value: pkg })),
|
||||
})
|
||||
|
||||
const camelcase = (str) => str
|
||||
.toLowerCase()
|
||||
.replace(/([-][a-z])/g, (group) => group.toUpperCase().replace('-', ''))
|
||||
const camelcase = (str) =>
|
||||
str
|
||||
.toLowerCase()
|
||||
.replace(/([-][a-z])/g, (group) => group.toUpperCase().replace('-', ''))
|
||||
|
||||
const testUrl = new URL(`cypress/integration/${name}.spec.ts`, import.meta.url)
|
||||
const test = dedent`
|
||||
|
|
|
|||
|
|
@ -6,38 +6,47 @@ const requestListener = (req, res) => {
|
|||
switch (endpoint) {
|
||||
case '/file-with-content-disposition': {
|
||||
const fileName = `DALL·E IMG_9078 - 学中文 🤑`
|
||||
res.setHeader('Content-Disposition', `attachment; filename="ASCII-name.zip"; filename*=UTF-8''${encodeURIComponent(fileName)}`)
|
||||
res.setHeader(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="ASCII-name.zip"; filename*=UTF-8''${encodeURIComponent(fileName)}`,
|
||||
)
|
||||
res.setHeader('Content-Type', 'image/jpeg')
|
||||
res.setHeader('Content-Length', '86500')
|
||||
break
|
||||
}
|
||||
case '/file-no-headers':
|
||||
break
|
||||
|
||||
case '/unknown-size': {
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.setHeader('Transfer-Encoding', 'chunked');
|
||||
const chunkSize = 1e5;
|
||||
if (req.method === 'GET') {
|
||||
let i = 0;
|
||||
const interval = setInterval(() => {
|
||||
if (i >= 10) { // 1MB
|
||||
clearInterval(interval);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
res.write(Buffer.from(Array.from({ length: chunkSize }, () => '1').join('')));
|
||||
res.write('\n');
|
||||
i++;
|
||||
}, 10);
|
||||
} else if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
} else {
|
||||
throw new Error('Unhandled method')
|
||||
|
||||
case '/unknown-size':
|
||||
{
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.setHeader('Transfer-Encoding', 'chunked')
|
||||
const chunkSize = 1e5
|
||||
if (req.method === 'GET') {
|
||||
let i = 0
|
||||
const interval = setInterval(() => {
|
||||
if (i >= 10) {
|
||||
// 1MB
|
||||
clearInterval(interval)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
res.write(
|
||||
Buffer.from(
|
||||
Array.from({ length: chunkSize }, () => '1').join(''),
|
||||
),
|
||||
)
|
||||
res.write('\n')
|
||||
i++
|
||||
}, 10)
|
||||
} else if (req.method === 'HEAD') {
|
||||
res.end()
|
||||
} else {
|
||||
throw new Error('Unhandled method')
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
res.writeHead(404).end('Unhandled request')
|
||||
}
|
||||
|
|
@ -45,7 +54,7 @@ const requestListener = (req, res) => {
|
|||
res.end()
|
||||
}
|
||||
|
||||
export default function startMockServer (host, port) {
|
||||
export default function startMockServer(host, port) {
|
||||
const server = http.createServer(requestListener)
|
||||
server.listen(port, host, () => {
|
||||
console.log(`Mock server is running on http://${host}:${port}`)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import http from 'node:http'
|
||||
import httpProxy from 'http-proxy'
|
||||
import process from 'node:process'
|
||||
import { execaNode } from 'execa';
|
||||
|
||||
import { execaNode } from 'execa'
|
||||
import httpProxy from 'http-proxy'
|
||||
|
||||
const numInstances = 3
|
||||
const lbPort = 3020
|
||||
const companionStartPort = 3021
|
||||
|
||||
// simple load balancer that will direct requests round robin between companion instances
|
||||
function createLoadBalancer (baseUrls) {
|
||||
function createLoadBalancer(baseUrls) {
|
||||
const proxy = httpProxy.createProxyServer({ ws: true })
|
||||
|
||||
let i = 0
|
||||
|
||||
function getTarget () {
|
||||
function getTarget() {
|
||||
return baseUrls[i % baseUrls.length]
|
||||
}
|
||||
|
||||
|
|
@ -50,40 +49,50 @@ function createLoadBalancer (baseUrls) {
|
|||
const isWindows = process.platform === 'win32'
|
||||
const isOSX = process.platform === 'darwin'
|
||||
|
||||
const startCompanion = ({ name, port }) => execaNode('packages/@uppy/companion/src/standalone/start-server.js', {
|
||||
nodeOptions: [
|
||||
'-r', 'dotenv/config',
|
||||
// Watch mode support is limited to Windows and macOS at the time of writing.
|
||||
...(isWindows || isOSX ? ['--watch-path', 'packages/@uppy/companion/src', '--watch'] : []),
|
||||
],
|
||||
cwd: new URL('../', import.meta.url),
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
// Note: these env variables will override anything set in .env
|
||||
...process.env,
|
||||
COMPANION_PORT: port,
|
||||
COMPANION_SECRET: 'development', // multi instance will not work without secret set
|
||||
COMPANION_PREAUTH_SECRET: 'development', // multi instance will not work without secret set
|
||||
COMPANION_ALLOW_LOCAL_URLS: 'true',
|
||||
COMPANION_ENABLE_URL_ENDPOINT: 'true',
|
||||
COMPANION_LOGGER_PROCESS_NAME: name,
|
||||
COMPANION_CLIENT_ORIGINS: 'true',
|
||||
},
|
||||
})
|
||||
|
||||
const startCompanion = ({ name, port }) =>
|
||||
execaNode('packages/@uppy/companion/src/standalone/start-server.js', {
|
||||
nodeOptions: [
|
||||
'-r',
|
||||
'dotenv/config',
|
||||
// Watch mode support is limited to Windows and macOS at the time of writing.
|
||||
...(isWindows || isOSX
|
||||
? ['--watch-path', 'packages/@uppy/companion/src', '--watch']
|
||||
: []),
|
||||
],
|
||||
cwd: new URL('../', import.meta.url),
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
// Note: these env variables will override anything set in .env
|
||||
...process.env,
|
||||
COMPANION_PORT: port,
|
||||
COMPANION_SECRET: 'development', // multi instance will not work without secret set
|
||||
COMPANION_PREAUTH_SECRET: 'development', // multi instance will not work without secret set
|
||||
COMPANION_ALLOW_LOCAL_URLS: 'true',
|
||||
COMPANION_ENABLE_URL_ENDPOINT: 'true',
|
||||
COMPANION_LOGGER_PROCESS_NAME: name,
|
||||
COMPANION_CLIENT_ORIGINS: 'true',
|
||||
},
|
||||
})
|
||||
|
||||
const hosts = Array.from({ length: numInstances }, (_, index) => {
|
||||
const port = companionStartPort + index
|
||||
return { index, port }
|
||||
})
|
||||
|
||||
console.log('Starting companion instances on ports', hosts.map(({ port }) => port))
|
||||
console.log(
|
||||
'Starting companion instances on ports',
|
||||
hosts.map(({ port }) => port),
|
||||
)
|
||||
|
||||
const companions = hosts.map(({ index, port }) => startCompanion({ name: `companion${index}`, port }))
|
||||
const companions = hosts.map(({ index, port }) =>
|
||||
startCompanion({ name: `companion${index}`, port }),
|
||||
)
|
||||
|
||||
let loadBalancer
|
||||
try {
|
||||
loadBalancer = createLoadBalancer(hosts.map(({ port }) => `http://localhost:${port}`))
|
||||
loadBalancer = createLoadBalancer(
|
||||
hosts.map(({ port }) => `http://localhost:${port}`),
|
||||
)
|
||||
await Promise.all(companions)
|
||||
} finally {
|
||||
loadBalancer?.close()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"noEmit": true,
|
||||
"target": "es2020",
|
||||
"lib": ["es2020", "dom"],
|
||||
"types": ["cypress"],
|
||||
"types": ["cypress"]
|
||||
},
|
||||
"include": ["cypress/**/*.ts"],
|
||||
"include": ["cypress/**/*.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { Component, type OnInit } from '@angular/core'
|
||||
import { Uppy } from '@uppy/core'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import Tus from '@uppy/tus'
|
||||
import GoogleDrive from '@uppy/google-drive'
|
||||
import Tus from '@uppy/tus'
|
||||
import Webcam from '@uppy/webcam'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { NgModule } from '@angular/core'
|
|||
import { BrowserModule } from '@angular/platform-browser'
|
||||
|
||||
import {
|
||||
UppyAngularDashboardModalModule,
|
||||
UppyAngularDashboardModule,
|
||||
UppyAngularStatusBarModule,
|
||||
UppyAngularDragDropModule,
|
||||
UppyAngularProgressBarModule,
|
||||
UppyAngularDashboardModalModule,
|
||||
UppyAngularStatusBarModule,
|
||||
} from '@uppy/angular'
|
||||
import { AppComponent } from './app.component'
|
||||
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ import { AppModule } from './app/app.module'
|
|||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err)) // eslint-disable-line no-console
|
||||
.catch((err) => console.error(err))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
@import '@uppy/core/dist/style.css';
|
||||
@import '@uppy/dashboard/dist/style.css';
|
||||
@import '@uppy/drag-drop/dist/style.css';
|
||||
@import '@uppy/progress-bar/dist/style.css';
|
||||
@import "@uppy/core/dist/style.css";
|
||||
@import "@uppy/dashboard/dist/style.css";
|
||||
@import "@uppy/drag-drop/dist/style.css";
|
||||
@import "@uppy/progress-bar/dist/style.css";
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@
|
|||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"lib": ["ES2022", "dom"],
|
||||
"lib": ["ES2022", "dom"]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true,
|
||||
},
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,22 @@ const app = require('express')()
|
|||
|
||||
const DATA_DIR = path.join(__dirname, 'tmp')
|
||||
|
||||
app.use(require('cors')({
|
||||
origin: 'http://localhost:3000',
|
||||
methods: ['GET', 'POST', 'OPTIONS'],
|
||||
credentials: true,
|
||||
}))
|
||||
app.use(
|
||||
require('cors')({
|
||||
origin: 'http://localhost:3000',
|
||||
methods: ['GET', 'POST', 'OPTIONS'],
|
||||
credentials: true,
|
||||
}),
|
||||
)
|
||||
app.use(require('cookie-parser')())
|
||||
app.use(require('body-parser').json())
|
||||
app.use(require('express-session')({
|
||||
secret: 'hello planet',
|
||||
saveUninitialized: false,
|
||||
resave: false,
|
||||
}))
|
||||
app.use(
|
||||
require('express-session')({
|
||||
secret: 'hello planet',
|
||||
saveUninitialized: false,
|
||||
resave: false,
|
||||
}),
|
||||
)
|
||||
|
||||
const options = {
|
||||
providerOptions: {
|
||||
|
|
@ -46,7 +50,7 @@ const options = {
|
|||
// Create the data directory here for the sake of the example.
|
||||
try {
|
||||
fs.accessSync(DATA_DIR)
|
||||
} catch (err) {
|
||||
} catch (_err) {
|
||||
fs.mkdirSync(DATA_DIR)
|
||||
}
|
||||
process.on('exit', () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const crypto = require('node:crypto')
|
||||
const { existsSync } = require('node:fs')
|
||||
|
|
@ -84,14 +82,16 @@ const extractFileParameters = (req) => {
|
|||
|
||||
return {
|
||||
filename: params.filename,
|
||||
contentType: params.type
|
||||
contentType: params.type,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the file parameters
|
||||
const validateFileParameters = (filename, contentType) => {
|
||||
if (!filename || !contentType) {
|
||||
throw new Error('Missing required parameters: filename and content type are required')
|
||||
throw new Error(
|
||||
'Missing required parameters: filename and content type are required',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +195,6 @@ app.post('/s3/multipart', (req, res, next) => {
|
|||
})
|
||||
|
||||
function validatePartNumber(partNumber) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
partNumber = Number(partNumber)
|
||||
return Number.isInteger(partNumber) && partNumber >= 1 && partNumber <= 10_000
|
||||
}
|
||||
|
|
@ -204,19 +203,15 @@ app.get('/s3/multipart/:uploadId/:partNumber', (req, res, next) => {
|
|||
const { key } = req.query
|
||||
|
||||
if (!validatePartNumber(partNumber)) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error: 's3: the part number must be an integer between 1 and 10000.',
|
||||
})
|
||||
return res.status(400).json({
|
||||
error: 's3: the part number must be an integer between 1 and 10000.',
|
||||
})
|
||||
}
|
||||
if (typeof key !== 'string') {
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
return res.status(400).json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
}
|
||||
|
||||
return getSignedUrl(
|
||||
|
|
@ -241,38 +236,39 @@ app.get('/s3/multipart/:uploadId', (req, res, next) => {
|
|||
const { key } = req.query
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
res
|
||||
.status(400)
|
||||
.json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
res.status(400).json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const parts = []
|
||||
|
||||
function listPartsPage(startsAt = undefined) {
|
||||
client.send(new ListPartsCommand({
|
||||
Bucket: process.env.COMPANION_AWS_BUCKET,
|
||||
Key: key,
|
||||
UploadId: uploadId,
|
||||
PartNumberMarker: startsAt,
|
||||
}), (err, data) => {
|
||||
if (err) {
|
||||
next(err)
|
||||
return
|
||||
}
|
||||
client.send(
|
||||
new ListPartsCommand({
|
||||
Bucket: process.env.COMPANION_AWS_BUCKET,
|
||||
Key: key,
|
||||
UploadId: uploadId,
|
||||
PartNumberMarker: startsAt,
|
||||
}),
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
next(err)
|
||||
return
|
||||
}
|
||||
|
||||
parts.push(...data.Parts)
|
||||
|
||||
// continue to get list of all uploaded parts until the IsTruncated flag is false
|
||||
if (data.IsTruncated) {
|
||||
listPartsPage(data.NextPartNumberMarker)
|
||||
} else {
|
||||
res.json(parts)
|
||||
}
|
||||
})
|
||||
// continue to get list of all uploaded parts until the IsTruncated flag is false
|
||||
if (data.IsTruncated) {
|
||||
listPartsPage(data.NextPartNumberMarker)
|
||||
} else {
|
||||
res.json(parts)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
listPartsPage()
|
||||
})
|
||||
|
|
@ -292,19 +288,15 @@ app.post('/s3/multipart/:uploadId/complete', (req, res, next) => {
|
|||
const { parts } = req.body
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
return res.status(400).json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
}
|
||||
if (!Array.isArray(parts) || !parts.every(isValidPart)) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error: 's3: `parts` must be an array of {ETag, PartNumber} objects.',
|
||||
})
|
||||
return res.status(400).json({
|
||||
error: 's3: `parts` must be an array of {ETag, PartNumber} objects.',
|
||||
})
|
||||
}
|
||||
|
||||
return client.send(
|
||||
|
|
@ -335,12 +327,10 @@ app.delete('/s3/multipart/:uploadId', (req, res, next) => {
|
|||
const { key } = req.query
|
||||
|
||||
if (typeof key !== 'string') {
|
||||
return res
|
||||
.status(400)
|
||||
.json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
return res.status(400).json({
|
||||
error:
|
||||
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
|
||||
})
|
||||
}
|
||||
|
||||
return client.send(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import AwsS3 from '@uppy/aws-s3'
|
||||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import AwsS3 from '@uppy/aws-s3'
|
||||
|
||||
const uppy = new Uppy({
|
||||
debug: true,
|
||||
|
|
@ -13,7 +13,7 @@ uppy.use(Dashboard, {
|
|||
uppy.use(AwsS3, {
|
||||
shouldUseMultipart: false, // The PHP backend only supports non-multipart uploads
|
||||
|
||||
getUploadParameters (file) {
|
||||
getUploadParameters(file) {
|
||||
// Send a request to our PHP signing endpoint.
|
||||
return fetch('/s3-sign.php', {
|
||||
method: 'post',
|
||||
|
|
@ -26,17 +26,19 @@ uppy.use(AwsS3, {
|
|||
filename: file.name,
|
||||
contentType: file.type,
|
||||
}),
|
||||
}).then((response) => {
|
||||
// Parse the JSON response.
|
||||
return response.json()
|
||||
}).then((data) => {
|
||||
// Return an object in the correct shape.
|
||||
return {
|
||||
method: data.method,
|
||||
url: data.url,
|
||||
fields: data.fields,
|
||||
headers: data.headers,
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
// Parse the JSON response.
|
||||
return response.json()
|
||||
})
|
||||
.then((data) => {
|
||||
// Return an object in the correct shape.
|
||||
return {
|
||||
method: data.method,
|
||||
url: data.url,
|
||||
fields: data.fields,
|
||||
headers: data.headers,
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Instagram from '@uppy/instagram'
|
||||
import GoogleDrive from '@uppy/google-drive'
|
||||
import Instagram from '@uppy/instagram'
|
||||
import Tus from '@uppy/tus'
|
||||
import Url from '@uppy/url'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import Tus from '@uppy/tus'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
@ -33,7 +33,10 @@ const uppy = new Uppy({
|
|||
note: '2 files, images and video only',
|
||||
restrictions: { requiredMetaFields: ['caption'] },
|
||||
})
|
||||
.use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
|
||||
.use(GoogleDrive, {
|
||||
target: Dashboard,
|
||||
companionUrl: 'http://localhost:3020',
|
||||
})
|
||||
.use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
|
||||
.use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
|
||||
.use(Webcam, { target: Dashboard })
|
||||
|
|
@ -67,4 +70,3 @@ uppy.on('complete', (result) => {
|
|||
// console.log('Registration failed with ' + error)
|
||||
// })
|
||||
// }
|
||||
/* eslint-enable */
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
// https://uppy.io/docs/golden-retriever/
|
||||
|
||||
/* globals clients */
|
||||
/* eslint-disable no-restricted-globals */
|
||||
|
||||
const fileCache = Object.create(null)
|
||||
|
||||
function getCache (name) {
|
||||
function getCache(name) {
|
||||
if (!fileCache[name]) {
|
||||
fileCache[name] = Object.create(null)
|
||||
}
|
||||
|
|
@ -17,15 +16,14 @@ function getCache (name) {
|
|||
self.addEventListener('install', (event) => {
|
||||
console.log('Installing Uppy Service Worker...')
|
||||
|
||||
event.waitUntil(Promise.resolve()
|
||||
.then(() => self.skipWaiting()))
|
||||
event.waitUntil(Promise.resolve().then(() => self.skipWaiting()))
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
function sendMessageToAllClients (msg) {
|
||||
function sendMessageToAllClients(msg) {
|
||||
clients.matchAll().then((clients) => {
|
||||
clients.forEach((client) => {
|
||||
client.postMessage(msg)
|
||||
|
|
@ -33,17 +31,17 @@ function sendMessageToAllClients (msg) {
|
|||
})
|
||||
}
|
||||
|
||||
function addFile (store, file) {
|
||||
function addFile(store, file) {
|
||||
getCache(store)[file.id] = file.data
|
||||
console.log('Added file blob to service worker cache:', file.data)
|
||||
}
|
||||
|
||||
function removeFile (store, fileID) {
|
||||
function removeFile(store, fileID) {
|
||||
delete getCache(store)[fileID]
|
||||
console.log('Removed file blob from service worker cache:', fileID)
|
||||
}
|
||||
|
||||
function getFiles (store) {
|
||||
function getFiles(store) {
|
||||
sendMessageToAllClients({
|
||||
type: 'uppy/ALL_FILES',
|
||||
store,
|
||||
|
|
@ -63,6 +61,7 @@ self.addEventListener('message', (event) => {
|
|||
getFiles(event.data.store)
|
||||
break
|
||||
|
||||
default: throw new Error('unreachable')
|
||||
default:
|
||||
throw new Error('unreachable')
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/** @jsx h */
|
||||
|
||||
import { getAllowedHosts, Provider, tokenStorage } from '@uppy/companion-client'
|
||||
import { UIPlugin } from '@uppy/core'
|
||||
import { Provider, getAllowedHosts, tokenStorage } from '@uppy/companion-client'
|
||||
import { ProviderViews } from '@uppy/provider-views'
|
||||
import { h } from 'preact'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import GoogleDrive from '@uppy/google-drive'
|
||||
import Tus from '@uppy/tus'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import MyCustomProvider from './MyCustomProvider.jsx'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ const { Readable } = require('node:stream')
|
|||
|
||||
const BASE_URL = 'https://api.unsplash.com'
|
||||
|
||||
function adaptData (res) {
|
||||
function adaptData(res) {
|
||||
const data = {
|
||||
username: null,
|
||||
items: [],
|
||||
|
|
@ -34,27 +34,29 @@ function adaptData (res) {
|
|||
class MyCustomProvider {
|
||||
static version = 2
|
||||
|
||||
static get oauthProvider () {
|
||||
static get oauthProvider() {
|
||||
return 'myunsplash'
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async list ({ token, directory }) {
|
||||
async list({ token, directory }) {
|
||||
const path = directory ? `/${directory}/photos` : ''
|
||||
|
||||
const resp = await fetch(`${BASE_URL}/collections${path}`, {
|
||||
headers:{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
if (!resp.ok) {
|
||||
throw new Error(`Errornous HTTP response (${resp.status} ${resp.statusText})`)
|
||||
throw new Error(
|
||||
`Errornous HTTP response (${resp.status} ${resp.statusText})`,
|
||||
)
|
||||
}
|
||||
return adaptData(await resp.json())
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
async download ({ id, token }) {
|
||||
async download({ id, token }) {
|
||||
const resp = await fetch(`${BASE_URL}/photos/${id}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
|
@ -62,11 +64,16 @@ class MyCustomProvider {
|
|||
})
|
||||
|
||||
const contentLengthStr = resp.headers['content-length']
|
||||
const contentLength = parseInt(contentLengthStr, 10);
|
||||
const size = !Number.isNaN(contentLength) && contentLength >= 0 ? contentLength : undefined;
|
||||
const contentLength = parseInt(contentLengthStr, 10)
|
||||
const size =
|
||||
!Number.isNaN(contentLength) && contentLength >= 0
|
||||
? contentLength
|
||||
: undefined
|
||||
|
||||
if (!resp.ok) {
|
||||
throw new Error(`Errornous HTTP response (${resp.status} ${resp.statusText})`)
|
||||
throw new Error(
|
||||
`Errornous HTTP response (${resp.status} ${resp.statusText})`,
|
||||
)
|
||||
}
|
||||
return { stream: Readable.fromWeb(resp.body), size }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ const { mkdtempSync } = require('node:fs')
|
|||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
|
||||
require('dotenv').config({ path: path.join(__dirname, '..', '..', '..', '.env') })
|
||||
require('dotenv').config({
|
||||
path: path.join(__dirname, '..', '..', '..', '.env'),
|
||||
})
|
||||
const express = require('express')
|
||||
// the ../../../packages is just to use the local version
|
||||
// instead of the npm version—in a real app use `require('@uppy/companion')`
|
||||
|
|
@ -14,11 +16,13 @@ const MyCustomProvider = require('./CustomProvider.cjs')
|
|||
const app = express()
|
||||
|
||||
app.use(bodyParser.json())
|
||||
app.use(session({
|
||||
secret: 'some-secret',
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
}))
|
||||
app.use(
|
||||
session({
|
||||
secret: 'some-secret',
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
}),
|
||||
)
|
||||
|
||||
// Routes
|
||||
app.get('/', (req, res) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import AwsS3 from '@uppy/aws-s3'
|
||||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import AwsS3 from '@uppy/aws-s3'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -17,10 +17,22 @@ const companion = require('../../packages/@uppy/companion')
|
|||
* - COMPANION_AWS_FORCE_PATH_STYLE - Indicates if s3ForcePathStyle should be used rather than subdomain for S3 buckets.
|
||||
*/
|
||||
|
||||
if (!process.env.COMPANION_AWS_REGION) throw new Error('Missing Space region, please set the COMPANION_AWS_REGION environment variable (eg. "COMPANION_AWS_REGION=ams3")')
|
||||
if (!process.env.COMPANION_AWS_KEY) throw new Error('Missing access key, please set the COMPANION_AWS_KEY environment variable')
|
||||
if (!process.env.COMPANION_AWS_SECRET) throw new Error('Missing secret key, please set the COMPANION_AWS_SECRET environment variable')
|
||||
if (!process.env.COMPANION_AWS_BUCKET) throw new Error('Missing Space name, please set the COMPANION_AWS_BUCKET environment variable')
|
||||
if (!process.env.COMPANION_AWS_REGION)
|
||||
throw new Error(
|
||||
'Missing Space region, please set the COMPANION_AWS_REGION environment variable (eg. "COMPANION_AWS_REGION=ams3")',
|
||||
)
|
||||
if (!process.env.COMPANION_AWS_KEY)
|
||||
throw new Error(
|
||||
'Missing access key, please set the COMPANION_AWS_KEY environment variable',
|
||||
)
|
||||
if (!process.env.COMPANION_AWS_SECRET)
|
||||
throw new Error(
|
||||
'Missing secret key, please set the COMPANION_AWS_SECRET environment variable',
|
||||
)
|
||||
if (!process.env.COMPANION_AWS_BUCKET)
|
||||
throw new Error(
|
||||
'Missing Space name, please set the COMPANION_AWS_BUCKET environment variable',
|
||||
)
|
||||
|
||||
// Prepare the server.
|
||||
const PORT = process.env.PORT || 3452
|
||||
|
|
@ -54,9 +66,11 @@ const { app: companionApp } = companion.app({
|
|||
|
||||
app.use('/companion', companionApp)
|
||||
|
||||
require('vite').createServer({ clearScreen: false, server:{ middlewareMode: true } }).then(({ middlewares }) => {
|
||||
app.use(middlewares)
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Listening on http://localhost:${PORT}/...`)
|
||||
require('vite')
|
||||
.createServer({ clearScreen: false, server: { middlewareMode: true } })
|
||||
.then(({ middlewares }) => {
|
||||
app.use(middlewares)
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Listening on http://localhost:${PORT}/...`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
import http from 'node:http'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { mkdir } from 'node:fs/promises'
|
||||
|
||||
import formidable from 'formidable'
|
||||
|
||||
|
|
@ -12,41 +10,50 @@ const UPLOAD_DIR = new URL('./uploads/', import.meta.url)
|
|||
|
||||
await mkdir(UPLOAD_DIR, { recursive: true })
|
||||
|
||||
http.createServer((req, res) => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
|
||||
'Access-Control-Max-Age': 2592000, // 30 days
|
||||
/** add other headers as per requirement */
|
||||
}
|
||||
http
|
||||
.createServer((req, res) => {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
|
||||
'Access-Control-Max-Age': 2592000, // 30 days
|
||||
/** add other headers as per requirement */
|
||||
}
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204, headers)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
if (req.url === '/upload' && req.method.toLowerCase() === 'post') {
|
||||
// parse a file upload
|
||||
const form = formidable({
|
||||
keepExtensions: true,
|
||||
uploadDir: fileURLToPath(UPLOAD_DIR),
|
||||
})
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.writeHead(204, headers)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
if (req.url === '/upload' && req.method.toLowerCase() === 'post') {
|
||||
// parse a file upload
|
||||
const form = formidable({
|
||||
keepExtensions: true,
|
||||
uploadDir: fileURLToPath(UPLOAD_DIR),
|
||||
})
|
||||
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err) {
|
||||
console.log('some error', err)
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err) {
|
||||
console.log('some error', err)
|
||||
res.writeHead(200, headers)
|
||||
res.write(JSON.stringify(err))
|
||||
return res.end()
|
||||
}
|
||||
const {
|
||||
file: [{ filepath, originalFilename, mimetype, size }],
|
||||
} = files
|
||||
console.log('saved file', {
|
||||
filepath,
|
||||
originalFilename,
|
||||
mimetype,
|
||||
size,
|
||||
})
|
||||
res.writeHead(200, headers)
|
||||
res.write(JSON.stringify(err))
|
||||
res.write(JSON.stringify({ fields, files }))
|
||||
return res.end()
|
||||
}
|
||||
const { file:[{ filepath, originalFilename, mimetype, size }] } = files
|
||||
console.log('saved file', { filepath, originalFilename, mimetype, size })
|
||||
res.writeHead(200, headers)
|
||||
res.write(JSON.stringify({ fields, files }))
|
||||
return res.end()
|
||||
})
|
||||
}
|
||||
}).listen(3020, () => {
|
||||
console.log('server started')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
.listen(3020, () => {
|
||||
console.log('server started')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
|
|
|
|||
74
examples/react-native-expo/App.js
vendored
74
examples/react-native-expo/App.js
vendored
|
|
@ -1,16 +1,16 @@
|
|||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import { Text, View, Image, StyleSheet } from 'react-native'
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import Uppy from '@uppy/core'
|
||||
import FilePicker from '@uppy/react-native'
|
||||
import Tus from '@uppy/tus'
|
||||
import FilePicker from '@uppy/react-native'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Image, StyleSheet, Text, View } from 'react-native'
|
||||
import FileList from './FileList'
|
||||
import PauseResumeButton from './PauseResumeButton'
|
||||
import ProgressBar from './ProgressBar'
|
||||
import SelectFiles from './SelectFilesButton'
|
||||
import getTusFileReader from './tusFileReader'
|
||||
|
||||
export default function App () {
|
||||
export default function App() {
|
||||
const [state, _setState] = useState({
|
||||
progress: 0,
|
||||
total: 0,
|
||||
|
|
@ -24,15 +24,19 @@ export default function App () {
|
|||
totalProgress: 0,
|
||||
})
|
||||
|
||||
const setState = useCallback((newState) => _setState((oldState) => ({ ...oldState, ...newState })), [])
|
||||
const setState = useCallback(
|
||||
(newState) => _setState((oldState) => ({ ...oldState, ...newState })),
|
||||
[],
|
||||
)
|
||||
|
||||
const [uppy] = useState(() => new Uppy({ autoProceed: true, debug: true })
|
||||
.use(Tus, {
|
||||
endpoint: 'https://tusd.tusdemo.net/files/',
|
||||
urlStorage: AsyncStorage,
|
||||
fileReader: getTusFileReader,
|
||||
chunkSize: 10 * 1024 * 1024, // keep the chunk size small to avoid memory exhaustion
|
||||
}));
|
||||
const [uppy] = useState(() =>
|
||||
new Uppy({ autoProceed: true, debug: true }).use(Tus, {
|
||||
endpoint: 'https://tusd.tusdemo.net/files/',
|
||||
urlStorage: AsyncStorage,
|
||||
fileReader: getTusFileReader,
|
||||
chunkSize: 10 * 1024 * 1024, // keep the chunk size small to avoid memory exhaustion
|
||||
}),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
uppy.on('upload-progress', (file, progress) => {
|
||||
|
|
@ -48,7 +52,9 @@ export default function App () {
|
|||
})
|
||||
uppy.on('complete', (result) => {
|
||||
setState({
|
||||
status: result.successful[0] ? 'Upload complete ✅' : 'Upload errored ❌',
|
||||
status: result.successful[0]
|
||||
? 'Upload complete ✅'
|
||||
: 'Upload errored ❌',
|
||||
uploadURL: result.successful[0] ? result.successful[0].uploadURL : null,
|
||||
uploadComplete: true,
|
||||
uploadStarted: false,
|
||||
|
|
@ -98,34 +104,24 @@ export default function App () {
|
|||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.root}
|
||||
>
|
||||
<Text
|
||||
style={styles.title}
|
||||
>
|
||||
Uppy in React Native
|
||||
</Text>
|
||||
<View style={styles.root}>
|
||||
<Text style={styles.title}>Uppy in React Native</Text>
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<Image
|
||||
style={styles.logo}
|
||||
// eslint-disable-next-line global-require
|
||||
source={require('./assets/uppy-logo.png')}
|
||||
/>
|
||||
<Image style={styles.logo} source={require('./assets/uppy-logo.png')} />
|
||||
</View>
|
||||
<SelectFiles showFilePicker={showFilePicker} />
|
||||
|
||||
{state.info ? (
|
||||
<Text
|
||||
style={{
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
color: '#b8006b',
|
||||
}}
|
||||
>
|
||||
{state.info.message}
|
||||
</Text>
|
||||
) : null}
|
||||
{state.info
|
||||
? <Text
|
||||
style={{
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
color: '#b8006b',
|
||||
}}
|
||||
>
|
||||
{state.info.message}
|
||||
</Text>
|
||||
: null}
|
||||
|
||||
<ProgressBar progress={state.totalProgress} />
|
||||
|
||||
|
|
@ -148,7 +144,9 @@ export default function App () {
|
|||
{uppy && <FileList uppy={uppy} />}
|
||||
|
||||
{state.status && <Text>Status: {state.status}</Text>}
|
||||
<Text>{state.progress} of {state.total}</Text>
|
||||
<Text>
|
||||
{state.progress} of {state.total}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
36
examples/react-native-expo/FileList.js
vendored
36
examples/react-native-expo/FileList.js
vendored
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react'
|
||||
import { StyleSheet, View, FlatList, Text, Image } from 'react-native'
|
||||
|
||||
import getFileTypeIcon from '@uppy/dashboard/lib/utils/getFileTypeIcon.js'
|
||||
import renderStringFromJSX from 'preact-render-to-string'
|
||||
import React from 'react'
|
||||
import { FlatList, Image, StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
const fileIcon = require('./assets/file-icon.png')
|
||||
|
||||
|
|
@ -15,18 +14,15 @@ const truncateString = (str) => {
|
|||
return str
|
||||
}
|
||||
|
||||
function FileIcon () {
|
||||
function FileIcon() {
|
||||
return (
|
||||
<View style={styles.itemIconContainer}>
|
||||
<Image
|
||||
style={styles.itemIcon}
|
||||
source={fileIcon}
|
||||
/>
|
||||
<Image style={styles.itemIcon} source={fileIcon} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function UppyDashboardFileIcon ({ type }) {
|
||||
function UppyDashboardFileIcon({ type }) {
|
||||
const icon = renderStringFromJSX(getFileTypeIcon(type).icon)
|
||||
if (!icon) {
|
||||
return <FileIcon />
|
||||
|
|
@ -44,7 +40,7 @@ function UppyDashboardFileIcon ({ type }) {
|
|||
)
|
||||
}
|
||||
|
||||
export default function FileList ({ uppy }) {
|
||||
export default function FileList({ uppy }) {
|
||||
const uppyFiles = uppy.store.state.files
|
||||
const uppyFilesArray = Object.keys(uppyFiles).map((id) => uppyFiles[id])
|
||||
|
||||
|
|
@ -57,15 +53,15 @@ export default function FileList ({ uppy }) {
|
|||
renderItem={({ item }) => {
|
||||
return (
|
||||
<View style={styles.item}>
|
||||
{item.type === 'image' ? (
|
||||
<Image
|
||||
style={styles.itemImage}
|
||||
source={{ uri: item.data.uri }}
|
||||
/>
|
||||
) : (
|
||||
<UppyDashboardFileIcon type={item.type} />
|
||||
)}
|
||||
<Text style={styles.itemName}>{truncateString(item.name, 20)}</Text>
|
||||
{item.type === 'image'
|
||||
? <Image
|
||||
style={styles.itemImage}
|
||||
source={{ uri: item.data.uri }}
|
||||
/>
|
||||
: <UppyDashboardFileIcon type={item.type} />}
|
||||
<Text style={styles.itemName}>
|
||||
{truncateString(item.name, 20)}
|
||||
</Text>
|
||||
<Text style={styles.itemType}>{item.type}</Text>
|
||||
</View>
|
||||
)
|
||||
|
|
@ -81,7 +77,7 @@ const styles = StyleSheet.create({
|
|||
marginBottom: 20,
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems:'center',
|
||||
alignItems: 'center',
|
||||
marginRight: -25,
|
||||
},
|
||||
item: {
|
||||
|
|
|
|||
18
examples/react-native-expo/PauseResumeButton.js
vendored
18
examples/react-native-expo/PauseResumeButton.js
vendored
|
|
@ -1,21 +1,19 @@
|
|||
import React from 'react'
|
||||
import { StyleSheet, Text, TouchableHighlight } from 'react-native'
|
||||
|
||||
export default function PauseResumeButton ({ uploadStarted, uploadComplete, isPaused, onPress }) {
|
||||
export default function PauseResumeButton({
|
||||
uploadStarted,
|
||||
uploadComplete,
|
||||
isPaused,
|
||||
onPress,
|
||||
}) {
|
||||
if (!uploadStarted || uploadComplete) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={onPress}
|
||||
style={styles.button}
|
||||
>
|
||||
<Text
|
||||
style={styles.text}
|
||||
>
|
||||
{isPaused ? 'Resume' : 'Pause'}
|
||||
</Text>
|
||||
<TouchableHighlight onPress={onPress} style={styles.button}>
|
||||
<Text style={styles.text}>{isPaused ? 'Resume' : 'Pause'}</Text>
|
||||
</TouchableHighlight>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
22
examples/react-native-expo/ProgressBar.js
vendored
22
examples/react-native-expo/ProgressBar.js
vendored
|
|
@ -1,19 +1,21 @@
|
|||
import React from 'react'
|
||||
import { View, Text, StyleSheet } from 'react-native'
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
|
||||
const colorGreen = '#0b8600'
|
||||
const colorBlue = '#006bb7'
|
||||
|
||||
export default function ProgressBar ({ progress }) {
|
||||
export default function ProgressBar({ progress }) {
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<View
|
||||
style={styles.wrapper}
|
||||
>
|
||||
<View style={[styles.bar, {
|
||||
backgroundColor: progress === 100 ? colorGreen : colorBlue,
|
||||
width: `${progress}%`,
|
||||
}]}
|
||||
<View style={styles.wrapper}>
|
||||
<View
|
||||
style={[
|
||||
styles.bar,
|
||||
{
|
||||
backgroundColor: progress === 100 ? colorGreen : colorBlue,
|
||||
width: `${progress}%`,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
<Text>{progress ? `${progress}%` : null}</Text>
|
||||
|
|
@ -26,7 +28,7 @@ const styles = StyleSheet.create({
|
|||
marginTop: 15,
|
||||
marginBottom: 15,
|
||||
},
|
||||
wrapper:{
|
||||
wrapper: {
|
||||
height: 5,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#dee1e3',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import React from 'react'
|
||||
import { Text, TouchableHighlight, StyleSheet } from 'react-native'
|
||||
import { StyleSheet, Text, TouchableHighlight } from 'react-native'
|
||||
|
||||
export default function SelectFiles ({ showFilePicker }) {
|
||||
export default function SelectFiles({ showFilePicker }) {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
onPress={showFilePicker}
|
||||
style={styles.button}
|
||||
>
|
||||
<TouchableHighlight onPress={showFilePicker} style={styles.button}>
|
||||
<Text style={styles.text}>Select files</Text>
|
||||
</TouchableHighlight>
|
||||
)
|
||||
|
|
|
|||
24
examples/react-native-expo/tusFileReader.js
vendored
24
examples/react-native-expo/tusFileReader.js
vendored
|
|
@ -1,26 +1,30 @@
|
|||
import * as FileSystem from 'expo-file-system'
|
||||
import base64 from 'base64-js'
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
|
||||
export default function getTusFileReader (file, chunkSize, cb) {
|
||||
FileSystem.getInfoAsync(file.uri, { size: true }).then((info) => {
|
||||
cb(null, new TusFileReader(file, info.size))
|
||||
}).catch(cb)
|
||||
export default function getTusFileReader(file, chunkSize, cb) {
|
||||
FileSystem.getInfoAsync(file.uri, { size: true })
|
||||
.then((info) => {
|
||||
cb(null, new TusFileReader(file, info.size))
|
||||
})
|
||||
.catch(cb)
|
||||
}
|
||||
|
||||
class TusFileReader {
|
||||
constructor (file, size) {
|
||||
constructor(file, size) {
|
||||
this.file = file
|
||||
this.size = size
|
||||
}
|
||||
|
||||
slice (start, end, cb) {
|
||||
slice(start, end, cb) {
|
||||
const options = {
|
||||
encoding: FileSystem.EncodingType.Base64,
|
||||
length: Math.min(end, this.size) - start,
|
||||
position: start,
|
||||
}
|
||||
FileSystem.readAsStringAsync(this.file.uri, options).then((data) => {
|
||||
cb(null, base64.toByteArray(data))
|
||||
}).catch(cb)
|
||||
FileSystem.readAsStringAsync(this.file.uri, options)
|
||||
.then((data) => {
|
||||
cb(null, base64.toByteArray(data))
|
||||
})
|
||||
.catch(cb)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
/* eslint-disable no-shadow */
|
||||
/* eslint-disable jsx-a11y/media-has-caption */
|
||||
/* eslint-disable react/button-has-type */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import React, { useState, useRef } from 'react'
|
||||
/** biome-ignore-all lint/nursery/useUniqueElementIds: it's fine */
|
||||
import Uppy from '@uppy/core'
|
||||
import {
|
||||
Dropzone,
|
||||
FilesGrid,
|
||||
|
|
@ -11,16 +7,15 @@ import {
|
|||
UploadButton,
|
||||
UppyContextProvider,
|
||||
} from '@uppy/react'
|
||||
import Uppy from '@uppy/core'
|
||||
import UppyRemoteSources from '@uppy/remote-sources'
|
||||
import UppyScreenCapture from '@uppy/screen-capture'
|
||||
import Tus from '@uppy/tus'
|
||||
import UppyWebcam from '@uppy/webcam'
|
||||
import UppyRemoteSources from '@uppy/remote-sources'
|
||||
|
||||
import UppyScreenCapture from '@uppy/screen-capture'
|
||||
import { RemoteSource } from './RemoteSource.js'
|
||||
import Webcam from './Webcam.tsx'
|
||||
import ScreenCapture from './ScreenCapture.tsx'
|
||||
import React, { useRef, useState } from 'react'
|
||||
import CustomDropzone from './CustomDropzone.tsx'
|
||||
import { RemoteSource } from './RemoteSource.js'
|
||||
import ScreenCapture from './ScreenCapture.tsx'
|
||||
import Webcam from './Webcam.tsx'
|
||||
|
||||
import './app.css'
|
||||
import '@uppy/react/dist/styles.css'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable react/react-in-jsx-scope */
|
||||
/* eslint-disable react/button-has-type */
|
||||
import { useDropzone, useFileInput, ProviderIcon } from '@uppy/react'
|
||||
import { ProviderIcon, useDropzone, useFileInput } from '@uppy/react'
|
||||
|
||||
export interface CustomDropzoneProps {
|
||||
openModal: (plugin: 'webcam' | 'dropbox' | 'screen-capture') => void
|
||||
|
|
@ -16,7 +13,6 @@ export function CustomDropzone({ openModal }: CustomDropzoneProps) {
|
|||
<input {...getInputProps()} className="hidden" />
|
||||
<div
|
||||
{...getRootProps()}
|
||||
role="button"
|
||||
className="border-2 border-dashed border-gray-300 rounded-lg p-6 bg-gray-50 transition-colors duration-200"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable react/jsx-props-no-spreading, react/button-has-type */
|
||||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import React from 'react'
|
||||
|
||||
type ButtonProps = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
/* eslint-disable no-shadow */
|
||||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import { type PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import { useRemoteSource } from '@uppy/react'
|
||||
import type { AvailablePluginsKeys } from '@uppy/remote-sources'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
|
@ -104,11 +102,12 @@ export function RemoteSource({
|
|||
{state.breadcrumbs.map((breadcrumb, index) => (
|
||||
<>
|
||||
{index > 0 && <span className="text-gray-500">></span>}{' '}
|
||||
{index === state.breadcrumbs.length - 1 ?
|
||||
{index === state.breadcrumbs.length - 1 ? (
|
||||
<span>
|
||||
{breadcrumb.type === 'root' ? 'Dropbox' : breadcrumb.data.name}
|
||||
</span>
|
||||
: <button
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="text-blue-500"
|
||||
key={breadcrumb.id}
|
||||
|
|
@ -116,7 +115,7 @@ export function RemoteSource({
|
|||
>
|
||||
{breadcrumb.type === 'root' ? 'Dropbox' : breadcrumb.data.name}
|
||||
</button>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
|
|
@ -134,9 +133,10 @@ export function RemoteSource({
|
|||
</div>
|
||||
|
||||
<ul className="p-4 flex-1 overflow-y-auto">
|
||||
{state.loading ?
|
||||
{state.loading ? (
|
||||
<p>loading...</p>
|
||||
: state.partialTree.map((item) => {
|
||||
) : (
|
||||
state.partialTree.map((item) => {
|
||||
if (item.type === 'file') {
|
||||
return <File key={item.id} item={item} checkbox={checkbox} />
|
||||
}
|
||||
|
|
@ -152,7 +152,7 @@ export function RemoteSource({
|
|||
}
|
||||
return null
|
||||
})
|
||||
}
|
||||
)}
|
||||
</ul>
|
||||
|
||||
{state.selectedAmount > 0 && (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import React, { useEffect } from 'react'
|
||||
import { useScreenCapture } from '@uppy/react'
|
||||
import React, { useEffect } from 'react'
|
||||
import MediaCapture from './MediaCapture.tsx'
|
||||
|
||||
export interface ScreenCaptureProps {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import React, { useEffect } from 'react'
|
||||
import { useWebcam } from '@uppy/react'
|
||||
import React, { useEffect } from 'react'
|
||||
import MediaCapture from './MediaCapture.tsx'
|
||||
|
||||
export interface WebcamProps {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
|
||||
/* example how to use the data attributes to apply custom styles */
|
||||
/* button[data-uppy-element="upload-button"][data-state="uploading"] {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.js'
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src", "src/**/*.tsx"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
// @ts-expect-error untyped for some reason but fine
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import { compose, combineReducers, applyMiddleware } from 'redux'
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import logger from 'redux-logger'
|
||||
import Uppy from '@uppy/core'
|
||||
import ReduxStore from '@uppy/store-redux'
|
||||
import * as uppyReduxStore from '@uppy/store-redux'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import ReduxStore, * as uppyReduxStore from '@uppy/store-redux'
|
||||
import Tus from '@uppy/tus'
|
||||
import { applyMiddleware, combineReducers, compose } from 'redux'
|
||||
import logger from 'redux-logger'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
||||
function counter (state = 0, action) {
|
||||
function counter(state = 0, action) {
|
||||
switch (action.type) {
|
||||
case 'INCREMENT':
|
||||
return state + 1
|
||||
|
|
@ -28,31 +27,30 @@ const reducer = combineReducers({
|
|||
uppy: uppyReduxStore.reducer,
|
||||
})
|
||||
|
||||
let enhancer = applyMiddleware(
|
||||
uppyReduxStore.middleware(),
|
||||
logger,
|
||||
)
|
||||
let enhancer = applyMiddleware(uppyReduxStore.middleware(), logger)
|
||||
if (typeof __REDUX_DEVTOOLS_EXTENSION__ !== 'undefined') {
|
||||
// eslint-disable-next-line no-undef
|
||||
enhancer = compose(enhancer, __REDUX_DEVTOOLS_EXTENSION__())
|
||||
}
|
||||
|
||||
const store = configureStore({
|
||||
reducer,
|
||||
enhancers: [enhancer],
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
||||
serializableCheck: {
|
||||
ignoredActions: [uppyReduxStore.STATE_UPDATE],
|
||||
ignoreState: true,
|
||||
},
|
||||
}),
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware({
|
||||
serializableCheck: {
|
||||
ignoredActions: [uppyReduxStore.STATE_UPDATE],
|
||||
ignoreState: true,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
// Counter example from https://github.com/reactjs/redux/blob/master/examples/counter-vanilla/index.html
|
||||
const valueEl = document.querySelector('#value')
|
||||
|
||||
function getCounter () { return store.getState().counter }
|
||||
function render () {
|
||||
function getCounter() {
|
||||
return store.getState().counter
|
||||
}
|
||||
function render() {
|
||||
valueEl.innerHTML = getCounter().toString()
|
||||
}
|
||||
render()
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { useDropzone, useFileInput, ProviderIcon } from '@uppy/svelte'
|
||||
import { ProviderIcon, useDropzone, useFileInput } from '@uppy/svelte'
|
||||
|
||||
interface Props {
|
||||
openModal: (plugin: 'webcam' | 'dropbox' | 'screen-capture') => void
|
||||
}
|
||||
interface Props {
|
||||
openModal: (plugin: 'webcam' | 'dropbox' | 'screen-capture') => void
|
||||
}
|
||||
|
||||
const { openModal }: Props = $props()
|
||||
const { openModal }: Props = $props()
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({ noClick: true })
|
||||
const { getButtonProps, getInputProps: getFileInputProps } = useFileInput()
|
||||
const { getRootProps, getInputProps } = useDropzone({ noClick: true })
|
||||
const { getButtonProps, getInputProps: getFileInputProps } = useFileInput()
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
<script lang="ts">
|
||||
type ButtonProps = Record<string, unknown>
|
||||
type VideoProps = Record<string, unknown>
|
||||
type ButtonProps = Record<string, unknown>
|
||||
type VideoProps = Record<string, unknown>
|
||||
|
||||
interface Props {
|
||||
title: string
|
||||
close: () => void
|
||||
videoProps: VideoProps
|
||||
primaryActionButtonProps: ButtonProps
|
||||
primaryActionButtonLabel: string
|
||||
recordButtonProps: ButtonProps
|
||||
stopRecordingButtonProps: ButtonProps
|
||||
submitButtonProps: ButtonProps
|
||||
discardButtonProps: ButtonProps
|
||||
}
|
||||
interface Props {
|
||||
title: string
|
||||
close: () => void
|
||||
videoProps: VideoProps
|
||||
primaryActionButtonProps: ButtonProps
|
||||
primaryActionButtonLabel: string
|
||||
recordButtonProps: ButtonProps
|
||||
stopRecordingButtonProps: ButtonProps
|
||||
submitButtonProps: ButtonProps
|
||||
discardButtonProps: ButtonProps
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
close,
|
||||
videoProps,
|
||||
primaryActionButtonProps,
|
||||
primaryActionButtonLabel,
|
||||
recordButtonProps,
|
||||
stopRecordingButtonProps,
|
||||
submitButtonProps,
|
||||
discardButtonProps,
|
||||
}: Props = $props()
|
||||
const {
|
||||
title,
|
||||
close,
|
||||
videoProps,
|
||||
primaryActionButtonProps,
|
||||
primaryActionButtonLabel,
|
||||
recordButtonProps,
|
||||
stopRecordingButtonProps,
|
||||
submitButtonProps,
|
||||
discardButtonProps,
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
<div class="p-4 max-w-lg w-full">
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
<script lang="ts">
|
||||
import { useRemoteSource } from '@uppy/svelte'
|
||||
import type { AvailablePluginsKeys } from '@uppy/remote-sources'
|
||||
import type { PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { AvailablePluginsKeys } from '@uppy/remote-sources'
|
||||
import { useRemoteSource } from '@uppy/svelte'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
id: AvailablePluginsKeys
|
||||
}
|
||||
|
||||
const { close, id }: Props = $props()
|
||||
|
||||
// Use the value directly, destructuring looses reactivity
|
||||
const remoteSource = useRemoteSource(id)
|
||||
|
||||
const dtf = new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'short',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
|
||||
function setFolderCheckboxIndeterminate(
|
||||
node: HTMLInputElement,
|
||||
item: PartialTreeFolderNode,
|
||||
) {
|
||||
if (item.status === 'partial') {
|
||||
// Can only be set via JS
|
||||
node.indeterminate = true
|
||||
}
|
||||
interface Props {
|
||||
close: () => void
|
||||
id: AvailablePluginsKeys
|
||||
}
|
||||
|
||||
const { close, id }: Props = $props()
|
||||
|
||||
// Use the value directly, destructuring looses reactivity
|
||||
const remoteSource = useRemoteSource(id)
|
||||
|
||||
const dtf = new Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'short',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
|
||||
function setFolderCheckboxIndeterminate(
|
||||
node: HTMLInputElement,
|
||||
item: PartialTreeFolderNode,
|
||||
) {
|
||||
if (item.status === 'partial') {
|
||||
// Can only be set via JS
|
||||
node.indeterminate = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !remoteSource.state.authenticated}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<script lang="ts">
|
||||
import MediaCapture from './MediaCapture.svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { useScreenCapture } from '@uppy/svelte'
|
||||
import { useScreenCapture } from '@uppy/svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import MediaCapture from './MediaCapture.svelte'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
interface Props {
|
||||
close: () => void
|
||||
}
|
||||
|
||||
const { close }: Props = $props()
|
||||
const screenCaptureStore = useScreenCapture({ onSubmit: close })
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => screenCaptureStore.start())
|
||||
return () => {
|
||||
untrack(() => screenCaptureStore.stop())
|
||||
}
|
||||
|
||||
const { close }: Props = $props()
|
||||
const screenCaptureStore = useScreenCapture({ onSubmit: close })
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => screenCaptureStore.start())
|
||||
return () => {
|
||||
untrack(() => screenCaptureStore.stop())
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<MediaCapture
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<script lang="ts">
|
||||
import MediaCapture from './MediaCapture.svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { useWebcam } from '@uppy/svelte'
|
||||
import { useWebcam } from '@uppy/svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import MediaCapture from './MediaCapture.svelte'
|
||||
|
||||
interface Props {
|
||||
close: () => void
|
||||
interface Props {
|
||||
close: () => void
|
||||
}
|
||||
|
||||
const { close }: Props = $props()
|
||||
const webcamStore = useWebcam({ onSubmit: close })
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => webcamStore.start())
|
||||
return () => {
|
||||
untrack(() => webcamStore.stop())
|
||||
}
|
||||
|
||||
const { close }: Props = $props()
|
||||
const webcamStore = useWebcam({ onSubmit: close })
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => webcamStore.start())
|
||||
return () => {
|
||||
untrack(() => webcamStore.stop())
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<MediaCapture
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
|
||||
let { children } = $props();
|
||||
import '../app.css'
|
||||
|
||||
const { children } = $props()
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
<script lang="ts">
|
||||
import Uppy from '@uppy/core'
|
||||
import Tus from '@uppy/tus'
|
||||
import UppyWebcam from '@uppy/webcam'
|
||||
import UppyRemoteSources from '@uppy/remote-sources'
|
||||
import UppyScreenCapture from '@uppy/screen-capture'
|
||||
import {
|
||||
UppyContextProvider,
|
||||
Dropzone,
|
||||
FilesList,
|
||||
FilesGrid,
|
||||
UploadButton,
|
||||
} from '@uppy/svelte'
|
||||
import '@uppy/svelte/dist/styles.css'
|
||||
import Uppy from '@uppy/core'
|
||||
import UppyRemoteSources from '@uppy/remote-sources'
|
||||
import UppyScreenCapture from '@uppy/screen-capture'
|
||||
import {
|
||||
Dropzone,
|
||||
FilesGrid,
|
||||
FilesList,
|
||||
UploadButton,
|
||||
UppyContextProvider,
|
||||
} from '@uppy/svelte'
|
||||
import Tus from '@uppy/tus'
|
||||
import UppyWebcam from '@uppy/webcam'
|
||||
import '@uppy/svelte/dist/styles.css'
|
||||
|
||||
import CustomDropzone from '../components/CustomDropzone.svelte'
|
||||
import Webcam from '../components/Webcam.svelte'
|
||||
import RemoteSource from '../components/RemoteSource.svelte'
|
||||
import ScreenCapture from '../components/ScreenCapture.svelte'
|
||||
import CustomDropzone from '../components/CustomDropzone.svelte'
|
||||
import RemoteSource from '../components/RemoteSource.svelte'
|
||||
import ScreenCapture from '../components/ScreenCapture.svelte'
|
||||
import Webcam from '../components/Webcam.svelte'
|
||||
|
||||
const uppy = new Uppy()
|
||||
.use(Tus, {
|
||||
endpoint: 'https://tusd.tusdemo.net/files/',
|
||||
})
|
||||
.use(UppyWebcam)
|
||||
.use(UppyScreenCapture)
|
||||
.use(UppyRemoteSources, { companionUrl: 'http://localhost:3020' })
|
||||
const uppy = new Uppy()
|
||||
.use(Tus, {
|
||||
endpoint: 'https://tusd.tusdemo.net/files/',
|
||||
})
|
||||
.use(UppyWebcam)
|
||||
.use(UppyScreenCapture)
|
||||
.use(UppyRemoteSources, { companionUrl: 'http://localhost:3020' })
|
||||
|
||||
let dialogRef: HTMLDialogElement
|
||||
let modalPlugin = $state<'webcam' | 'dropbox' | 'screen-capture' | null>(null)
|
||||
let dialogRef: HTMLDialogElement
|
||||
let modalPlugin = $state<'webcam' | 'dropbox' | 'screen-capture' | null>(null)
|
||||
|
||||
function openModal(plugin: 'webcam' | 'dropbox' | 'screen-capture') {
|
||||
modalPlugin = plugin
|
||||
dialogRef?.showModal()
|
||||
}
|
||||
function openModal(plugin: 'webcam' | 'dropbox' | 'screen-capture') {
|
||||
modalPlugin = plugin
|
||||
dialogRef?.showModal()
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
modalPlugin = null
|
||||
dialogRef?.close()
|
||||
}
|
||||
function closeModal() {
|
||||
modalPlugin = null
|
||||
dialogRef?.close()
|
||||
}
|
||||
</script>
|
||||
|
||||
<UppyContextProvider {uppy}>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import adapter from '@sveltejs/adapter-auto'
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter(),
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
export default config
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@
|
|||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"module": "preserve",
|
||||
"moduleResolution": "bundler",
|
||||
},
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { sveltekit } from '@sveltejs/kit/vite'
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { marked } from 'marked'
|
||||
import Uppy from '@uppy/core'
|
||||
import DropTarget from '@uppy/drop-target'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Transloadit from '@uppy/transloadit'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import DropTarget from '@uppy/drop-target'
|
||||
import ImageEditor from '@uppy/image-editor'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import Transloadit from '@uppy/transloadit'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import { marked } from 'marked'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
@ -14,13 +14,12 @@ import '@uppy/image-editor/dist/style.css'
|
|||
const TRANSLOADIT_EXAMPLE_KEY = '35c1aed03f5011e982b6afe82599b6a0'
|
||||
const TRANSLOADIT_EXAMPLE_TEMPLATE = '0b2ee2bc25dc43619700c2ce0a75164a'
|
||||
|
||||
function matchFilesAndThumbs (results) {
|
||||
function matchFilesAndThumbs(results) {
|
||||
const filesById = {}
|
||||
const thumbsById = {}
|
||||
|
||||
for (const [stepName, result] of Object.entries(results)) {
|
||||
// eslint-disable-next-line no-shadow
|
||||
result.forEach(result => {
|
||||
result.forEach((result) => {
|
||||
if (stepName === 'thumbnails') {
|
||||
thumbsById[result.original_id] = result
|
||||
} else {
|
||||
|
|
@ -39,7 +38,7 @@ function matchFilesAndThumbs (results) {
|
|||
* A textarea for markdown text, with support for file attachments.
|
||||
*/
|
||||
class MarkdownTextarea {
|
||||
constructor (element) {
|
||||
constructor(element) {
|
||||
this.element = element
|
||||
this.controls = document.createElement('div')
|
||||
this.controls.classList.add('mdtxt-controls')
|
||||
|
|
@ -52,7 +51,7 @@ class MarkdownTextarea {
|
|||
)
|
||||
}
|
||||
|
||||
install () {
|
||||
install() {
|
||||
const { element } = this
|
||||
const wrapper = document.createElement('div')
|
||||
wrapper.classList.add('mdtxt')
|
||||
|
|
@ -82,11 +81,9 @@ class MarkdownTextarea {
|
|||
this.uppy.on('complete', (result) => {
|
||||
const { successful, failed, transloadit } = result
|
||||
if (successful.length !== 0) {
|
||||
this.insertAttachments(
|
||||
matchFilesAndThumbs(transloadit[0].results),
|
||||
)
|
||||
this.insertAttachments(matchFilesAndThumbs(transloadit[0].results))
|
||||
} else {
|
||||
failed.forEach(error => {
|
||||
failed.forEach((error) => {
|
||||
console.error(error)
|
||||
this.reportUploadError(error)
|
||||
})
|
||||
|
|
@ -95,14 +92,14 @@ class MarkdownTextarea {
|
|||
})
|
||||
}
|
||||
|
||||
reportUploadError (err) {
|
||||
reportUploadError(err) {
|
||||
this.uploadLine.classList.add('error')
|
||||
const message = document.createElement('span')
|
||||
message.appendChild(document.createTextNode(err.message))
|
||||
this.uploadLine.insertChild(message, this.uploadLine.firstChild)
|
||||
}
|
||||
|
||||
unreportUploadError () {
|
||||
unreportUploadError() {
|
||||
this.uploadLine.classList.remove('error')
|
||||
const message = this.uploadLine.querySelector('message')
|
||||
if (message) {
|
||||
|
|
@ -110,13 +107,16 @@ class MarkdownTextarea {
|
|||
}
|
||||
}
|
||||
|
||||
insertAttachments (attachments) {
|
||||
insertAttachments(attachments) {
|
||||
attachments.forEach((attachment) => {
|
||||
const { file, thumb } = attachment
|
||||
const link = `\n[LABEL](${file.ssl_url})\n`
|
||||
const labelText = `View File ${file.basename}`
|
||||
if (thumb) {
|
||||
this.element.value += link.replace('LABEL', ``)
|
||||
this.element.value += link.replace(
|
||||
'LABEL',
|
||||
``,
|
||||
)
|
||||
} else {
|
||||
this.element.value += link.replace('LABEL', labelText)
|
||||
}
|
||||
|
|
@ -124,7 +124,7 @@ class MarkdownTextarea {
|
|||
}
|
||||
|
||||
uploadFiles = (files) => {
|
||||
const filesForUppy = files.map(file => {
|
||||
const filesForUppy = files.map((file) => {
|
||||
return {
|
||||
data: file,
|
||||
type: file.type,
|
||||
|
|
@ -139,7 +139,7 @@ class MarkdownTextarea {
|
|||
const textarea = new MarkdownTextarea(document.querySelector('#new textarea'))
|
||||
textarea.install()
|
||||
|
||||
function renderSnippet (title, text) {
|
||||
function renderSnippet(title, text) {
|
||||
const template = document.querySelector('#snippet')
|
||||
const newSnippet = document.importNode(template.content, true)
|
||||
const titleEl = newSnippet.querySelector('.snippet-title')
|
||||
|
|
@ -152,13 +152,13 @@ function renderSnippet (title, text) {
|
|||
list.insertBefore(newSnippet, list.firstChild)
|
||||
}
|
||||
|
||||
function saveSnippet (title, text) {
|
||||
function saveSnippet(title, text) {
|
||||
const id = parseInt(localStorage.numSnippets || 0, 10)
|
||||
localStorage[`snippet_${id}`] = JSON.stringify({ title, text })
|
||||
localStorage.numSnippets = id + 1
|
||||
}
|
||||
|
||||
function loadSnippets () {
|
||||
function loadSnippets() {
|
||||
for (let id = 0; localStorage[`snippet_${id}`] != null; id += 1) {
|
||||
const { title, text } = JSON.parse(localStorage[`snippet_${id}`])
|
||||
renderSnippet(title, text)
|
||||
|
|
@ -168,16 +168,13 @@ function loadSnippets () {
|
|||
document.querySelector('#new').addEventListener('submit', (event) => {
|
||||
event.preventDefault()
|
||||
|
||||
const title = event.target.elements['title'].value
|
||||
|| 'Unnamed Snippet'
|
||||
const title = event.target.elements.title.value || 'Unnamed Snippet'
|
||||
const text = textarea.element.value
|
||||
|
||||
saveSnippet(title, text)
|
||||
renderSnippet(title, text)
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.target.querySelector('input').value = ''
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
event.target.querySelector('textarea').value = ''
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import Transloadit, { COMPANION_URL } from '@uppy/transloadit'
|
||||
import Uppy from '@uppy/core'
|
||||
import Form from '@uppy/form'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import Form from '@uppy/form'
|
||||
import ImageEditor from '@uppy/image-editor'
|
||||
import Webcam from '@uppy/webcam'
|
||||
import ProgressBar from '@uppy/progress-bar'
|
||||
import RemoteSources from '@uppy/remote-sources'
|
||||
import Transloadit, { COMPANION_URL } from '@uppy/transloadit'
|
||||
import Webcam from '@uppy/webcam'
|
||||
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
|
|
@ -59,13 +59,11 @@ const formUppy = new Uppy({
|
|||
})
|
||||
|
||||
formUppy.on('error', (err) => {
|
||||
document.querySelector('#test-form .error')
|
||||
.textContent = err.message
|
||||
document.querySelector('#test-form .error').textContent = err.message
|
||||
})
|
||||
|
||||
formUppy.on('upload-error', (file, err) => {
|
||||
document.querySelector('#test-form .error')
|
||||
.textContent = err.message
|
||||
document.querySelector('#test-form .error').textContent = err.message
|
||||
})
|
||||
|
||||
formUppy.on('complete', ({ transloadit }) => {
|
||||
|
|
@ -163,15 +161,13 @@ const dashboardModal = new Uppy({
|
|||
|
||||
dashboardModal.on('complete', ({ transloadit, successful, failed }) => {
|
||||
if (failed?.length !== 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('it failed', failed)
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('success', { transloadit, successful })
|
||||
}
|
||||
})
|
||||
|
||||
function openModal () {
|
||||
function openModal() {
|
||||
dashboardModal.getPlugin('Dashboard').openModal()
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +204,7 @@ window.doUpload = (event) => {
|
|||
errorEl.classList.add('hidden')
|
||||
resultEl.textContent = JSON.stringify(transloadit[0].results, null, 2)
|
||||
|
||||
const resizedUrl = transloadit[0].results['resize'][0]['ssl_url']
|
||||
const resizedUrl = transloadit[0].results.resize[0].ssl_url
|
||||
const img = document.createElement('img')
|
||||
img.src = resizedUrl
|
||||
document.getElementById('upload-result-image').appendChild(img)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
|
||||
import http from 'node:http'
|
||||
import qs from 'node:querystring'
|
||||
import he from 'he'
|
||||
|
||||
const e = he.encode
|
||||
|
||||
function Header () {
|
||||
function Header() {
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
|
@ -28,7 +27,7 @@ function Header () {
|
|||
`
|
||||
}
|
||||
|
||||
function Footer () {
|
||||
function Footer() {
|
||||
return `
|
||||
</main>
|
||||
</body>
|
||||
|
|
@ -36,31 +35,28 @@ function Footer () {
|
|||
`
|
||||
}
|
||||
|
||||
function FormFields (fields) {
|
||||
function Field ([name, value]) {
|
||||
function FormFields(fields) {
|
||||
function Field([name, value]) {
|
||||
if (name === 'transloadit') return ''
|
||||
let isValueJSON = false
|
||||
if (value.startsWith('{') || value.startsWith('[')) {
|
||||
try {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
value = JSON.stringify(
|
||||
JSON.parse(value),
|
||||
null,
|
||||
2,
|
||||
)
|
||||
value = JSON.stringify(JSON.parse(value), null, 2)
|
||||
isValueJSON = true
|
||||
} catch {
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
|
||||
const prettyValue = isValueJSON ? `
|
||||
const prettyValue = isValueJSON
|
||||
? `
|
||||
<details open>
|
||||
<code>
|
||||
<pre style="max-width: 100%; max-height: 400px; white-space: pre-wrap; overflow: auto;">${e(value)}</pre>
|
||||
</code>
|
||||
</details>
|
||||
` : e(value)
|
||||
`
|
||||
: e(value)
|
||||
|
||||
return `
|
||||
<dt>${e(name)}</dt>
|
||||
|
|
@ -78,8 +74,8 @@ function FormFields (fields) {
|
|||
`
|
||||
}
|
||||
|
||||
function UploadsList (uploads) {
|
||||
function Upload (upload) {
|
||||
function UploadsList(uploads) {
|
||||
function Upload(upload) {
|
||||
return `<li>${e(upload.name)}</li>`
|
||||
}
|
||||
|
||||
|
|
@ -90,12 +86,12 @@ function UploadsList (uploads) {
|
|||
`
|
||||
}
|
||||
|
||||
function ResultsList (results) {
|
||||
function Result (result) {
|
||||
function ResultsList(results) {
|
||||
function Result(result) {
|
||||
return `<li>${e(result.name)} <a href="${result.ssl_url}" target="_blank">View</a></li>`
|
||||
}
|
||||
|
||||
function ResultsSection (stepName) {
|
||||
function ResultsSection(stepName) {
|
||||
return `
|
||||
<h2>${e(stepName)}</h2>
|
||||
<ul>
|
||||
|
|
@ -104,12 +100,10 @@ function ResultsList (results) {
|
|||
`
|
||||
}
|
||||
|
||||
return Object.keys(results)
|
||||
.map(ResultsSection)
|
||||
.join('\n')
|
||||
return Object.keys(results).map(ResultsSection).join('\n')
|
||||
}
|
||||
|
||||
function AssemblyResult (assembly) {
|
||||
function AssemblyResult(assembly) {
|
||||
return `
|
||||
<h1>${e(assembly.assembly_id)} (${e(assembly.ok)})</h1>
|
||||
${UploadsList(assembly.uploads)}
|
||||
|
|
@ -117,14 +111,14 @@ function AssemblyResult (assembly) {
|
|||
`
|
||||
}
|
||||
|
||||
function onrequest (req, res) {
|
||||
function onrequest(req, res) {
|
||||
if (req.url !== '/test') {
|
||||
res.writeHead(404, { 'content-type': 'text/html' })
|
||||
res.end('404')
|
||||
return
|
||||
}
|
||||
|
||||
function onbody (body) {
|
||||
function onbody(body) {
|
||||
const fields = qs.parse(body)
|
||||
const result = JSON.parse(fields.uppyResult)
|
||||
const assemblies = result[0].transloadit
|
||||
|
|
@ -140,7 +134,9 @@ function onrequest (req, res) {
|
|||
|
||||
{
|
||||
let body = ''
|
||||
req.on('data', (chunk) => { body += chunk })
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk
|
||||
})
|
||||
req.on('end', () => {
|
||||
onbody(body)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ const companion = require('@uppy/companion')
|
|||
const app = express()
|
||||
|
||||
app.use(bodyParser.json())
|
||||
app.use(session({
|
||||
secret: 'some-secret',
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
}))
|
||||
app.use(
|
||||
session({
|
||||
secret: 'some-secret',
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
}),
|
||||
)
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
|
||||
|
|
|
|||
|
|
@ -42,23 +42,23 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import Uppy from '@uppy/core'
|
||||
import Tus from '@uppy/tus'
|
||||
import {
|
||||
UppyContextProvider,
|
||||
Dropzone,
|
||||
FilesList,
|
||||
FilesGrid,
|
||||
UploadButton,
|
||||
} from '@uppy/vue'
|
||||
import CustomDropzone from './Dropzone.vue'
|
||||
import Webcam from './Webcam.vue'
|
||||
import RemoteSource from './RemoteSource.vue'
|
||||
import ScreenCapture from './ScreenCapture.vue'
|
||||
import UppyWebcam from '@uppy/webcam'
|
||||
import UppyRemoteSources from '@uppy/remote-sources'
|
||||
import UppyScreenCapture from '@uppy/screen-capture'
|
||||
import Tus from '@uppy/tus'
|
||||
import {
|
||||
Dropzone,
|
||||
FilesGrid,
|
||||
FilesList,
|
||||
UploadButton,
|
||||
UppyContextProvider,
|
||||
} from '@uppy/vue'
|
||||
import UppyWebcam from '@uppy/webcam'
|
||||
import { computed, ref } from 'vue'
|
||||
import CustomDropzone from './Dropzone.vue'
|
||||
import RemoteSource from './RemoteSource.vue'
|
||||
import ScreenCapture from './ScreenCapture.vue'
|
||||
import Webcam from './Webcam.vue'
|
||||
|
||||
const dialogRef = ref<HTMLDialogElement | null>(null)
|
||||
const modalPlugin = ref<'webcam' | 'dropbox' | 'screen-capture' | null>(null)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDropzone, useFileInput, ProviderIcon } from '@uppy/vue'
|
||||
import { ProviderIcon, useDropzone, useFileInput } from '@uppy/vue'
|
||||
|
||||
const props = defineProps<{
|
||||
openModal: (plugin: 'webcam' | 'dropbox' | 'screen-capture') => void
|
||||
|
|
|
|||
|
|
@ -132,9 +132,9 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRemoteSource } from '@uppy/vue'
|
||||
import type { PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { AvailablePluginsKeys } from '@uppy/remote-sources'
|
||||
import { PartialTreeFolderNode } from '@uppy/core'
|
||||
import { useRemoteSource } from '@uppy/vue'
|
||||
|
||||
const props = defineProps<{
|
||||
close: () => void
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { fileURLToPath } from 'node:url'
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
const ROOT = new URL('../../', import.meta.url)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const upload = multer({
|
|||
storage: multer.memoryStorage(),
|
||||
})
|
||||
|
||||
function uploadRoute (req, res) {
|
||||
function uploadRoute(req, res) {
|
||||
res.json({
|
||||
files: req.files.map((file) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
|
|
|||
62
package.json
62
package.json
|
|
@ -24,8 +24,10 @@
|
|||
"build:css": "yarn node ./bin/build-css.js && yarn workspace @uppy/components build:tailwind && yarn node ./bin/copy-tailwind-css.mjs",
|
||||
"build:js": "npm-run-all build:js:typeless build:locale-pack build:angular build:bundle",
|
||||
"build:js:typeless": "npm-run-all build:companion build:svelte",
|
||||
"build:locale-pack": "yarn workspace @uppy-dev/locale-pack build && eslint packages/@uppy/locales/src/en_US.ts --fix && yarn workspace @uppy-dev/locale-pack test unused",
|
||||
"build:locale-pack": "yarn workspace @uppy-dev/locale-pack build && yarn workspace @uppy-dev/locale-pack test unused",
|
||||
"build:svelte": "yarn workspace @uppy/svelte build",
|
||||
"check": "yarn exec biome check --write",
|
||||
"check:ci": "yarn exec biome ci",
|
||||
"contributors:save": "yarn node ./bin/update-contributors.mjs",
|
||||
"dev": "yarn workspace @uppy-dev/dev dev",
|
||||
"dev:with-companion": "npm-run-all --parallel start:companion dev",
|
||||
|
|
@ -36,15 +38,6 @@
|
|||
"e2e:generate": "yarn workspace e2e generate-test",
|
||||
"e2e:headless": "yarn workspace e2e cypress:headless",
|
||||
"e2e:skip-build": "npm-run-all --parallel watch:js:lib e2e:client start:companion:with-loadbalancer e2e:cypress",
|
||||
"format": "prettier -w .",
|
||||
"format:check": "prettier -c .",
|
||||
"format:check-diff": "yarn format:check || (yarn format:show-diff && false)",
|
||||
"format:show-diff": "git diff --quiet || (echo 'Unable to show a diff because there are unstaged changes'; false) && (prettier . -w --loglevel silent && git --no-pager diff; git restore .)",
|
||||
"lint": "eslint . --cache",
|
||||
"lint:css": "stylelint ./packages/**/*.scss",
|
||||
"lint:css:fix": "stylelint ./packages/**/*.scss --fix",
|
||||
"lint:fix": "yarn lint --fix",
|
||||
"lint:staged": "lint-staged",
|
||||
"release": "PACKAGES=$(yarn workspaces list --json) yarn workspace @uppy-dev/release interactive",
|
||||
"size": "echo 'JS Bundle mingz:' && cat ./packages/uppy/dist/uppy.min.js | gzip | wc -c && echo 'CSS Bundle mingz:' && cat ./packages/uppy/dist/uppy.min.css | gzip | wc -c",
|
||||
"start:companion": "bash bin/companion.sh",
|
||||
|
|
@ -64,81 +57,36 @@
|
|||
"watch:js:bundle": "onchange 'packages/{@uppy/,}*/src/**/*.{js,ts,jsx,tsx,svelte,scss,css}' --initial --verbose -- yarn run build:bundle",
|
||||
"watch:js:lib": "onchange 'packages/{@uppy/,}*/src/**/*.{js,ts,jsx,tsx,svelte,scss,css}' --initial --verbose -- yarn run build"
|
||||
},
|
||||
"pre-commit": "lint:staged",
|
||||
"lint-staged": {
|
||||
"*.{js,mjs,cjs,jsx}": "eslint --fix",
|
||||
"*.{ts,mts,cts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier -w",
|
||||
"eslint"
|
||||
],
|
||||
"*.{css,html,json,scss,vue,yaml,yml}": "prettier -w",
|
||||
"*.md": [
|
||||
"eslint --fix",
|
||||
"prettier -w",
|
||||
"eslint"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/eslint@^7.2.13": "^8.2.0",
|
||||
"@types/react": "^18",
|
||||
"@types/webpack-dev-server": "^4",
|
||||
"@vitest/utils": "patch:@vitest/utils@npm%3A1.2.1#./.yarn/patches/@vitest-utils-npm-1.2.1-3028846845.patch",
|
||||
"p-queue": "patch:p-queue@npm%3A8.0.1#~/.yarn/patches/p-queue-npm-8.0.1-fe1ddcd827.patch",
|
||||
"pre-commit": "patch:pre-commit@npm:1.2.2#.yarn/patches/pre-commit-npm-1.2.2-f30af83877.patch",
|
||||
"resize-observer-polyfill": "patch:resize-observer-polyfill@npm%3A1.5.1#./.yarn/patches/resize-observer-polyfill-npm-1.5.1-603120e8a0.patch",
|
||||
"start-server-and-test": "patch:start-server-and-test@npm:1.14.0#.yarn/patches/start-server-and-test-npm-1.14.0-841aa34fdf.patch",
|
||||
"stylelint-order": "^6.0.0",
|
||||
"stylelint@^9.10.1": "^16.0.0",
|
||||
"uuid@^8.3.2": "patch:uuid@npm:8.3.2#.yarn/patches/uuid-npm-8.3.2-eca0baba53.patch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-plugin": "^7.27.1",
|
||||
"@biomejs/biome": "2.0.5",
|
||||
"@types/jasmine": "file:./private/@types/jasmine",
|
||||
"@types/jasminewd2": "file:./private/@types/jasmine",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^8.35.0",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"chalk": "^5.0.0",
|
||||
"cssnano": "^7.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"esbuild": "^0.25.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-transloadit": "^2.0.0",
|
||||
"eslint-plugin-compat": "^4.0.0",
|
||||
"eslint-plugin-cypress": "^3.0.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^28.0.0",
|
||||
"eslint-plugin-jsdoc": "^48.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-markdown": "^5.0.0",
|
||||
"eslint-plugin-no-only-tests": "^3.1.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prefer-import": "^0.0.1",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-unicorn": "^53.0.0",
|
||||
"execa": "^9.5.1",
|
||||
"github-contributors-list": "^1.2.4",
|
||||
"glob": "^8.0.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"lint-staged": "^15.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"onchange": "^7.1.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-dir-pseudo-class": "^6.0.0",
|
||||
"postcss-logical": "^5.0.0",
|
||||
"pre-commit": "^1.2.2",
|
||||
"prettier": "^3.0.3",
|
||||
"resolve": "^1.17.0",
|
||||
"sass": "^1.29.0",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"stylelint": "^15.0.0",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-standard": "^36.0.0",
|
||||
"stylelint-config-standard-scss": "^13.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^1.6.1",
|
||||
"vue-template-compiler": "workspace:*"
|
||||
|
|
@ -148,4 +96,4 @@
|
|||
"node": "^16.15.0 || >=18.0.0",
|
||||
"yarn": "3.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import * as Dashboard from '@uppy/dashboard';
|
||||
import { Uppy } from '@uppy/core';
|
||||
import { Body, Meta } from '@uppy/utils/lib/UppyFile';
|
||||
import { ChangeDetectionStrategy, Component } from "@angular/core";
|
||||
import { Uppy } from "@uppy/core";
|
||||
import type * as Dashboard from "@uppy/dashboard";
|
||||
import type { Body, Meta } from "@uppy/utils/lib/UppyFile";
|
||||
|
||||
@Component({
|
||||
selector: 'uppy-dashboard-demo',
|
||||
template: `<uppy-dashboard-modal
|
||||
selector: "uppy-dashboard-demo",
|
||||
template: `<uppy-dashboard-modal
|
||||
[uppy]="uppy"
|
||||
[props]="props"
|
||||
></uppy-dashboard-modal>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DashboardModalDemoComponent<M extends Meta, B extends Body> {
|
||||
uppy: Uppy<M, B> = new Uppy({ debug: true, autoProceed: true });
|
||||
props?: Dashboard.DashboardOptions<M, B>;
|
||||
uppy: Uppy<M, B> = new Uppy({ debug: true, autoProceed: true });
|
||||
props?: Dashboard.DashboardOptions<M, B>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { async, type ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
|
||||
import { DashboardModalComponent } from './dashboard-modal.component';
|
||||
import { DashboardModalComponent } from "./dashboard-modal.component";
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardModalComponent;
|
||||
let fixture: ComponentFixture<DashboardModalComponent>;
|
||||
describe("DashboardComponent", () => {
|
||||
let component: DashboardModalComponent;
|
||||
let fixture: ComponentFixture<DashboardModalComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardModalComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DashboardModalComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
it("should create", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue