diff --git a/.babelrc b/.babelrc deleted file mode 100644 index de74bdcea..000000000 --- a/.babelrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "presets": [["@babel/preset-env", { "loose": true }]], - "env": { - "test": { - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": true - } - } - ] - ] - } - } -} diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index e6fc5a0c7..000000000 --- a/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -types/ -public/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index e1e776020..72942a457 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,15 +10,17 @@ ], "env": { "es6": true, - "browser": true, - "mocha": true, - "cypress/globals": true + "node": true, + "browser": true }, "parserOptions": { - "project": "./tsconfig.json", - "ecmaVersion": 2020 + "sourceType": "module", + "project": true }, "rules": { + "no-param-reassign": ["error", { "props": false }], + "@typescript-eslint/explicit-function-return-type": "error", + "import/no-named-as-default": "off", "import/prefer-default-export": "off", "import/no-extraneous-dependencies": [ "error", @@ -62,16 +64,27 @@ } ], "lines-between-class-members": "off", + "@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-namespace": "off", - "react/jsx-filename-extension": [0] + "react/jsx-filename-extension": [0], + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "mjs": "never", + "jsx": "never", + "ts": "never", + "tsx": "never" + } + ] }, "overrides": [ { - "files": ["*.test.ts"], - "env": { - "mocha": true - }, + "files": ["*.test.ts", "*.spec.ts"], "rules": { + "no-await-in-loop": "off", + "@typescript-eslint/explicit-function-return-type": "off", "no-restricted-syntax": "off", "compat/compat": "off", "no-new": "off", @@ -88,16 +101,6 @@ } ] } - }, - { - "files": ["cypress/**"], - "plugins": ["cypress"], - "rules": { - "no-unused-vars": "warn" - }, - "env": { - "cypress/globals": true - } } ], "settings": { @@ -119,5 +122,6 @@ "extensions": [".js", ".ts"] } } - } + }, + "ignorePatterns": ["node_modules/*", "public/*"] } diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..629ebd2d8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,15 @@ +# byte shaving (hoist semi-commonly variables, remove some low level low-usage functions) +157a47a44a01e3ce4b54ad211b1756ff59985bef +5bee41d7ff08e05442b232e3e552dcb6c703568d +e9382df0ae63edfc7540f82f74cf969342c759c0 + +# prettier config change +00433d200d8cccc8b544fbc8f05d5e96bf8ccff7 + +# misc linting cleanup +00009d2effa8b41a6ce27ef8b06a35a04215aea6 +62b786d1f13d0934137a62909d3a37db0a3e927e +5ad61841143508c9f91f0edd57f81f8b11066e0a +84a61cad1ddab1e851c98efa619a2cd35af434c1 +33f573247e8badc9ee10defe326f13985342e09b +b0199538a82d49de429f35546e412d14fc8bfeb9 \ No newline at end of file diff --git a/.github/actions-scripts/__snapshots__/chrome-win32.png b/.github/actions-scripts/__snapshots__/chrome-win32.png deleted file mode 100644 index d85cc5e75..000000000 Binary files a/.github/actions-scripts/__snapshots__/chrome-win32.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/edge-win32.png b/.github/actions-scripts/__snapshots__/edge-win32.png deleted file mode 100644 index 79140fe18..000000000 Binary files a/.github/actions-scripts/__snapshots__/edge-win32.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/firefox-darwin.png b/.github/actions-scripts/__snapshots__/firefox-darwin.png deleted file mode 100755 index ca2b341f0..000000000 Binary files a/.github/actions-scripts/__snapshots__/firefox-darwin.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/firefox-win32.png b/.github/actions-scripts/__snapshots__/firefox-win32.png deleted file mode 100644 index e3fc4e997..000000000 Binary files a/.github/actions-scripts/__snapshots__/firefox-win32.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/ie-win32.png b/.github/actions-scripts/__snapshots__/ie-win32.png deleted file mode 100644 index 7b74d9878..000000000 Binary files a/.github/actions-scripts/__snapshots__/ie-win32.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/puppeteer-darwin.png b/.github/actions-scripts/__snapshots__/puppeteer-darwin.png deleted file mode 100755 index 272f354d1..000000000 Binary files a/.github/actions-scripts/__snapshots__/puppeteer-darwin.png and /dev/null differ diff --git a/.github/actions-scripts/__snapshots__/safari-darwin.png b/.github/actions-scripts/__snapshots__/safari-darwin.png deleted file mode 100644 index 722b8d485..000000000 Binary files a/.github/actions-scripts/__snapshots__/safari-darwin.png and /dev/null differ diff --git a/.github/actions-scripts/polyfills-sync.js b/.github/actions-scripts/polyfills-sync.cjs similarity index 100% rename from .github/actions-scripts/polyfills-sync.js rename to .github/actions-scripts/polyfills-sync.cjs diff --git a/.github/actions-scripts/puppeteer.js b/.github/actions-scripts/puppeteer.js deleted file mode 100644 index d18be7e6a..000000000 --- a/.github/actions-scripts/puppeteer.js +++ /dev/null @@ -1,92 +0,0 @@ -const { readFileSync, writeFileSync, mkdirSync } = require('fs'); -const path = require('path'); -const { once } = require('events'); - -const puppeteer = require('puppeteer'); -const pixelmatch = require('pixelmatch'); -const { PNG } = require('pngjs'); - -const server = require('../../server'); - -async function test() { - const browser = await puppeteer.launch(); - const page = await browser.newPage(); - const artifactsPath = 'screenshot'; - const snapshotName = `puppeteer-${process.platform}.png`; - let error; - let pixelDifference; - let diff; - - if (!server.listening) await once(server, 'listening'); - - try { - page.on('console', msg => { - if (msg.type() === 'error') throw new Error(msg.text()); - }); - page.on('pageerror', err => { - throw err; - }); - - await page.goto(`http://127.0.0.1:${server.address().port}`, { - waitUntil: 'networkidle2', - }); - await page.setViewport({ width: 640, height: 1000 }); - await page.waitForTimeout(500); // Wait for resize to complete - await page.click('label[for="choices-single-custom-templates"]'); - await page.keyboard.press('ArrowDown'); - await page.keyboard.press('ArrowDown'); - - mkdirSync(artifactsPath, { recursive: true }); - const imageBuffer = await page.screenshot({ - path: path.join(artifactsPath, snapshotName), - fullPage: true, - }); - - // compare with snapshot - const screenshot = PNG.sync.read(imageBuffer); - const snapshot = PNG.sync.read( - readFileSync(path.resolve(__dirname, `./__snapshots__/${snapshotName}`)), - ); - const { width, height } = screenshot; - diff = new PNG({ width, height }); - pixelDifference = pixelmatch( - screenshot.data, - snapshot.data, - diff.data, - width, - height, - { - threshold: 0.6, - }, - ); - } catch (err) { - console.error(err); - error = err; - } finally { - if (diff) { - writeFileSync(path.join(artifactsPath, 'diff-' + snapshotName), PNG.sync.write(diff)); - } - await Promise.all([ - browser.close(), - new Promise(resolve => server.close(resolve)), - ]); - } - - if (pixelDifference > 200) { - console.error( - `Snapshot is different from screenshot by ${pixelDifference} pixels`, - ); - process.exit(1); - } - if (error) process.exit(1); -} - -process.on('unhandledRejection', err => { - console.error(err); - process.exit(1); -}); -process.once('uncaughtException', err => { - console.error(err); - process.exit(1); -}); -setImmediate(test); diff --git a/.github/actions-scripts/selenium.js b/.github/actions-scripts/selenium.js deleted file mode 100644 index 1b69bac42..000000000 --- a/.github/actions-scripts/selenium.js +++ /dev/null @@ -1,155 +0,0 @@ -const path = require('path'); -const { readFileSync, writeFileSync, mkdirSync } = require('fs'); -const { once } = require('events'); - -const pixelmatch = require('pixelmatch'); -const { PNG } = require('pngjs'); -const { - Builder, - By, - Key, - until, - Capabilities, - logging, -} = require('selenium-webdriver'); - -const server = require('../../server'); - -async function test() { - let pixelDifference; - let error; - - let capabilities; - switch (process.env.BROWSER) { - case 'ie': - capabilities = Capabilities.ie(); - capabilities.set('ignoreProtectedModeSettings', true); - capabilities.set('ignoreZoomSetting', true); - capabilities.set('ie.options', { - enableFullPageScreenshot: true, - ensureCleanSession: true, - }); - break; - - case 'edge': - capabilities = Capabilities.edge(); - break; - - case 'safari': - capabilities = Capabilities.safari(); - capabilities.set('safari.options', { technologyPreview: false }); - break; - - case 'firefox': { - capabilities = Capabilities.firefox().setLoggingPrefs({ browser: 'ALL' }); - break; - } - case 'chrome': { - capabilities = Capabilities.chrome().setLoggingPrefs({ browser: 'ALL' }); - capabilities.set('chromeOptions', { - args: ['--headless', '--no-sandbox', '--disable-gpu'], - }); - break; - } - } - - let driver = await new Builder().withCapabilities(capabilities).build(); - - if (!server.listening) await once(server, 'listening'); - - try { - await driver.get(`http://127.0.0.1:${server.address().port}`); - - // wait for last choice to init - await driver.wait( - until.elementLocated(By.css('#reset-multiple ~ .choices__list')), - 10000, - 'waiting for all Choices instances to init', - ); - - // Resize window - await driver - .manage() - .window() - .maximize(); - await driver - .manage() - .window() - // magic numbers here to make sure all demo page are fit inside - .setRect({ x: 0, y: 0, width: 630, height: 4000 }); - - // and click on press space on it, so it should open choices - await driver - .findElement(By.css('#reset-multiple ~ .choices__list button')) - .sendKeys(Key.SPACE); - await driver.sleep(1000); - - // take screenshot - const image = await driver.takeScreenshot(); - const imageBuffer = Buffer.from(image, 'base64'); - - const snapshotName = `${process.env.BROWSER}-${process.platform}.png`; - const artifactsPath = 'screenshot'; - mkdirSync(artifactsPath, { recursive: true }); - - writeFileSync(path.join(artifactsPath, snapshotName), imageBuffer); - - // compare with snapshot - const screenshot = PNG.sync.read(imageBuffer); - const snapshot = PNG.sync.read( - readFileSync(path.resolve(__dirname, `./__snapshots__/${snapshotName}`)), - ); - const { width, height } = screenshot; - const diff = new PNG({ width, height }); - pixelDifference = pixelmatch( - screenshot.data, - snapshot.data, - diff.data, - width, - height, - { - threshold: 1, - }, - ); - writeFileSync(path.join(artifactsPath, 'diff.png'), PNG.sync.write(diff)); - - // getting console logs - // ensure no errors in console (only supported in Chrome currently) - if (process.env.BROWSER === 'chrome') { - const entries = await driver - .manage() - .logs() - .get(logging.Type.BROWSER); - if ( - Array.isArray(entries) && - entries.some(entry => entry.level.name_ === 'SEVERE') - ) - throw new Error(JSON.stringify(entries)); - } - } catch (err) { - console.error(err); - error = err; - } finally { - await Promise.all([ - driver.quit(), - new Promise(resolve => server.close(resolve)), - ]); - } - if (pixelDifference > 200) { - console.error( - `Snapshot is different from screenshot by ${pixelDifference} pixels`, - ); - process.exit(1); - } - if (error) process.exit(1); -} - -process.on('unhandledRejection', err => { - console.error(err); - process.exit(1); -}); -process.once('uncaughtException', err => { - console.error(err); - process.exit(1); -}); -setImmediate(test); diff --git a/.github/workflows/browsers.yml b/.github/workflows/browsers.yml index 20ba1bc9c..1c10485fd 100644 --- a/.github/workflows/browsers.yml +++ b/.github/workflows/browsers.yml @@ -1,131 +1,88 @@ -name: Browsers - +name: End-to-end tests (playwright) on: + push: + branches: [ playwright ] pull_request: - paths: - - 'src/**' - - 'package-lock.json' - - '.browserslistrc' - - '.babelrc' - - 'webpack.config.*' - - 'public/index.html' - - '.github/actions-scripts/__snapshots__/**' - - '.github/workflows/browsers.yml' - + branches: [ main, master ] jobs: - selenium: + test-e2e-playwright: + timeout-minutes: 60 strategy: fail-fast: false matrix: - os: [windows-latest, macos-latest] - browser: [edge, firefox, safari, chrome] + os: [windows-latest, macos-latest, ubuntu-latest] + browser: [chromium, firefox, webkit] exclude: - os: windows-latest - browser: safari - - os: macos-latest - browser: edge + browser: webkit + - os: windows-latest + browser: firefox - os: macos-latest - browser: chrome - # Safari workaround is not working in Catalina - - browser: safari - + browser: firefox runs-on: ${{ matrix.os }} steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" + - uses: actions/checkout@v4 + with: + fetch-depth: 1 - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - uses: actions/setup-node@v2 - with: - node-version: 18.x - - - name: Cache node modules - uses: actions/cache@v2 - with: - path: ~/.npm - key: ${{ runner.OS }}-build-${{ matrix.browser }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' - - run: | - npm ci - npm run build - env: - CYPRESS_INSTALL_BINARY: 0 - HUSKY_SKIP_INSTALL: true + - name: Install dependencies + run: npm ci --no-audit + env: + HUSKY_SKIP_INSTALL: true - # install drivers - - name: Enable Safari Driver - run: | - # Workaround for `sudo safardriver --enable` not working: - # https://github.com/web-platform-tests/wpt/issues/19845 - # https://github.com/web-platform-tests/wpt/blob/master/tools/ci/azure/install_safari.yml - mkdir -p ~/Library/WebDriver/ - curl https://raw.githubusercontent.com/web-platform-tests/wpt/master/tools/ci/azure/com.apple.Safari.plist -o ~/Library/WebDriver/com.apple.Safari.plist - defaults write com.apple.Safari WebKitJavaScriptCanOpenWindowsAutomatically 1 - # sudo safaridriver --enable - if: matrix.browser == 'safari' + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - run: npx playwright install-deps - - run: | - brew install --cask firefox - brew install geckodriver - if: matrix.browser == 'firefox' && matrix.os == 'macos-latest' + - name: Run Playwright tests + run: npx playwright test --project=${{ matrix.browser }} + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: screenshot-${{ matrix.os }} + path: test-results/**/*.png + - uses: actions/upload-artifact@v4 + if: '!cancelled()' + with: + name: blob-report-${{ matrix.os }}-${{ matrix.browser }} + path: blob-report/ + retention-days: 1 - - run: echo "$env:GeckoWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'firefox' && matrix.os == 'windows-latest' + merge-reports: + # Merge reports after playwright-tests, even if some shards have failed + if: ${{ !cancelled() }} + needs: [test-e2e-playwright] - - run: echo "$env:EdgeWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'edge' && matrix.os == 'windows-latest' - - - run: echo "$env:ChromeWebDriver" >> $GITHUB_PATH - if: matrix.browser == 'chrome' && matrix.os == 'windows-latest' - - - run: npm i --no-optional --no-audit selenium-webdriver pixelmatch pngjs - - run: node .github/actions-scripts/selenium.js - env: - BROWSER: ${{ matrix.browser }} - PORT: 0 - NODE_ENV: production # prevent watching - - - uses: actions/upload-artifact@v2 - if: failure() - with: - name: screenshot-${{ matrix.browser }}-${{ matrix.os }} - path: screenshot - - puppeteer: - runs-on: macos-latest + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 - - name: Cache node modules - uses: actions/cache@v2 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install dependencies + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v4 with: - path: ~/.npm - key: ${{ runner.OS }}-build-puppeteer - restore-keys: | - ${{ runner.OS }}-build-puppeteer - - run: | - npm ci - npm run build - env: - CYPRESS_INSTALL_BINARY: 0 - HUSKY_SKIP_INSTALL: true - - run: npm i --no-optional --no-audit puppeteer pixelmatch pngjs - - run: node .github/actions-scripts/puppeteer.js - env: - PORT: 0 - NODE_ENV: production # prevent watching + path: all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --reporter html ./all-blob-reports - - uses: actions/upload-artifact@v2 - if: failure() + - name: Upload HTML report + uses: actions/upload-artifact@v4 with: - name: screenshot-puppeteer-darwin - path: screenshot + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 68eea047d..cf0737664 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -19,7 +19,7 @@ jobs: run: | npm ci npm run build - npx bundlesize + npm run bundlesize npm run test:unit:coverage npm run test:e2e env: @@ -32,7 +32,7 @@ jobs: BUNDLESIZE_GITHUB_TOKEN: ${{secrets.BUNDLESIZE_GITHUB_TOKEN}} FORCE_COLOR: 2 HUSKY_SKIP_INSTALL: true - ## + ## ## Disabling for now. There does not appear to be a secure way to do this ## with protected branches. See discussion: ## https://github.community/t/how-to-push-to-protected-branches-in-a-github-action/16101 diff --git a/.github/workflows/bundlesize.yml b/.github/workflows/bundlesize.yml index 905926123..259779680 100644 --- a/.github/workflows/bundlesize.yml +++ b/.github/workflows/bundlesize.yml @@ -12,28 +12,29 @@ jobs: measure: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 + cache: 'npm' - - name: Install dependencies and build - run: | - npm ci - npm run build + - name: Install dependencies + run: npm ci --no-audit env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true + - run: npm run build + # we don't need to build here, as even minized assets expected to be commited - - run: npx bundlesize + - run: npm run bundlesize env: - CI: true - BUNDLESIZE_GITHUB_TOKEN: ${{secrets.BUNDLESIZE_GITHUB_TOKEN}} + # token has expired, don't block the test + #CI: true + #BUNDLESIZE_GITHUB_TOKEN: ${{secrets.BUNDLESIZE_GITHUB_TOKEN}} CI_REPO_NAME: ${{ github.event.repository.name }} CI_REPO_OWNER: ${{ github.event.organization.login }} CI_COMMIT_SHA: ${{ github.event.after }} diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 455b534fd..c69e71b6a 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -3,8 +3,8 @@ name: Deploy Pages on: release: types: [published] - workflow_dispatch: - + workflow_dispatch: + jobs: deploy-gh-pages: runs-on: ubuntu-latest @@ -22,7 +22,6 @@ jobs: npm run build rm -rf public/test env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - name: Deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index bec38f47d..18ee93fca 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -17,7 +17,6 @@ jobs: registry-url: https://registry.npmjs.org/ - run: npm ci env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - run: npm publish env: diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml deleted file mode 100644 index d3ebf5cbf..000000000 --- a/.github/workflows/e2e-tests.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: End-to-end tests - -on: - pull_request: - paths: - - 'src/**' - - 'package-lock.json' - - '.browserslistrc' - - '.babelrc' - - 'webpack.config.*' - - 'public/test/**' - - 'cypress/**' - - '.github/workflows/e2e-tests.yml' - -jobs: - test-e2e: - runs-on: ubuntu-latest - env: - CI: true - TERM: xterm-256color - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 1 - - - uses: actions/setup-node@v2 - with: - node-version: 18.x - - - name: Cache node modules - uses: actions/cache@v2 - with: - path: ~/.npm - key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.OS }}-build-${{ env.cache-name }}- - ${{ runner.OS }}-build- - ${{ runner.OS }}- - - - name: Get Cypress info - id: cypress-info - run: | - echo ::set-output name=version::$(jq -r .devDependencies.cypress ./package.json) - echo ::set-output name=cache::$(npx cypress cache path) - env: - CYPRESS_INSTALL_BINARY: 0 - - name: Cache Cypress cache - uses: actions/cache@v2 - with: - path: ${{ steps.cypress-info.outputs.cache }} - key: ${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }} - restore-keys: | - ${{ runner.OS }}-cypress-${{ steps.cypress-info.outputs.version }} - - - name: Install dependencies - run: npm ci - env: - HUSKY_SKIP_INSTALL: true - - - name: run Cypress (with or without recording) - # if we have ran out of free Cypress recordings, run Cypress with recording switched off - run: npm exec -- run-p --race start cypress:ci || npm exec -- run-p --race start cypress:run - env: - NODE_ENV: production # prevent watching - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - DEBUG: commit-info,cypress:server:record - # https://docs.cypress.io/guides/guides/continuous-integration.html#Environment-variables - COMMIT_INFO_BRANCH: ${{ github.head_ref }} - COMMIT_INFO_AUTHOR: ${{ github.event.sender.login }} - COMMIT_INFO_SHA: ${{ github.event.after }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cd4d4d8fa..a4419257f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,18 +12,18 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 + cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --no-audit env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true - name: run eslint diff --git a/.github/workflows/polyfills-sync.yml b/.github/workflows/polyfills-sync.yml index 7cb7193f6..ebfda1a8e 100644 --- a/.github/workflows/polyfills-sync.yml +++ b/.github/workflows/polyfills-sync.yml @@ -11,13 +11,13 @@ jobs: sync: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 - name: Check Polyfills documentation and settings sync - run: node .github/actions-scripts/polyfills-sync.js + run: node .github/actions-scripts/polyfills-sync.cjs diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 1737db8b4..3c90dd88d 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -11,20 +11,22 @@ jobs: test-unit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 1 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 + cache: 'npm' - name: Install dependencies - run: npm install --no-optional --no-audit --ignore-scripts + run: npm ci --no-audit env: - CYPRESS_INSTALL_BINARY: 0 HUSKY_SKIP_INSTALL: true + - run: npm run build + - run: npm run test:unit:coverage env: FORCE_COLOR: 2 diff --git a/.gitignore b/.gitignore index 7b0171fd2..38fd10048 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,17 @@ node_modules npm-debug.log .DS_Store .idea +.rollup.cache +tsconfig.tsbuildinfo +.npmrc +.run # Test tests/reports tests/results .nyc_output coverage -cypress/videos -cypress/screenshots +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.mocharc.yml b/.mocharc.yml deleted file mode 100644 index 40fed2d6f..000000000 --- a/.mocharc.yml +++ /dev/null @@ -1,8 +0,0 @@ -require: - - 'ts-node/register' - - './config/jsdom.js' -exit: true -spec: src/**/**/*.test.ts -extension: - - ts - - js diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 285bebcb1..000000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -message=":bookmark: Version %s" -git-tag-version=true \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 28193ca74..b427e2ae2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v12.13.1 +v20.16.0 \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index ab9f9780b..dde4fb41d 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,4 +1,5 @@ { + "printWidth": 120, "singleQuote": true, "trailingComma": "all", "endOfLine": "lf", diff --git a/.stylelintrc.json b/.stylelintrc.json index 1f86e6bf7..d87c358e1 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,6 +1,7 @@ { "extends": "stylelint-config-standard-scss", "rules": { + "media-feature-range-notation": null, "declaration-block-no-redundant-longhand-properties": null } } \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 45cbd235e..a623962b6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,8 +10,5 @@ "github.vscode-pull-request-github", // needed for our configured debug configuration with Chrome "msjsdiag.debugger-for-chrome" - // Mocha recommended - https://mochajs.org/#mocha-sidebar-vs-code, - // but it's buggy - // "maty.vscode-mocha-sidebar" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 6bf8b709d..7592f2534 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,59 +12,5 @@ "webpack://Choices/*": "${workspaceFolder}/*" } }, - { - "type": "node", - "request": "launch", - "name": "Mocha Current File", - "program": "${workspaceFolder}/node_modules/mocha/bin/mocha", - "args": ["-u", "bdd", "--timeout", "999999", "--colors", "${file}"], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "env": { - "NODE_ENV": "test" - } - }, - { - "type": "node", - "request": "launch", - "name": "Mocha All", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": ["-u", "bdd", "--timeout", "999999", "--colors"], - "console": "integratedTerminal", - "internalConsoleOptions": "openOnSessionStart", - "env": { - "NODE_ENV": "test" - } - }, - { - "type": "node", - "request": "launch", - "name": "Cypress Current File", - "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/cypress", - "windows": { - "runtimeExecutable": "${workspaceFolder}\\node_modules\\.bin\\cypress.cmd" - }, - "runtimeArgs": [ - "run", - "--headed", - "--no-exit", - "--browser=electron", - "--port", - "9898", - "--spec" - ], - "protocol": "legacy", - "port": 9898, - "program": "${file}", - "console": "integratedTerminal", - "preLaunchTask": "buildAndWatch", - "internalConsoleOptions": "openOnSessionStart", - "timeout": 999999999999999, - "autoAttachChildProcesses": false, - "env": { - "NODE_ENV": "test" - // "DEBUG": "cypress:*" - } - } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 439eef583..5a669ecb2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,12 +23,6 @@ "public/assets": true, "**/coverage": true }, - // Mocha Sidebar settings - "mocha.env": { - "NODE_ENV": "test" - }, - "mocha.files.glob": "src/scripts/**/*.test.js", - "mocha.requires": ["@babel/register", "./config/jsdom.js"], // for Windows collaborators "files.eol": "\n", "files.encoding": "utf8", @@ -44,11 +38,6 @@ "npm.fetchOnlinePackageInfo": true, "eslint.packageManager": "npm", "json.schemas": [ - // Cypress related settings - https://docs.cypress.io/guides/tooling/intelligent-code-completion.html#Features-1 - { - "fileMatch": ["cypress.json"], - "url": "https://on.cypress.io/cypress.schema.json" - }, // Husky config file { "fileMatch": [".huskyrc"], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5014b08f8..aa3cc2087 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -78,10 +78,5 @@ "script": "test:unit", "group": "test" }, - { - "type": "npm", - "script": "cypress:open", - "isBackground": true - } ] } diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..4ec2555e0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,173 @@ +# Changelog + +## [11.0.0-rc8] (2024-08-) + +### ⚠ BREAKING CHANGES +* Trigger a search event (with empty value and 0 resultCount) when search stops + +### Features +* `searchResultLimit` can be set to `-1` for no limit of search results to display. + +### Bug Fixes (from 10.2.0) +* Fix edge case where aria-label could be added twice +* Fix the page scrolls when you press 'space' on a single select input #1103 + +### Chore +* Reduce the number of loops over choices when rendering search results, results in more compact code. +* Byte shave bundle sizes down + +## [11.0.0-rc7] (2024-08-19) + +### ⚠ BREAKING CHANGES +* Improve consistency of the `choice` event firing. `choice` event now occurs after the `addItem` event +* `enter` key now consistently opens/closes the dropdown instead of the behavior varying depending on backing element or internal state of the highlighted choice + +### Features +* Add `closeDropdownOnSelect` option, controls how the dropdown is close after selection is made. [#636](https://github.com/Choices-js/Choices/issues/636) [#973](https://github.com/Choices-js/Choices/issues/873) [#1012](https://github.com/Choices-js/Choices/issues/1012) +* Allow choices.js to be imported on nodejs, useful for tests and also server side rendering. As windows.document is by default not defined, the default template rendering will not function. The `callbackOnCreateTemplates` callback must be used. [#861](https://github.com/Choices-js/Choices/issues/861) + +### Bug Fixes (from 10.2.0) +* Improve various `[aria-*]` attribute handling for better lighthouse accessibility scores [#1169](https://github.com/Choices-js/Choices/issues/1169) +* Improve contrast on default CSS by darkening primary item selection color [#924](https://github.com/Choices-js/Choices/issues/924) + +### Bug Fixes (from 11.0.0RC6) +* Fix destroy&init of `choices.js` would lost track of data from the backing ``/`` initializes and selected values do not match the configured `choices.js` +* Fix legacy `placeholder` attribute support for `select-one` +* Fix `data-value` attribute on choices may not be correctly rendered into html + +### Chore +* Switch e2e tests from `puppeteer`/`selenium`/`cypress` to `playwright` +* Restructure end-to-end tests so html/script blocks are co-located to improve debugability +* Enable `@typescript-eslint/explicit-function-return-type` eslint rule + +## [11.0.0-rc6] (2024-08-12) + +### ⚠ BREAKING CHANGES +* Mutation APIs `setChoiceByValue`/`setChoices`/`setValue` now throw an error if the Choices instance was not initialized or multiple choices instances where initialized on the same element. Prevents bad internal states from triggering unexpected errors [#1129](https://github.com/Choices-js/Choices/issues/1129) + +### Features +* Improve performance of search/filtering with large number of choices. + +### Bug Fixes (from 10.2.0) +* Fix Choices does not accept an element from an iframe [#1057](https://github.com/Choices-js/Choices/issues/1057) +* Fix Choices was not disable in a `
` [#1132](https://github.com/Choices-js/Choices/issues/1132) +* Fix `silent` option does not silence warnings about unknown options [#1119](https://github.com/Choices-js/Choices/issues/1119) +* Fix documentation that suggests duplicateItemsAllowed works with select-multiple, when it only works for text. [#1123](https://github.com/Choices-js/Choices/issues/1123) +* Fix quadratic algorithm complexity (aka O(N^2) ) when filtering/search choices. +* Fix search results could be unexpectedly unstable, and that `fuseOptions.sortFn` was effectively ignored [#1106](https://github.com/Choices-js/Choices/issues/1106) + +### Bug Fixes (from 11.0.0RC1) +* Fix possible empty `aria-label` generation on remove item button +* Fix `clearChoices()` did not remove the actual selection options + +## [11.0.0-rc5] (2024-08-08) + +### ⚠ BREAKING CHANGES +* Update to using Fuse.js v7.0.0 +* Update choices.js package to be an ES module, and use '[subpath exports](https://nodejs.org/api/packages.html#subpath-exports)' to expose multiple versions (UMD, CJS or MTS bundles). +* Provide "fuse full" (default `choices.js`, ~20.36KB), or "fuse basic" (`choices.search-basic.js` ~19.31KB) or "prefix filter" (`choices.search-filter.js` ~15.27KB) based on how much Fuse.js is included. + +### Bug Fixes (from 10.2.0) +* Fix `select-one` placeholder could ignore the non-option placeholder configuration +* Remove typescript types for tests from distribution + +### Chore +* Reduce bundle size from ~24KB to ~20.36KB +* Switch bundler from `webpack` to `rollup` +* Switch test framework from `mocha` to `vitest` + +### Bug Fixes (from 11.0.0RC4) +* Fix `aria-describedby` was being assigned when it shouldn't be +* Fix check to ensure search was fully enabled for multiple select mode, as this functionality is hard-coded enabled elsewhere in the code base. + +## [11.0.0 RC3] (2024-08-04) + +### ⚠ BREAKING CHANGES +* For `select-one` and `select-multiple`, the placeholder value is pulled from `config.placeholderValue="..."` or `` as expected +* Fix adding user provided choices for `select-one` would not remove the existing item and result in a select-one with multiple items set. + +### Chore +* Remove unused code +* Use constant enum instead of repeating strings and type information +* For test html pages, prevent a failing `fetch()` from breaking the rest of the examples +* Tweak `_render()` loop to avoid duplicating has-changed checks + +## [11.0.0 RC1] (2024-08-02) + +### ⚠ BREAKING CHANGES + +* `allowHtml` now defaults to false. +* HTML escaping of choice/item labels should no longer double escape depending on allowHTML mode. +* Templates/text functions now escape `'` characters for display. +* `addItemText`/`uniqueItemText`/`customAddItemText` are now called with the `value` argument already escaped. +* Typescript classes for input data vs internal working data have been adjusted resulting in the `Choice`/`Group`/`Item` typescript classes have been renamed, and aliases left as required. + +### Features + +* `config.classNames` now accept arrays to support multiple classes. [#1121](https://github.com/Choices-js/Choices/issues/1121) [#1074](https://github.com/Choices-js/Choices/issues/1074) [#907](https://github.com/Choices-js/Choices/issues/907) [#832](https://github.com/Choices-js/Choices/issues/832) +* The original option list for the select is not destroyed, and all loaded choices are serialised to HTML for better compatibility with external javascript. [#1053](https://github.com/Choices-js/Choices/issues/1053) [#1023](https://github.com/Choices-js/Choices/issues/1023) +* New `singleModeForMultiSelect` feature to treat a `select-single` as if it was a `select-multiple` with a max item count of `1`, and still auto-close the dropdown and swap the active item on selection. [#1136](https://github.com/Choices-js/Choices/issues/1136) [#904](https://github.com/Choices-js/Choices/issues/904) +* `Remove item text` can be localized. +* Allow user-created choices for selects. [#1117](https://github.com/Choices-js/Choices/issues/1117) [#1114](https://github.com/Choices-js/Choices/issues/1114) + * User input is escaped by default. At the risk of XSS attacks this can be disabled by `allowHtmlUserInput`. +* Render options without a group even if groups are present. [#615](https://github.com/Choices-js/Choices/issues/615) [#1110](https://github.com/Choices-js/Choices/issues/1110) +* Read `data-labelclass`/`data-label-description` from ` ``` -For backward compatibility, `` is also supported. +For backward compatibility, `` and `` are also supported. ### placeholderValue @@ -583,10 +674,32 @@ For backward compatibility, `` **Type:** `String/Function` **Default:** `Press Enter to add "${value}"` -**Input types affected:** `text` +**Input types affected:** `text`, `select-one`, `select-multiple` **Usage:** The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example), otherwise pass a string. +Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + +### removeItemIconText + +**Type:** `String/Function` **Default:** `Remove item"` + +**Input types affected:** `text`, `select-one`, `select-multiple` + +**Usage:** The text/icon for the remove button. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + +Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + +### removeItemLabelText + +**Type:** `String/Function` **Default:** `Remove item: ${value}"` + +**Input types affected:** `text`, `select-one`, `select-multiple` + +**Usage:** The text for the remove button's aria label. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + +Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + ### maxItemText **Type:** `String/Function` **Default:** `Only ${maxItemCount} values can be added` @@ -625,29 +738,35 @@ const example = new Choices(element, { ``` classNames: { - containerOuter: 'choices', - containerInner: 'choices__inner', - input: 'choices__input', - inputCloned: 'choices__input--cloned', - list: 'choices__list', - listItems: 'choices__list--multiple', - listSingle: 'choices__list--single', - listDropdown: 'choices__list--dropdown', - item: 'choices__item', - itemSelectable: 'choices__item--selectable', - itemDisabled: 'choices__item--disabled', - itemOption: 'choices__item--choice', - group: 'choices__group', - groupHeading : 'choices__heading', - button: 'choices__button', - activeState: 'is-active', - focusState: 'is-focused', - openState: 'is-open', - disabledState: 'is-disabled', - highlightedState: 'is-highlighted', - selectedState: 'is-selected', - flippedState: 'is-flipped', - selectedState: 'is-highlighted', + containerOuter: ['choices'], + containerInner: ['choices__inner'], + input: ['choices__input'], + inputCloned: ['choices__input--cloned'], + list: ['choices__list'], + listItems: ['choices__list--multiple'], + listSingle: ['choices__list--single'], + listDropdown: ['choices__list--dropdown'], + item: ['choices__item'], + itemSelectable: ['choices__item--selectable'], + itemDisabled: ['choices__item--disabled'], + itemChoice: ['choices__item--choice'], + description: ['choices__description'], + placeholder: ['choices__placeholder'], + group: ['choices__group'], + groupHeading: ['choices__heading'], + button: ['choices__button'], + activeState: ['is-active'], + focusState: ['is-focused'], + openState: ['is-open'], + disabledState: ['is-disabled'], + highlightedState: ['is-highlighted'], + selectedState: ['is-selected'], + flippedState: ['is-flipped'], + loadingState: ['is-loading'], + notice: ['choices__notice'], + addChoice: ['choices__item--selectable', 'add-choice'], + noResults: ['has-no-results'], + noChoices: ['has-no-choices'], } ``` @@ -667,9 +786,9 @@ classNames: { **Usage:** Function to run once Choices initialises. -### callbackOnCreateTemplates +### callbackOnCreateTemplates(strToEl: (str: string) => HTMLElement, escapeForTemplate: (allowHTML: boolean, s: StringUntrusted | StringPreEscaped | string) => string) -**Type:** `Function` **Default:** `null` **Arguments:** `template` +**Type:** `Function` **Default:** `null` **Arguments:** `strToEl`, `escapeForTemplate` **Input types affected:** `text`, `select-one`, `select-multiple` @@ -679,11 +798,13 @@ original template function. Templates receive the full Choices config as the first argument to any template, which allows you to conditionally display things based on the options specified. +@note For each callback, `this` refers to the current instance of Choices. This can be useful if you need access to methods `(this.disable())`. + **Example:** ```js const example = new Choices(element, { - callbackOnCreateTemplates: () => ({ + callbackOnCreateTemplates: (strToEl, escapeForTemplate) => ({ input: (...args) => Object.assign(Choices.defaults.templates.input.call(this, ...args), { type: 'email', @@ -696,35 +817,35 @@ or more complex: ```js const example = new Choices(element, { - callbackOnCreateTemplates: function(template) { + callbackOnCreateTemplates: function(strToEl, escapeForTemplate) { return { item: ({ classNames }, data) => { return template(` -
- ${data.label} + ${escapeForTemplate(data.label)}
`); }, choice: ({ classNames }, data) => { return template(` -
0 ? 'role="treeitem"' : 'role="option"' }> - ${data.label} + ${escapeForTemplate(data.label)}
`); }, @@ -816,7 +937,7 @@ example.passedElement.element.addEventListener( ### change -**Payload:** `value` +**Payload:** `value: string` **Input types affected:** `text`, `select-one`, `select-multiple` @@ -824,11 +945,11 @@ example.passedElement.element.addEventListener( ### search -**Payload:** `value`, `resultCount` +**Payload:** `value: string`, `resultCount: number` **Input types affected:** `select-one`, `select-multiple` -**Usage:** Triggered when a user types into an input to search choices. +**Usage:** Triggered when a user types into an input to search choices. When a search is ended, a search event with an empty value with no resultCount is triggered. ### showDropdown @@ -892,6 +1013,12 @@ choices.disable(); **Note:** This is called implicitly when a new instance of Choices is created. This would be used after a Choices instance had already been destroyed (using `destroy()`). +### refresh(withEvents: boolean = false, selectFirstOption: boolean = false); + +**Input types affected:** `select-multiple`, `select-one` + +**Usage:** Reads options from backing ` element'); + } + return this; + } + this._store.withTxn(function () { + var choicesFromOptions = _this.passedElement.optionsAsChoices(); + var items = _this._store.items; + // Build the list of items which require preserving + var existingItems = {}; + if (!deselectAll) { + items.forEach(function (choice) { + if (choice.id && choice.active && choice.selected && !choice.disabled) { + existingItems[choice.value] = true; + } + }); + } + choicesFromOptions.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + return; + } + var choice = groupOrChoice; + if (deselectAll) { + choice.selected = false; + } + else if (existingItems[choice.value]) { + choice.selected = true; + } + }); + _this.clearStore(); + /* @todo only generate add events for the added options instead of all + if (withEvents) { + items.forEach((choice) => { + if (existingItems[choice.value]) { + this.passedElement.triggerEvent( + EventType.removeItem, + this._getChoiceForEvent(choice), + ); + } + }); + } + */ + // load new choices & items + _this._addPredefinedChoices(choicesFromOptions, selectFirstOption, withEvents); + // re-do search if required + if (_this._isSearching) { + _this._searchChoices(_this.input.value); + } + }); + return this; + }; + Choices.prototype.removeChoice = function (value) { + var choice = this._store.choices.find(function (c) { return c.value === value; }); + if (!choice) { + return this; + } + this._store.dispatch(removeChoice(choice)); + // @todo integrate with Store + this._searcher.reset(); + if (choice.selected) { + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.clearChoices = function () { + this.passedElement.element.innerHTML = ''; + this._store.dispatch(clearChoices()); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearStore = function () { + this._store.reset(); + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearInput = function () { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); + this._clearNotice(); + if (this._isSearching) { + this._stopSearch(); + } + return this; + }; + Choices.prototype._validateConfig = function () { + var config = this.config; + var invalidConfigOptions = diff(config, DEFAULT_CONFIG); + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + if (config.allowHTML && config.allowHtmlUserInput) { + if (config.addItems) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addItems all being true is strongly not recommended and may lead to XSS attacks'); + } + if (config.addChoices) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addChoices all being true is strongly not recommended and may lead to XSS attacks'); + } + } + }; + Choices.prototype._render = function (changes) { + if (changes === void 0) { changes = { choices: true, groups: true, items: true }; } + if (this._store.inTxn()) { + return; + } + if (this._isSelectElement) { + if (changes.choices || changes.groups) { + this._renderChoices(); + } + } + if (changes.items) { + this._renderItems(); + } + }; + Choices.prototype._renderChoices = function () { + var _this = this; + this.choiceList.clear(); + if (!this._canAddItems()) { + return; // block rendering choices if the input limit is reached. + } + var config = this.config; + var _a = this._store, activeGroups = _a.activeGroups, activeChoices = _a.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + var noChoices = true; + if (activeChoices.length) { + if (config.resetScrollPosition) { + requestAnimationFrame(function () { return _this.choiceList.scrollToTop(); }); + } + // If we have grouped options + if (activeGroups.length && !this._isSearching) { + if (!this._hasNonChoicePlaceholder) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { return activeChoice.placeholder && activeChoice.groupId === -1; }); + if (activePlaceholders.length) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + } + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } + else { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } + noChoices = !choiceListFragment.childNodes.length; + } + var notice = this._notice; + if (noChoices) { + if (!notice) { + this._notice = { + text: resolveStringFunction(config.noChoicesText), + type: NoticeTypes.noChoices, + }; + } + } + else if (notice && notice.type === NoticeTypes.noChoices) { + this._notice = undefined; + } + this._renderNotice(); + if (!noChoices) { + this.choiceList.element.append(choiceListFragment); + this._highlightChoice(); + } + }; + Choices.prototype._renderItems = function () { + var items = this._store.items || []; + this.itemList.clear(); + // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) + var itemListFragment = this._createItemsFragment(items); + // If we have items to add, append them + if (itemListFragment.childNodes.length) { + this.itemList.element.append(itemListFragment); + } + }; + Choices.prototype._createGroupsFragment = function (groups, choices, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + var config = this.config; + var getGroupChoices = function (group) { + return choices.filter(function (choice) { + if (_this._isSelectOneElement) { + return choice.groupId === group.id; + } + return choice.groupId === group.id && (config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; + // If sorting is enabled, filter groups + if (config.shouldSort) { + groups.sort(config.sorter); + } + // Add Choices without group first, regardless of sort, otherwise they won't be distinguishable + // from the last group + var choicesWithoutGroup = choices.filter(function (c) { return !c.groupId; }); + if (choicesWithoutGroup.length) { + this._createChoicesFragment(choicesWithoutGroup, fragment, false); + } + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + if (groupChoices.length) { + var dropdownGroup = _this._templates.choiceGroup(_this.config, group); + fragment.appendChild(dropdownGroup); + _this._createChoicesFragment(groupChoices, fragment, true); + } + }); + return fragment; + }; + Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + if (withinGroup === void 0) { withinGroup = false; } + // Create a fragment to store our list items (so we don't have to update the DOM for each item) + var _a = this, config = _a.config, isSearching = _a._isSearching, isSelectOneElement = _a._isSelectOneElement; + var searchResultLimit = config.searchResultLimit, renderChoiceLimit = config.renderChoiceLimit; + var groupLookup = []; + var appendGroupInSearch = config.appendGroupInSearch && isSearching; + if (appendGroupInSearch) { + this._store.groups.forEach(function (group) { + groupLookup[group.id] = group.label; + }); + } + if (this._isSelectElement) { + var backingOptions = choices.filter(function (choice) { return !choice.element; }); + if (backingOptions.length) { + this.passedElement.addOptions(backingOptions); + } + } + var skipSelected = config.renderSelectedChoices === 'auto' && !isSelectOneElement; + var placeholderChoices = []; + var normalChoices = []; + choices.forEach(function (choice) { + if ((isSearching && !choice.rank) || (skipSelected && choice.selected)) { + return; + } + if (_this._hasNonChoicePlaceholder || !choice.placeholder) { + normalChoices.push(choice); + } + else { + placeholderChoices.push(choice); + } + }); + if (isSearching) { + // sortByRank is used to ensure stable sorting, as scores are non-unique + // this additionally ensures fuseOptions.sortFn is not ignored + normalChoices.sort(sortByRank); + } + else if (config.shouldSort) { + normalChoices.sort(config.sorter); + } + var sortedChoices = isSelectOneElement && placeholderChoices.length ? __spreadArray(__spreadArray([], placeholderChoices, true), normalChoices, true) : normalChoices; + var choiceLimit = sortedChoices.length; + var limit = choiceLimit; + if (isSearching && searchResultLimit > 0) { + limit = searchResultLimit; + } + else if (renderChoiceLimit > 0 && !withinGroup) { + limit = renderChoiceLimit; + } + if (limit < choiceLimit) { + choiceLimit = limit; + } + choiceLimit--; + // Add each choice to dropdown within range + sortedChoices.every(function (choice, index) { + var dropdownItem = _this._templates.choice(config, choice, config.itemSelectText); + if (appendGroupInSearch && choice.groupId > 0) { + var groupName = groupLookup[choice.groupId]; + if (groupName) { + dropdownItem.innerHTML += " (".concat(groupName, ")"); + } + } + fragment.appendChild(dropdownItem); + return index < choiceLimit; + }); + return fragment; + }; + Choices.prototype._createItemsFragment = function (items, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + // Create fragment to add elements to + var config = this.config; + var shouldSortItems = config.shouldSortItems, sorter = config.sorter, removeItemButton = config.removeItemButton, delimiter = config.delimiter; + // If sorting is enabled, filter items + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sorter); + } + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items.map(function (_a) { + var value = _a.value; + return value; + }).join(delimiter); + } + var addItemToFragment = function (item) { + // Create new list element + var listItem = _this._templates.item(config, item, removeItemButton); + // Append it to list + fragment.appendChild(listItem); + }; + // Add each list item to list + items.forEach(addItemToFragment); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder && !items.length) { + addItemToFragment(mapInputToChoice({ + selected: true, + value: '', + label: this.config.placeholderValue || '', + active: true, + placeholder: true, + }, false)); + } + return fragment; + }; + Choices.prototype._displayNotice = function (text, type, openDropdown) { + if (openDropdown === void 0) { openDropdown = true; } + var oldNotice = this._notice; + if (oldNotice && + ((oldNotice.type === type && oldNotice.text === text) || + (oldNotice.type === NoticeTypes.addChoice && + (type === NoticeTypes.noResults || type === NoticeTypes.noChoices)))) { + if (openDropdown) { + this.showDropdown(true); + } + return; + } + this._clearNotice(); + this._notice = text + ? { + text: text, + type: type, + } + : undefined; + this._renderNotice(); + if (openDropdown && text) { + this.showDropdown(true); + } + }; + Choices.prototype._clearNotice = function () { + if (!this._notice) { + return; + } + var noticeElement = this.choiceList.element.querySelector(getClassNamesSelector(this.config.classNames.notice)); + if (noticeElement) { + noticeElement.remove(); + } + this._notice = undefined; + }; + Choices.prototype._renderNotice = function () { + var noticeConf = this._notice; + if (noticeConf) { + var notice = this._templates.notice(this.config, noticeConf.text, noticeConf.type); + this.choiceList.prepend(notice); + } + }; + Choices.prototype._getChoiceForOutput = function (choice, keyCode) { + if (!choice) { + return undefined; + } + var group = choice.groupId > 0 ? this._store.getGroupById(choice.groupId) : null; + return { + id: choice.id, + highlighted: choice.highlighted, + labelClass: choice.labelClass, + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + disabled: choice.disabled, + active: choice.active, + label: choice.label, + placeholder: choice.placeholder, + value: choice.value, + groupValue: group && group.label ? group.label : undefined, + element: choice.element, + keyCode: keyCode, + }; + }; + Choices.prototype._triggerChange = function (value) { + if (value === undefined || value === null) { + return; + } + this.passedElement.triggerEvent("change" /* EventType.change */, { + value: value, + }); + }; + Choices.prototype._handleButtonAction = function (element) { + var items = this._store.items; + if (!items.length || !this.config.removeItems || !this.config.removeItemButton) { + return; + } + var id = element && parseDataSetId(element.parentNode); + var itemToRemove = id && items.find(function (item) { return item.id === id; }); + if (!itemToRemove) { + return; + } + // Remove item associated with button + this._removeItem(itemToRemove); + this._triggerChange(itemToRemove.value); + if (this._isSelectOneElement && !this._hasNonChoicePlaceholder) { + var placeholderChoice = this._store.choices.reverse().find(function (choice) { return !choice.disabled && choice.placeholder; }); + if (placeholderChoice) { + this._addItem(placeholderChoice); + if (placeholderChoice.value) { + this._triggerChange(placeholderChoice.value); + } + } + } + }; + Choices.prototype._handleItemAction = function (element, hasShiftKey) { + var _this = this; + if (hasShiftKey === void 0) { hasShiftKey = false; } + var items = this._store.items; + if (!items.length || !this.config.removeItems || this._isSelectOneElement) { + return; + } + var id = parseDataSetId(element); + if (!id) { + return; + } + // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + items.forEach(function (item) { + if (item.id === id && !item.highlighted) { + _this.highlightItem(item); + } + else if (!hasShiftKey && item.highlighted) { + _this.unhighlightItem(item); + } + }); + // Focus input as without focus, a user cannot do anything with a + // highlighted item + this.input.focus(); + }; + Choices.prototype._handleChoiceAction = function (element) { + var _this = this; + // If we are clicking on an option + var id = parseDataSetId(element); + var choice = id && this._store.getChoiceById(id); + if (!choice || choice.disabled) { + return false; + } + var hasActiveDropdown = this.dropdown.isActive; + if (!choice.selected) { + if (!this._canAddItems()) { + return true; // causes _onEnterKey to early out + } + this._store.withTxn(function () { + _this._addItem(choice, true, true); + _this.clearInput(); + _this.unhighlightAll(); + }); + this._triggerChange(choice.value); + } + // We want to close the dropdown if we are dealing with a single select box + if (hasActiveDropdown && this.config.closeDropdownOnSelect) { + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + return true; + }; + Choices.prototype._handleBackspace = function (items) { + var config = this.config; + if (!config.removeItems || !items.length) { + return; + } + var lastItem = items[items.length - 1]; + var hasHighlightedItems = items.some(function (item) { return item.highlighted; }); + // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items + if (config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + this._removeItem(lastItem); + this._triggerChange(lastItem.value); + } + else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } + this.removeHighlightedItems(true); + } + }; + Choices.prototype._loadChoices = function () { + var _a; + var config = this.config; + if (this._isTextElement) { + // Assign preset items from passed object first + this._presetChoices = config.items.map(function (e) { return mapInputToChoice(e, false); }); + // Add any values passed from attribute + var value = this.passedElement.value; + if (value) { + var elementItems = value + .split(config.delimiter) + .map(function (e) { return mapInputToChoice(e, false); }); + this._presetChoices = this._presetChoices.concat(elementItems); + } + this._presetChoices.forEach(function (choice) { + choice.selected = true; + }); + } + else if (this._isSelectElement) { + // Assign preset choices from passed object + this._presetChoices = config.choices.map(function (e) { return mapInputToChoice(e, true); }); + // Create array of choices from option elements + var choicesFromOptions = this.passedElement.optionsAsChoices(); + if (choicesFromOptions) { + (_a = this._presetChoices).push.apply(_a, choicesFromOptions); + } + } + }; + Choices.prototype._handleLoadingState = function (setLoading) { + if (setLoading === void 0) { setLoading = true; } + var config = this.config; + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); + if (this._isSelectOneElement) { + this.itemList.clear(); + this.itemList.element.append(this._templates.placeholder(config, config.loadingText)); + } + else { + this.input.placeholder = config.loadingText; + } + } + else { + this.enable(); + this.containerOuter.removeLoadingState(); + if (!this._isSelectOneElement) { + this.input.placeholder = this._placeholderValue || ''; + } + } + }; + Choices.prototype._handleSearch = function (value) { + if (!this.input.isFocussed) { + return; + } + var choices = this._store.choices; + var _a = this.config, searchFloor = _a.searchFloor, searchChoices = _a.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { return !option.active; }); + // Check that we have a value to search and the input was an alphanumeric character + if (value !== null && typeof value !== 'undefined' && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; + if (resultCount !== null) { + // Trigger search event + this.passedElement.triggerEvent("search" /* EventType.search */, { + value: value, + resultCount: resultCount, + }); + } + } + else if (hasUnactiveChoices) { + this._stopSearch(); + } + }; + Choices.prototype._canAddItems = function () { + var config = this.config; + var maxItemCount = config.maxItemCount, maxItemText = config.maxItemText; + if (!config.singleModeForMultiSelect && maxItemCount > 0 && maxItemCount <= this._store.items.length) { + this._displayNotice(typeof maxItemText === 'function' ? maxItemText(maxItemCount) : maxItemText, NoticeTypes.addChoice); + return false; + } + return true; + }; + Choices.prototype._canCreateItem = function (value) { + var config = this.config; + var canAddItem = true; + var notice = ''; + if (canAddItem && typeof config.addItemFilter === 'function' && !config.addItemFilter(value)) { + canAddItem = false; + notice = resolveNoticeFunction(config.customAddItemText, value); + } + if (canAddItem) { + var foundChoice = this._store.choices.find(function (choice) { return config.valueComparer(choice.value, value); }); + if (this._isSelectElement) { + // for exact matches, do not prompt to add it as a custom choice + if (foundChoice) { + this._displayNotice('', NoticeTypes.addChoice); + return false; + } + } + else if (this._isTextElement && !config.duplicateItemsAllowed) { + if (foundChoice) { + canAddItem = false; + notice = resolveNoticeFunction(config.uniqueItemText, value); + } + } + } + if (canAddItem) { + notice = resolveNoticeFunction(config.addItemText, value); + } + if (notice) { + this._displayNotice(notice, NoticeTypes.addChoice); + } + return canAddItem; + }; + Choices.prototype._searchChoices = function (value) { + var newValue = value.trim().replace(/\s{2,}/, ' '); + // signal input didn't change search + if (!newValue.length || newValue === this._currentValue) { + return null; + } + var searcher = this._searcher; + if (searcher.isEmptyIndex()) { + searcher.index(this._store.searchableChoices); + } + // If new value matches the desired length and is not the same as the current value with a space + var results = searcher.search(newValue); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + var notice = this._notice; + var noticeType = notice && notice.type; + if (noticeType !== NoticeTypes.addChoice) { + if (!results.length) { + this._displayNotice(resolveStringFunction(this.config.noResultsText), NoticeTypes.noResults); + } + else if (noticeType === NoticeTypes.noResults) { + this._clearNotice(); + } + } + this._store.dispatch(filterChoices(results)); + return results.length; + }; + Choices.prototype._stopSearch = function () { + var wasSearching = this._isSearching; + this._currentValue = ''; + this._isSearching = false; + if (wasSearching) { + this._store.dispatch(activateChoices(true)); + } + }; + Choices.prototype._addEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + // capture events - can cancel event processing or propagation + documentElement.addEventListener('touchend', this._onTouchEnd, true); + outerElement.addEventListener('keydown', this._onKeyDown, true); + outerElement.addEventListener('mousedown', this._onMouseDown, true); + // passive events - doesn't call `preventDefault` or `stopPropagation` + documentElement.addEventListener('click', this._onClick, { passive: true }); + documentElement.addEventListener('touchmove', this._onTouchMove, { + passive: true, + }); + this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { + passive: true, + }); + if (this._isSelectOneElement) { + outerElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + outerElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + } + inputElement.addEventListener('keyup', this._onKeyUp, { + passive: true, + }); + inputElement.addEventListener('input', this._onInput, { + passive: true, + }); + inputElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + inputElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + if (inputElement.form) { + inputElement.form.addEventListener('reset', this._onFormReset, { + passive: true, + }); + } + this.input.addEventListeners(); + }; + Choices.prototype._removeEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + documentElement.removeEventListener('touchend', this._onTouchEnd, true); + outerElement.removeEventListener('keydown', this._onKeyDown, true); + outerElement.removeEventListener('mousedown', this._onMouseDown, true); + documentElement.removeEventListener('click', this._onClick); + documentElement.removeEventListener('touchmove', this._onTouchMove); + this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); + if (this._isSelectOneElement) { + outerElement.removeEventListener('focus', this._onFocus); + outerElement.removeEventListener('blur', this._onBlur); + } + inputElement.removeEventListener('keyup', this._onKeyUp); + inputElement.removeEventListener('input', this._onInput); + inputElement.removeEventListener('focus', this._onFocus); + inputElement.removeEventListener('blur', this._onBlur); + if (inputElement.form) { + inputElement.form.removeEventListener('reset', this._onFormReset); + } + this.input.removeEventListeners(); + }; + Choices.prototype._onKeyDown = function (event) { + var keyCode = event.keyCode; + var items = this._store.items; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.element.hasChildNodes(); + /* + See: + https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key + https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values + https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs + https://stackoverflow.com/a/70866532 - "Unidentified" for mobile + http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7) + + Logic: when a key event is sent, `event.key` represents its printable value _or_ one + of a large list of special values indicating meta keys/functionality. In addition, + key events for compose functionality contain a value of `Dead` when mid-composition. + + I can't quite verify it, but non-English IMEs may also be able to generate key codes + for code points in the surrogate-pair range, which could potentially be seen as having + key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that + alone. + + Here, key.length === 1 means we know for sure the input was printable and not a special + `key` value. When the length is greater than 1, it could be either a printable surrogate + pair or a special `key` value. We can tell the difference by checking if the _character + code_ value (not code point!) is in the "surrogate pair" range or not. + + We don't use .codePointAt because an invalid code point would return 65535, which wouldn't + pass the >= 0x10000 check we would otherwise use. + + > ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points + > of each plane are noncharacters: U+FFFE and U+FFFF on the BMP... + */ + var wasPrintableChar = event.key.length === 1 || + (event.key.length === 2 && event.key.charCodeAt(0) >= 0xd800) || + event.key === 'Unidentified'; + if (!this._isTextElement && !hasActiveDropdown) { + this.showDropdown(); + if (!this.input.isFocussed && wasPrintableChar) { + /* + We update the input value with the pressed key as + the input was not focussed at the time of key press + therefore does not have the value of the key. + */ + this.input.value += event.key; + } + } + switch (keyCode) { + case 65 /* KeyCodeMap.A_KEY */: + return this._onSelectKey(event, hasItems); + case 13 /* KeyCodeMap.ENTER_KEY */: + return this._onEnterKey(event, hasActiveDropdown); + case 27 /* KeyCodeMap.ESC_KEY */: + return this._onEscapeKey(event, hasActiveDropdown); + case 38 /* KeyCodeMap.UP_KEY */: + case 33 /* KeyCodeMap.PAGE_UP_KEY */: + case 40 /* KeyCodeMap.DOWN_KEY */: + case 34 /* KeyCodeMap.PAGE_DOWN_KEY */: + return this._onDirectionKey(event, hasActiveDropdown); + case 8 /* KeyCodeMap.DELETE_KEY */: + case 46 /* KeyCodeMap.BACK_KEY */: + return this._onDeleteKey(event, items, hasFocusedInput); + } + }; + Choices.prototype._onKeyUp = function ( /* event: KeyboardEvent */) { + this._canSearch = this.config.searchEnabled; + }; + Choices.prototype._onInput = function ( /* event: InputEvent */) { + var value = this.input.value; + if (!value) { + if (this._isTextElement) { + this.hideDropdown(true); + } + else { + this._stopSearch(); + } + this._clearNotice(); + return; + } + if (!this._canAddItems()) { + return; + } + if (this._canSearch) { + // do the search even if the entered text can not be added + this._handleSearch(value); + } + if (!this._canAddUserChoices) { + return; + } + // determine if a notice needs to be displayed for why a search result can't be added + this._canCreateItem(value); + if (this._isSelectElement) { + this._highlightPosition = 0; // reset to select the notice and/or exact match + this._highlightChoice(); + } + }; + Choices.prototype._onSelectKey = function (event, hasItems) { + var ctrlKey = event.ctrlKey, metaKey = event.metaKey; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; + // If CTRL + A or CMD + A have been pressed and there are items to select + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + if (shouldHightlightAll) { + this.highlightAll(); + } + } + }; + Choices.prototype._onEnterKey = function (event, hasActiveDropdown) { + var _this = this; + var config = this.config; + var value = this.input.value; + var target = event.target; + var targetWasRemoveButton = target && target.hasAttribute('data-button'); + event.preventDefault(); + if (targetWasRemoveButton) { + this._handleButtonAction(target); + return; + } + if (!hasActiveDropdown) { + if (this._isSelectElement || this._notice) { + this.showDropdown(); + } + return; + } + var highlightedChoice = this.dropdown.element.querySelector(getClassNamesSelector(config.classNames.highlightedState)); + if (highlightedChoice && this._handleChoiceAction(highlightedChoice)) { + return; + } + if (!target || !value) { + this.hideDropdown(true); + return; + } + if (!this._canAddItems()) { + return; + } + var addedItem = false; + this._store.withTxn(function () { + addedItem = _this._findAndSelectChoiceByValue(value, true); + if (!addedItem) { + if (!_this._canAddUserChoices) { + return; + } + if (!_this._canCreateItem(value)) { + return; + } + var sanitisedValue = sanitise(value); + var userValue = config.allowHtmlUserInput || sanitisedValue === value ? value : { escaped: sanitisedValue, raw: value }; + _this._addChoice(mapInputToChoice({ + value: userValue, + label: userValue, + selected: true, + }, false), true, true); + addedItem = true; + } + _this.clearInput(); + _this.unhighlightAll(); + }); + if (!addedItem) { + return; + } + this._triggerChange(value); + if (config.closeDropdownOnSelect) { + this.hideDropdown(true); + } + }; + Choices.prototype._onEscapeKey = function (event, hasActiveDropdown) { + if (hasActiveDropdown) { + event.stopPropagation(); + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + }; + Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) { + var keyCode = event.keyCode, metaKey = event.metaKey; + // If up or down key is pressed, traverse through options + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === 40 /* KeyCodeMap.DOWN_KEY */ || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ ? 1 : -1; + var skipKey = metaKey || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ || keyCode === 33 /* KeyCodeMap.PAGE_UP_KEY */; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl = void 0; + if (skipKey) { + if (directionInt > 0) { + nextEl = this.dropdown.element.querySelector("".concat(selectableChoiceIdentifier, ":last-of-type")); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + else { + var currentEl = this.dropdown.element.querySelector(getClassNamesSelector(this.config.classNames.highlightedState)); + if (currentEl) { + nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChildElement(nextEl, directionInt); + } + this._highlightChoice(nextEl); + } + // Prevent default to maintain cursor position whilst + // traversing dropdown options + event.preventDefault(); + } + }; + Choices.prototype._onDeleteKey = function (event, items, hasFocusedInput) { + var target = event.target; + // If backspace or delete key is pressed and the input has no value + if (!this._isSelectOneElement && !target.value && hasFocusedInput) { + this._handleBackspace(items); + event.preventDefault(); + } + }; + Choices.prototype._onTouchMove = function () { + if (this._wasTap) { + this._wasTap = false; + } + }; + Choices.prototype._onTouchEnd = function (event) { + var target = (event || event.touches[0]).target; + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } + else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } + // Prevents focus event firing + event.stopPropagation(); + } + this._wasTap = true; + }; + /** + * Handles mousedown event in capture mode for containetOuter.element + */ + Choices.prototype._onMouseDown = function (event) { + var target = event.target; + if (!(target instanceof HTMLElement)) { + return; + } + // If we have our mouse down on the scrollbar and are on IE11... + if (IS_IE11 && this.choiceList.element.contains(target)) { + // check if click was on a scrollbar area + var firstChoice = this.choiceList.element.firstElementChild; + this._isScrollingOnIe = + this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; + } + if (target === this.input.element) { + return; + } + var item = target.closest('[data-button],[data-item],[data-choice]'); + if (item instanceof HTMLElement) { + var hasShiftKey = event.shiftKey; + var dataset = item.dataset; + if ('button' in dataset) { + this._handleButtonAction(item); + } + else if ('item' in dataset) { + this._handleItemAction(item, hasShiftKey); + } + else if ('choice' in dataset) { + this._handleChoiceAction(item); + } + } + event.preventDefault(); + }; + /** + * Handles mouseover event over this.dropdown + * @param {MouseEvent} event + */ + Choices.prototype._onMouseOver = function (_a) { + var target = _a.target; + if (target instanceof HTMLElement && 'choice' in target.dataset) { + this._highlightChoice(target); + } + }; + Choices.prototype._onClick = function (_a) { + var target = _a.target; + var containerOuter = this.containerOuter; + var clickWasWithinContainer = containerOuter.element.contains(target); + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } + else { + this.showDropdown(); + containerOuter.element.focus(); + } + } + else if (this._isSelectOneElement && + target !== this.input.element && + !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } + else { + var hasHighlightedItems = !!this._store.highlightedActiveItems.length; + if (hasHighlightedItems) { + this.unhighlightAll(); + } + containerOuter.removeFocusState(); + this.hideDropdown(true); + } + }; + Choices.prototype._onFocus = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var focusWasWithinContainer = target && containerOuter.element.contains(target); + if (!focusWasWithinContainer) { + return; + } + var targetIsInput = target === this.input.element; + var focusActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput) { + containerOuter.addFocusState(); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.addFocusState(); + if (targetIsInput) { + _this.showDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput) { + _this.showDropdown(true); + // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown + containerOuter.addFocusState(); + } + }, + _b); + focusActions[this._elementType](); + }; + Choices.prototype._onBlur = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var blurWasWithinContainer = target && containerOuter.element.contains(target); + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeChoices = this._store.activeChoices; + var hasHighlightedItems_1 = activeChoices.some(function (item) { return item.highlighted; }); + var targetIsInput_1 = target === this.input.element; + var blurActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + _this.hideDropdown(true); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.removeFocusState(); + if (targetIsInput_1 || (target === containerOuter.element && !_this._canSearch)) { + _this.hideDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + _this.hideDropdown(true); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + } + }, + _b); + blurActions[this._elementType](); + } + else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + }; + Choices.prototype._onFormReset = function () { + var _this = this; + this._store.withTxn(function () { + _this.clearInput(); + _this.hideDropdown(); + _this.refresh(false, false, true); + if (_this._initialItems.length) { + _this.setChoiceByValue(_this._initialItems); + } + }); + }; + Choices.prototype._highlightChoice = function (el) { + var _a; + if (el === void 0) { el = null; } + var highlightedState = this.config.classNames.highlightedState; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); + if (!choices.length) { + return; + } + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(getClassNamesSelector(highlightedState))); + // Remove any highlighted choices + highlightedChoices.forEach(function (choice) { + var _a; + (_a = choice.classList).remove.apply(_a, getClassNames(highlightedState)); + choice.setAttribute('aria-selected', 'false'); + }); + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } + else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } + else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + if (!passedEl) { + passedEl = choices[0]; + } + } + (_a = passedEl.classList).add.apply(_a, getClassNames(highlightedState)); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent("highlightChoice" /* EventType.highlightChoice */, { + el: passedEl, + }); + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + }; + Choices.prototype._addItem = function (item, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + var id = item.id; + if (!id) { + throw new TypeError('item.id must be set before _addItem is called for a choice/item'); + } + if (this.config.singleModeForMultiSelect || this._isSelectOneElement) { + this.removeActiveItems(id); + } + this._store.dispatch(addItem(item)); + if (withEvents) { + this.passedElement.triggerEvent("addItem" /* EventType.addItem */, this._getChoiceForOutput(item)); + if (userTriggered) { + this.passedElement.triggerEvent("choice" /* EventType.choice */, this._getChoiceForOutput(item)); + } + } + }; + Choices.prototype._removeItem = function (item) { + var id = item.id; + if (!id) { + return; + } + this._store.dispatch(removeItem(item)); + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(item)); + }; + Choices.prototype._addChoice = function (choice, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + if (choice.id) { + throw new TypeError('Can not re-add a choice which has already been added'); + } + // Generate unique id, in-place update is required so chaining _addItem works as expected + this._lastAddedChoiceId++; + choice.id = this._lastAddedChoiceId; + choice.elementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choice.id); + var _a = this.config, prependValue = _a.prependValue, appendValue = _a.appendValue; + if (prependValue) { + choice.value = prependValue + choice.value; + } + if (appendValue) { + choice.value += appendValue.toString(); + } + if ((prependValue || appendValue) && choice.element) { + choice.element.value = choice.value; + } + this._store.dispatch(addChoice(choice)); + if (choice.selected) { + this._addItem(choice, withEvents, userTriggered); + } + }; + Choices.prototype._addGroup = function (group, withEvents) { + var _this = this; + if (withEvents === void 0) { withEvents = true; } + if (group.id) { + throw new TypeError('Can not re-add a group which has already been added'); + } + this._store.dispatch(addGroup(group)); + if (!group.choices) { + return; + } + // add unique id for the group(s), and do not store the full list of choices in this group + var g = group; + this._lastAddedGroupId++; + g.id = this._lastAddedGroupId; + var id = group.id, choices = group.choices; + g.choices = []; + choices.forEach(function (item) { + item.groupId = id; + if (group.disabled) { + item.disabled = true; + } + _this._addChoice(item, withEvents); + }); + }; + Choices.prototype._createTemplates = function () { + var _this = this; + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; + if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { + userTemplates = callbackOnCreateTemplates.call(this, strToEl, escapeForTemplate); + } + var templating = {}; + Object.keys(this._templates).forEach(function (name) { + if (name in userTemplates) { + templating[name] = userTemplates[name].bind(_this); + } + else { + templating[name] = _this._templates[name].bind(_this); + } + }); + this._templates = templating; + }; + Choices.prototype._createElements = function () { + var templating = this._templates; + var config = this.config; + var position = config.position, classNames = config.classNames; + var elementType = this._elementType; + this.containerOuter = new Container({ + element: templating.containerOuter(config, this._direction, this._isSelectElement, this._isSelectOneElement, config.searchEnabled, elementType, config.labelId), + classNames: classNames, + type: elementType, + position: position, + }); + this.containerInner = new Container({ + element: templating.containerInner(config), + classNames: classNames, + type: elementType, + position: position, + }); + this.input = new Input({ + element: templating.input(config, this._placeholderValue), + classNames: classNames, + type: elementType, + preventPaste: !config.paste, + }); + this.choiceList = new List({ + element: templating.choiceList(config, this._isSelectOneElement), + }); + this.itemList = new List({ + element: templating.itemList(config, this._isSelectOneElement), + }); + this.dropdown = new Dropdown({ + element: templating.dropdown(config), + classNames: classNames, + type: elementType, + }); + }; + Choices.prototype._createStructure = function () { + var _a = this, containerInner = _a.containerInner, containerOuter = _a.containerOuter, passedElement = _a.passedElement, dropdown = _a.dropdown, input = _a.input; + // Hide original element + passedElement.conceal(); + // Wrap input in container preserving DOM ordering + containerInner.wrap(passedElement.element); + // Wrapper inner container with outer container + containerOuter.wrap(containerInner.element); + if (this._isSelectOneElement) { + input.placeholder = this.config.searchPlaceholderValue || ''; + } + else { + if (this._placeholderValue) { + input.placeholder = this._placeholderValue; + } + input.setWidth(); + } + containerOuter.element.appendChild(containerInner.element); + containerOuter.element.appendChild(dropdown.element); + containerInner.element.appendChild(this.itemList.element); + dropdown.element.appendChild(this.choiceList.element); + if (!this._isSelectOneElement) { + containerInner.element.appendChild(input.element); + } + else if (this.config.searchEnabled) { + dropdown.element.insertBefore(input.element, dropdown.element.firstChild); + } + this._highlightPosition = 0; + this._isSearching = false; + }; + Choices.prototype._initStore = function () { + var _this = this; + this._store.subscribe(this._render); + this._store.withTxn(function () { + _this._addPredefinedChoices(_this._presetChoices, _this._isSelectOneElement && !_this._hasNonChoicePlaceholder, false); + }); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder) { + this._render({ choices: false, groups: false, items: true }); + } + }; + Choices.prototype._addPredefinedChoices = function (choices, selectFirstOption, withEvents) { + var _this = this; + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (withEvents === void 0) { withEvents = true; } + if (selectFirstOption) { + /** + * If there is a selected choice already or the choice is not the first in + * the array, add each choice normally. + * + * Otherwise we pre-select the first enabled choice in the array ("select-one" only) + */ + var noSelectedChoices = choices.findIndex(function (choice) { return choice.selected; }) === -1; + if (noSelectedChoices) { + choices.some(function (choice) { + if (choice.disabled || 'choices' in choice) { + return false; + } + choice.selected = true; + return true; + }); + } + } + choices.forEach(function (item) { + if ('choices' in item) { + if (_this._isSelectElement) { + _this._addGroup(item, withEvents); + } + } + else { + _this._addChoice(item, withEvents); + } + }); + }; + Choices.prototype._findAndSelectChoiceByValue = function (value, userTriggered) { + var _this = this; + if (userTriggered === void 0) { userTriggered = false; } + var choices = this._store.choices; + // Check 'value' property exists and the choice isn't already selected + var foundChoice = choices.find(function (choice) { return _this.config.valueComparer(choice.value, value); }); + if (foundChoice && !foundChoice.disabled && !foundChoice.selected) { + this._addItem(foundChoice, true, userTriggered); + return true; + } + return false; + }; + Choices.prototype._generatePlaceholderValue = function () { + var config = this.config; + if (!config.placeholder) { + return null; + } + if (this._hasNonChoicePlaceholder) { + return config.placeholderValue; + } + if (this._isSelectElement) { + var placeholderOption = this.passedElement.placeholderOption; + return placeholderOption ? placeholderOption.text : null; + } + return null; + }; + Choices.prototype._warnChoicesInitFailed = function (caller) { + if (this.config.silent) { + return; + } + if (!this.initialised) { + throw new TypeError("".concat(caller, " called on a non-initialised instance of Choices")); + } + else if (!this.initialisedOK) { + throw new TypeError("".concat(caller, " called for an element which has multiple instances of Choices initialised on it")); + } + }; + Choices.version = '11.0.0-rc7'; + return Choices; + }()); -/* harmony default export */ __webpack_exports__["default"] = ((_scripts_choices__WEBPACK_IMPORTED_MODULE_0___default())); + return Choices; -}(); -__webpack_exports__ = __webpack_exports__["default"]; -/******/ return __webpack_exports__; -/******/ })() -; -}); \ No newline at end of file +})); diff --git a/public/assets/scripts/choices.min.js b/public/assets/scripts/choices.min.js index af280949e..132039b83 100644 --- a/public/assets/scripts/choices.min.js +++ b/public/assets/scripts/choices.min.js @@ -1,2 +1,2 @@ -/*! For license information please see choices.min.js.LICENSE.txt */ -!function(){"use strict";var e={282:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.clearChoices=t.activateChoices=t.filterChoices=t.addChoice=void 0;var n=i(883);t.addChoice=function(e){var t=e.value,i=e.label,r=e.id,s=e.groupId,o=e.disabled,a=e.elementId,c=e.customProperties,l=e.placeholder,h=e.keyCode;return{type:n.ACTION_TYPES.ADD_CHOICE,value:t,label:i,id:r,groupId:s,disabled:o,elementId:a,customProperties:c,placeholder:l,keyCode:h}},t.filterChoices=function(e){return{type:n.ACTION_TYPES.FILTER_CHOICES,results:e}},t.activateChoices=function(e){return void 0===e&&(e=!0),{type:n.ACTION_TYPES.ACTIVATE_CHOICES,active:e}},t.clearChoices=function(){return{type:n.ACTION_TYPES.CLEAR_CHOICES}}},783:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.addGroup=void 0;var n=i(883);t.addGroup=function(e){var t=e.value,i=e.id,r=e.active,s=e.disabled;return{type:n.ACTION_TYPES.ADD_GROUP,value:t,id:i,active:r,disabled:s}}},464:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.highlightItem=t.removeItem=t.addItem=void 0;var n=i(883);t.addItem=function(e){var t=e.value,i=e.label,r=e.id,s=e.choiceId,o=e.groupId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:n.ACTION_TYPES.ADD_ITEM,value:t,label:i,id:r,choiceId:s,groupId:o,customProperties:a,placeholder:c,keyCode:l}},t.removeItem=function(e,t){return{type:n.ACTION_TYPES.REMOVE_ITEM,id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:n.ACTION_TYPES.HIGHLIGHT_ITEM,id:e,highlighted:t}}},137:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.setIsLoading=t.resetTo=t.clearAll=void 0;var n=i(883);t.clearAll=function(){return{type:n.ACTION_TYPES.CLEAR_ALL}},t.resetTo=function(e){return{type:n.ACTION_TYPES.RESET_TO,state:e}},t.setIsLoading=function(e){return{type:n.ACTION_TYPES.SET_IS_LOADING,isLoading:e}}},373:function(e,t,i){var n=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r=0?this._store.getGroupById(r):null;return this._store.dispatch((0,l.highlightItem)(i,!0)),t&&this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:i,value:o,label:c,groupValue:h&&h.value?h.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,s=void 0===r?"":r,o=e.label,a=void 0===o?"":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch((0,l.highlightItem)(t,!1)),this.passedElement.triggerEvent(d.EVENTS.highlightItem,{id:t,value:s,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(d.EVENTS.showDropdown,{})})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(d.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement||(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var s=e(this);if("function"==typeof Promise&&s instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return s})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(s))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof s));return this.setChoices(s,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt("".concat(e.id),10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch((0,a.clearChoices)()),this},e.prototype.clearStore=function(){return this._store.dispatch((0,h.clearAll)()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var s=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));s.length>=1&&(r=this._createChoicesFragment(s,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate("notice",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0,this._isSearching?(c="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate("notice",c,"no-results")):(c="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate("notice",c,"no-choices")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;return void 0===i&&(i=document.createDocumentFragment()),this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var s=n._getTemplate("choiceGroup",e);i.appendChild(s),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,o=s.renderSelectedChoices,a=s.searchResultLimit,c=s.renderChoiceLimit,l=this._isSearching?f.sortByScore:this.config.sorter,h=function(e){if("auto"!==o||r._isSelectOneElement||!e.selected){var i=r._getTemplate("choice",e,r.config.itemSelectText);t.appendChild(i)}},u=e;"auto"!==o||this._isSelectOneElement||(u=e.filter((function(e){return!e.selected})));var d=u.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,m=d.normalChoices;(this.config.shouldSort||this._isSearching)&&m.sort(l);var v=u.length,g=this._isSelectOneElement?n(n([],p,!0),m,!0):m;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var _=0;_=n){var o=r?this._searchChoices(e):0;this.passedElement.triggerEvent(d.EVENTS.search,{value:e,resultCount:o})}else s&&(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=(0,f.existsInArray)(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t==="".concat(i," "))return 0;var r=this._store.searchableChoices,s=t,c=Object.assign(this.config.fuseOptions,{keys:n([],this.config.searchFields,!0),includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch((0,a.filterChoices)(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,s=this.itemList.hasChildren(),o=String.fromCharCode(t),a=/[^\x00-\x1F]/.test(o),c=d.KEY_CODES.BACK_KEY,l=d.KEY_CODES.DELETE_KEY,h=d.KEY_CODES.ENTER_KEY,u=d.KEY_CODES.A_KEY,p=d.KEY_CODES.ESC_KEY,f=d.KEY_CODES.UP_KEY,m=d.KEY_CODES.DOWN_KEY,v=d.KEY_CODES.PAGE_UP_KEY,g=d.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=e.key.toLowerCase())),t){case u:return this._onSelectKey(e,s);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case g:return this._onDirectionKey(e,r);case l:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,s=this._canAddItem(r,n),o=d.KEY_CODES.BACK_KEY,c=d.KEY_CODES.DELETE_KEY;if(this._isTextElement)if(s.notice&&n){var l=this._getTemplate("notice",s.notice);this.dropdown.element.innerHTML=l.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0);else{var h=(i===o||i===c)&&t&&!t.value,u=!this._isTextElement&&this._isSearching,p=this._canSearch&&s.response;h&&u?(this._isSearching=!1,this._store.dispatch((0,a.activateChoices)(!0))):p&&this._handleSearch(this.input.rawValue)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=d.KEY_CODES.ENTER_KEY,s=n&&n.hasAttribute("data-button");if(this._isTextElement&&n&&n.value){var o=this.input.value;this._canAddItem(t,o).response&&(this.hideDropdown(!0),this._addItem({value:o}),this._triggerChange(o),this.clearInput())}if(s&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild(".".concat(this.config.classNames.highlightedState));a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=d.KEY_CODES.DOWN_KEY,s=d.KEY_CODES.PAGE_UP_KEY,o=d.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===o?1:-1,c="[data-choice-selectable]",l=void 0;if(n||i===o||i===s)l=a>0?this.dropdown.element.querySelector("".concat(c,":last-of-type")):this.dropdown.element.querySelector(c);else{var h=this.dropdown.element.querySelector(".".concat(this.config.classNames.highlightedState));l=h?(0,f.getAdjacentEl)(h,c,a):this.dropdown.element.querySelector(c)}l&&((0,f.isScrolledIntoView)(l,this.choiceList.element,a)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[d.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[d.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[d.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch((0,h.resetTo)(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll(".".concat(this.config.classNames.highlightedState))).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(d.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,s=void 0===r?-1:r,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,h=void 0===c?{}:c,u=e.placeholder,p=void 0!==u&&u,f=e.keyCode,m=void 0===f?-1:f,v="string"==typeof t?t.trim():t,g=this._store.items,_=n||v,y=s||-1,E=a>=0?this._store.getGroupById(a):null,b=g?g.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch((0,l.addItem)({value:v,label:_,id:b,choiceId:y,groupId:a,customProperties:h,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(b),this.passedElement.triggerEvent(d.EVENTS.addItem,{id:b,value:v,label:_,customProperties:h,groupValue:E&&E.value?E.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,s=e.choiceId,o=e.groupId,a=o&&o>=0?this._store.getGroupById(o):null;t&&s&&(this._store.dispatch((0,l.removeItem)(t,s)),this.passedElement.triggerEvent(d.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,s=void 0!==r&&r,o=e.isDisabled,c=void 0!==o&&o,l=e.groupId,h=void 0===l?-1:l,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var g=this._store.choices,_=n||t,y=g?g.length+1:1,E="".concat(this._baseId,"-").concat(this._idNames.itemChoice,"-").concat(y);this._store.dispatch((0,a.addChoice)({id:y,groupId:h,elementId:E,value:t,label:_,disabled:c,customProperties:d,placeholder:f,keyCode:v})),s&&this._addItem({value:t,label:_,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,s=void 0===r?"value":r,o=e.labelKey,a=void 0===o?"label":o,l=(0,f.isType)("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),h=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;l?(this._store.dispatch((0,c.addGroup)({value:i.label,id:h,active:!0,disabled:u})),l.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[s],label:(0,f.isType)("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:h,customProperties:e.customProperties,placeholder:e.placeholder})}))):this._store.dispatch((0,c.addGroup)({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r0?this.element.scrollTop+o-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,s=this.element.scrollTop,o=!1;t>0?(this._scrollDown(s,r,e),se&&(o=!0)),o&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},730:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0});var n=i(799),r=function(){function e(e){var t=e.element,i=e.classNames;if(this.element=t,this.classNames=i,!(t instanceof HTMLInputElement||t instanceof HTMLSelectElement))throw new TypeError("Invalid element passed");this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){this.element.classList.add(this.classNames.input),this.element.hidden=!0,this.element.tabIndex=-1;var e=this.element.getAttribute("style");e&&this.element.setAttribute("data-choice-orig-style",e),this.element.setAttribute("data-choice","active")},e.prototype.reveal=function(){this.element.classList.remove(this.classNames.input),this.element.hidden=!1,this.element.removeAttribute("tabindex");var e=this.element.getAttribute("data-choice-orig-style");e?(this.element.removeAttribute("data-choice-orig-style"),this.element.setAttribute("style",e)):this.element.removeAttribute("style"),this.element.removeAttribute("data-choice"),this.element.value=this.element.value},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.element.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.element.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){(0,n.dispatchEvent)(this.element,e,t)},e}();t.default=r},541:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,s=e.call(this,{element:i,classNames:n})||this;return s.delimiter=r,s}return r(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),t}(s(i(730)).default);t.default=o},982:function(e,t,i){var n,r=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,s=e.call(this,{element:i,classNames:n})||this;return s.template=r,s}return r(t,e),Object.defineProperty(t.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optionGroups",{get:function(){return Array.from(this.element.getElementsByTagName("OPTGROUP"))},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"options",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!1,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML="",this.element.appendChild(e)},t}(s(i(730)).default);t.default=o},883:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.SCROLLING_SPEED=t.SELECT_MULTIPLE_TYPE=t.SELECT_ONE_TYPE=t.TEXT_TYPE=t.KEY_CODES=t.ACTION_TYPES=t.EVENTS=void 0,t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice",unhighlightItem:"unhighlightItem"},t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL",RESET_TO:"RESET_TO",SET_IS_LOADING:"SET_IS_LOADING"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE="text",t.SELECT_ONE_TYPE="select-one",t.SELECT_MULTIPLE_TYPE="select-multiple",t.SCROLLING_SPEED=4},789:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CONFIG=t.DEFAULT_CLASSNAMES=void 0;var n=i(799);t.DEFAULT_CLASSNAMES={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,allowHTML:!0,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat((0,n.sanitise)(e),'"')},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES}},18:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},978:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},948:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},359:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},533:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},187:function(e,t,i){var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,r)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),r=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||n(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),r(i(18),t),r(i(978),t),r(i(948),t),r(i(359),t),r(i(285),t),r(i(533),t),r(i(287),t),r(i(132),t),r(i(837),t),r(i(598),t),r(i(369),t),r(i(37),t),r(i(47),t),r(i(923),t),r(i(876),t)},287:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},132:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},837:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},598:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},37:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},369:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},47:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},923:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},876:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},799:function(e,t){var i;Object.defineProperty(t,"__esModule",{value:!0}),t.parseCustomProperties=t.diff=t.cloneObject=t.existsInArray=t.dispatchEvent=t.sortByScore=t.sortByAlpha=t.strToEl=t.sanitise=t.isScrolledIntoView=t.getAdjacentEl=t.wrap=t.isType=t.getType=t.generateId=t.generateChars=t.getRandomNumber=void 0,t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return(0,t.getRandomNumber)(0,36).toString(36)})).join("")},t.generateId=function(e,i){var n=e.id||e.name&&"".concat(e.name,"-").concat((0,t.generateChars)(2))||(0,t.generateChars)(4);return n=n.replace(/(:|\.|\[|\]|,)/g,""),"".concat(i,"-").concat(n)},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&(0,t.getType)(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement("div")),e.parentNode&&(e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t)),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n="".concat(i>0?"next":"previous","ElementSibling"),r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return"string"!=typeof e?e:e.replace(/&/g,"&").replace(/>/g,">").replace(/-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(o.choiceId),10)&&(t.selected=!0),t})):e;case"REMOVE_ITEM":var a=n;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt("".concat(a.choiceId),10)&&(t.selected=!1),t})):e;case"FILTER_CHOICES":var c=n;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case"ACTIVATE_CHOICES":var l=n;return e.map((function(e){var t=e;return t.active=l.active,t}));case"CLEAR_CHOICES":return t.defaultState;default:return e}}},871:function(e,t){var i=this&&this.__spreadArray||function(e,t,i){if(i||2===arguments.length)for(var n,r=0,s=t.length;r0?"treeitem":"option"),Object.assign(E.dataset,{choice:"",id:d,value:p,selectText:i}),g?(E.classList.add(h),E.dataset.choiceDisabled="",E.setAttribute("aria-disabled","true")):(E.classList.add(c),E.dataset.choiceSelectable=""),E},input:function(e,t){var i=e.classNames,n=i.input,r=i.inputCloned,s=Object.assign(document.createElement("input"),{type:"search",name:"search_terms",className:"".concat(n," ").concat(r),autocomplete:"off",autocapitalize:"off",spellcheck:!1});return s.setAttribute("role","textbox"),s.setAttribute("aria-autocomplete","list"),s.setAttribute("aria-label",t),s},dropdown:function(e){var t=e.classNames,i=t.list,n=t.listDropdown,r=document.createElement("div");return r.classList.add(i,n),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var n,r=e.allowHTML,s=e.classNames,o=s.item,a=s.itemChoice,c=s.noResults,l=s.noChoices;void 0===i&&(i="");var h=[o,a];return"no-choices"===i?h.push(l):"no-results"===i&&h.push(c),Object.assign(document.createElement("div"),((n={})[r?"innerHTML":"innerText"]=t,n.className=h.join(" "),n))},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,s=e.disabled,o=new Option(t,i,!1,r);return n&&(o.dataset.customProperties="".concat(n)),o.disabled=!!s,o}};t.default=i},996:function(e){var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===i}(e)}(e)},i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function n(e,t){return!1!==t.clone&&t.isMergeableObject(e)?a((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function r(e,t,i){return e.concat(t).map((function(e){return n(e,i)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function o(e,t){try{return t in e}catch(e){return!1}}function a(e,i,c){(c=c||{}).arrayMerge=c.arrayMerge||r,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=n;var l=Array.isArray(i);return l===Array.isArray(e)?l?c.arrayMerge(e,i,c):function(e,t,i){var r={};return i.isMergeableObject(e)&&s(e).forEach((function(t){r[t]=n(e[t],i)})),s(t).forEach((function(s){(function(e,t){return o(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(o(e,s)&&i.isMergeableObject(t[s])?r[s]=function(e,t){if(!t.customMerge)return a;var i=t.customMerge(e);return"function"==typeof i?i:a}(s,i)(e[s],t[s],i):r[s]=n(t[s],i))})),r}(e,i,c):n(i,c)}a.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,i){return a(e,i,t)}),{})};var c=a;e.exports=c},221:function(e,t,i){function n(e){return Array.isArray?Array.isArray(e):"[object Array]"===l(e)}function r(e){return"string"==typeof e}function s(e){return"number"==typeof e}function o(e){return"object"==typeof e}function a(e){return null!=e}function c(e){return!e.trim().length}function l(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":Object.prototype.toString.call(e)}i.r(t),i.d(t,{default:function(){return R}});const h=Object.prototype.hasOwnProperty;class u{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=d(e);t+=i.weight,this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function d(e){let t=null,i=null,s=null,o=1,a=null;if(r(e)||n(e))s=e,t=p(e),i=f(e);else{if(!h.call(e,"name"))throw new Error("Missing name property in key");const n=e.name;if(s=n,h.call(e,"weight")&&(o=e.weight,o<=0))throw new Error((e=>`Property 'weight' in key '${e}' must be a positive integer`)(n));t=p(n),i=f(n),a=e.getFn}return{path:t,id:i,weight:o,src:s,getFn:a}}function p(e){return n(e)?e:e.split(".")}function f(e){return n(e)?e.join("."):e}var m={isCaseSensitive:!1,includeScore:!1,keys:[],shouldSort:!0,sortFn:(e,t)=>e.score===t.score?e.idx{if(a(e))if(t[u]){const d=e[t[u]];if(!a(d))return;if(u===t.length-1&&(r(d)||s(d)||function(e){return!0===e||!1===e||function(e){return o(e)&&null!==e}(e)&&"[object Boolean]"==l(e)}(d)))i.push(function(e){return null==e?"":function(e){if("string"==typeof e)return e;let t=e+"";return"0"==t&&1/e==-1/0?"-0":t}(e)}(d));else if(n(d)){c=!0;for(let e=0,i=d.length;e{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,r(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();r(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t{let o=t.getFn?t.getFn(e):this.getFn(e,t.path);if(a(o))if(n(o)){let e=[];const t=[{nestedArrIndex:-1,value:o}];for(;t.length;){const{nestedArrIndex:i,value:s}=t.pop();if(a(s))if(r(s)&&!c(s)){let t={v:s,i:i,n:this.norm.get(s)};e.push(t)}else n(s)&&s.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[s]=e}else if(r(o)&&!c(o)){let e={v:o,n:this.norm.get(o)};i.$[s]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function _(e,t,{getFn:i=m.getFn,fieldNormWeight:n=m.fieldNormWeight}={}){const r=new g({getFn:i,fieldNormWeight:n});return r.setKeys(e.map(d)),r.setSources(t),r.create(),r}function y(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:r=m.distance,ignoreLocation:s=m.ignoreLocation}={}){const o=t/e.length;if(s)return o;const a=Math.abs(n-i);return r?o+a/r:a?1:o}const E=32;function b(e){let t={};for(let i=0,n=e.length;i{this.chunks.push({pattern:e,alphabet:b(e),startIndex:t})},h=this.pattern.length;if(h>E){let e=0;const t=h%E,i=h-t;for(;e{const{isMatch:f,score:v,indices:g}=function(e,t,i,{location:n=m.location,distance:r=m.distance,threshold:s=m.threshold,findAllMatches:o=m.findAllMatches,minMatchCharLength:a=m.minMatchCharLength,includeMatches:c=m.includeMatches,ignoreLocation:l=m.ignoreLocation}={}){if(t.length>E)throw new Error("Pattern length exceeds max of 32.");const h=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=s,f=d;const v=a>1||c,g=v?Array(u):[];let _;for(;(_=e.indexOf(t,f))>-1;){let e=y(t,{currentLocation:_,expectedLocation:d,distance:r,ignoreLocation:l});if(p=Math.min(e,p),f=_+h,v){let e=0;for(;e=c;s-=1){let o=s-1,a=i[e.charAt(o)];if(v&&(g[o]=+!!a),_[s]=(_[s+1]<<1|1)&a,n&&(_[s]|=(b[s+1]|b[s])<<1|1|b[s+1]),_[s]&I&&(S=y(t,{errors:n,currentLocation:o,expectedLocation:d,distance:r,ignoreLocation:l}),S<=p)){if(p=S,f=o,f<=d)break;c=Math.max(1,2*d-f)}}if(y(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:r,ignoreLocation:l})>p)break;b=_}const C={isMatch:f>=0,score:Math.max(.001,S)};if(v){const e=function(e=[],t=m.minMatchCharLength){let i=[],n=-1,r=-1,s=0;for(let o=e.length;s=t&&i.push([n,r]),n=-1)}return e[s-1]&&s-n>=t&&i.push([n,s-1]),i}(g,a);e.length?c&&(C.indices=e):C.isMatch=!1}return C}(e,t,d,{location:n+p,distance:r,threshold:s,findAllMatches:o,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),h+=v,f&&g&&(l=[...l,...g])}));let d={isMatch:u,score:u?h/this.chunks.length:1};return u&&i&&(d.indices=l),d}}class O{constructor(e){this.pattern=e}static isMultiMatch(e){return I(e,this.multiRegex)}static isSingleMatch(e){return I(e,this.singleRegex)}search(){}}function I(e,t){const i=e.match(t);return i?i[1]:null}class C extends O{constructor(e,{location:t=m.location,threshold:i=m.threshold,distance:n=m.distance,includeMatches:r=m.includeMatches,findAllMatches:s=m.findAllMatches,minMatchCharLength:o=m.minMatchCharLength,isCaseSensitive:a=m.isCaseSensitive,ignoreLocation:c=m.ignoreLocation}={}){super(e),this._bitapSearch=new S(e,{location:t,threshold:i,distance:n,includeMatches:r,findAllMatches:s,minMatchCharLength:o,isCaseSensitive:a,ignoreLocation:c})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class T extends O{constructor(e){super(e)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],r=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+r,n.push([t,i-1]);const s=!!n.length;return{isMatch:s,score:s?0:1,indices:n}}}const L=[class extends O{constructor(e){super(e)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},T,class extends O{constructor(e){super(e)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends O{constructor(e){super(e)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},C],w=L.length,A=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,M=new Set([C.type,T.type]);const P=[];function x(e,t){for(let i=0,n=P.length;i!(!e.$and&&!e.$or),j=e=>({[N]:Object.keys(e).map((t=>({[t]:e[t]})))});function F(e,t,{auto:i=!0}={}){const s=e=>{let a=Object.keys(e);const c=(e=>!!e.$path)(e);if(!c&&a.length>1&&!D(e))return s(j(e));if((e=>!n(e)&&o(e)&&!D(e))(e)){const n=c?e.$path:a[0],s=c?e.$val:e[n];if(!r(s))throw new Error((e=>`Invalid value for key ${e}`)(n));const o={keyId:f(n),pattern:s};return i&&(o.searcher=x(s,t)),o}let l={children:[],operator:a[0]};return a.forEach((t=>{const i=e[t];n(i)&&i.forEach((e=>{l.children.push(s(e))}))})),l};return D(e)||(e=j(e)),s(e)}function k(e,t){const i=e.matches;t.matches=[],a(i)&&i.forEach((e=>{if(!a(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let r={indices:i,value:n};e.key&&(r.key=e.key.src),e.idx>-1&&(r.refIndex=e.idx),t.matches.push(r)}))}function K(e,t){t.score=e.score}class R{constructor(e,t={},i){this.options={...m,...t},this.options.useExtendedSearch,this._keyStore=new u(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof g))throw new Error("Incorrect 'index' type");this._myIndex=t||_(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){a(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=(()=>!1)){const t=[];for(let i=0,n=this._docs.length;i{let i=1;e.matches.forEach((({key:e,norm:n,score:r})=>{const s=e?e.weight:null;i*=Math.pow(0===r&&s?Number.EPSILON:r,(s||1)*(t?1:n))})),e.score=i}))}(l,{ignoreFieldNorm:c}),o&&l.sort(a),s(t)&&t>-1&&(l=l.slice(0,t)),function(e,t,{includeMatches:i=m.includeMatches,includeScore:n=m.includeScore}={}){const r=[];return i&&r.push(k),n&&r.push(K),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return r.length&&r.forEach((t=>{t(e,n)})),n}))}(l,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=x(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i:i,n:r})=>{if(!a(e))return;const{isMatch:s,score:o,indices:c}=t.searchIn(e);s&&n.push({item:e,idx:i,matches:[{score:o,value:e,norm:r,indices:c}]})})),n}_searchLogical(e){const t=F(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:r}=e,s=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:r});return s&&s.length?[{idx:n,item:t,matches:s}]:[]}const r=[];for(let s=0,o=e.children.length;s{if(a(e)){let o=i(t,e,n);o.length&&(r[n]||(r[n]={idx:n,item:e,matches:[]},s.push(r[n])),o.forEach((({matches:e})=>{r[n].matches.push(...e)})))}})),s}_searchObjectList(e){const t=x(e,this.options),{keys:i,records:n}=this._myIndex,r=[];return n.forEach((({$:e,i:n})=>{if(!a(e))return;let s=[];i.forEach(((i,n)=>{s.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),s.length&&r.push({idx:n,item:e,matches:s})})),r}_findMatches({key:e,value:t,searcher:i}){if(!a(t))return[];let r=[];if(n(t))t.forEach((({v:t,i:n,n:s})=>{if(!a(t))return;const{isMatch:o,score:c,indices:l}=i.searchIn(t);o&&r.push({score:c,key:e,value:t,idx:n,norm:s,indices:l})}));else{const{v:n,n:s}=t,{isMatch:o,score:a,indices:c}=i.searchIn(n);o&&r.push({score:a,key:e,value:n,norm:s,indices:c})}return r}}R.version="6.6.2",R.createIndex=_,R.parseIndex=function(e,{getFn:t=m.getFn,fieldNormWeight:i=m.fieldNormWeight}={}){const{keys:n,records:r}=e,s=new g({getFn:t,fieldNormWeight:i});return s.setKeys(n),s.setIndexRecords(r),s},R.config=m,R.parseQuery=F,function(...e){P.push(...e)}(class{constructor(e,{isCaseSensitive:t=m.isCaseSensitive,includeMatches:i=m.includeMatches,minMatchCharLength:n=m.minMatchCharLength,ignoreLocation:r=m.ignoreLocation,findAllMatches:s=m.findAllMatches,location:o=m.location,threshold:a=m.threshold,distance:c=m.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:s,ignoreLocation:r,location:o,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split("|").map((e=>{let i=e.trim().split(A).filter((e=>e&&!!e.trim())),n=[];for(let e=0,r=i.length;e/g,">").replace(/0?this.element.scrollTop+(e.offsetTop+e.offsetHeight)-(this.element.scrollTop+this.element.offsetHeight):e.offsetTop;requestAnimationFrame((function(){i._animateScroll(n,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t;this.element.scrollTop=e+(n>1?n:1)},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t;this.element.scrollTop=e-(n>1?n:1)},e.prototype._animateScroll=function(e,t){var i=this,n=this.element.scrollTop,s=!1;t>0?(this._scrollDown(n,4,e),ne&&(s=!0)),s&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}(),D=function(){function e(e){var t=e.classNames;this.element=e.element,this.classNames=t,this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){var e,t=this.element;(e=t.classList).add.apply(e,w(this.classNames.input)),t.hidden=!0,t.tabIndex=-1;var i=t.getAttribute("style");i&&t.setAttribute("data-choice-orig-style",i),t.setAttribute("data-choice","active")},e.prototype.reveal=function(){var e,t=this.element;(e=t.classList).remove.apply(e,w(this.classNames.input)),t.hidden=!1,t.removeAttribute("tabindex");var i=t.getAttribute("data-choice-orig-style");i?(t.removeAttribute("data-choice-orig-style"),t.setAttribute("style",i)):t.removeAttribute("style"),t.removeAttribute("data-choice")},e.prototype.enable=function(){var e=this.element;e.removeAttribute("disabled"),e.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){var e=this.element;e.setAttribute("disabled",""),e.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){var i;void 0===(i=t||{})&&(i=null),this.element.dispatchEvent(new CustomEvent(e,{detail:i,bubbles:!0,cancelable:!0}))},e}(),k=function(e){function i(){return null!==e&&e.apply(this,arguments)||this}return t(i,e),i}(D),P=function(e,t){return void 0===t&&(t=!0),void 0===e?t:!!e},j=function(e){if("string"==typeof e&&(e=e.split(" ").filter((function(e){return e.length}))),Array.isArray(e)&&e.length)return e},R=function(e,t){if("string"==typeof e)return R({value:e,label:e},!1);var i=e;if("choices"in i){if(!t)throw new TypeError("optGroup is not allowed");var n=i,s=n.choices.map((function(e){return R(e,!1)}));return{id:0,label:E(n.label)||n.value,active:!!s.length,disabled:!!n.disabled,choices:s}}var o=i;return{id:0,groupId:0,score:0,rank:0,value:o.value,label:o.label||o.value,active:P(o.active),selected:P(o.selected,!1),disabled:P(o.disabled,!1),placeholder:P(o.placeholder,!1),highlighted:!1,labelClass:j(o.labelClass),labelDescription:o.labelDescription,customProperties:o.customProperties}},K=function(e){return"SELECT"===e.tagName},H=function(e){function i(t){var i=t.template,n=t.extractPlaceholder,s=e.call(this,{element:t.element,classNames:t.classNames})||this;return s.template=i,s.extractPlaceholder=n,s}return t(i,e),Object.defineProperty(i.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),i.prototype.addOptions=function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){var n=e;if(!n.element){var s=t.template(n);i.appendChild(s),n.element=s}})),this.element.appendChild(i)},i.prototype.optionsAsChoices=function(){var e=this,t=[];return this.element.querySelectorAll(":scope > option, :scope > optgroup").forEach((function(i){!function(e){return"OPTION"===e.tagName}(i)?function(e){return"OPTGROUP"===e.tagName}(i)&&t.push(e._optgroupToChoice(i)):t.push(e._optionToChoice(i))})),t},i.prototype._optionToChoice=function(e){!e.hasAttribute("value")&&e.hasAttribute("placeholder")&&(e.setAttribute("value",""),e.value="");var t=e.dataset;return{id:0,groupId:0,score:0,rank:0,value:e.value,label:e.innerHTML,element:e,active:!0,selected:this.extractPlaceholder?e.selected:e.hasAttribute("selected"),disabled:e.disabled,highlighted:!1,placeholder:this.extractPlaceholder&&(!e.value||e.hasAttribute("placeholder")),labelClass:void 0!==t.labelClass?j(t.labelClass):void 0,labelDescription:void 0!==t.labelDescription?t.labelDescription:void 0,customProperties:A(t.customProperties)}},i.prototype._optgroupToChoice=function(e){var t=this,i=e.querySelectorAll("option"),n=Array.from(i).map((function(e){return t._optionToChoice(e)}));return{id:0,label:e.label||"",element:e,active:!!n.length,disabled:e.disabled,choices:n}},i}(D),B={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,closeDropdownOnSelect:"auto",singleModeForMultiSelect:!1,addChoices:!1,addItems:!0,addItemFilter:function(e){return!!e&&""!==e},removeItems:!0,removeItemButton:!1,removeItemButtonAlignLeft:!1,editItems:!1,allowHTML:!1,allowHtmlUserInput:!1,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:function(e,t){var i=e.label,n=t.label,s=void 0===n?t.value:n;return E(void 0===i?e.value:i).localeCompare(E(s),[],{sensitivity:"base",ignorePunctuation:!0,numeric:!0})},shadowRoot:null,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat(e,'"')},removeItemIconText:function(){return"Remove item"},removeItemLabelText:function(e){return"Remove item: ".concat(e)},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:{containerOuter:["choices"],containerInner:["choices__inner"],input:["choices__input"],inputCloned:["choices__input--cloned"],list:["choices__list"],listItems:["choices__list--multiple"],listSingle:["choices__list--single"],listDropdown:["choices__list--dropdown"],item:["choices__item"],itemSelectable:["choices__item--selectable"],itemDisabled:["choices__item--disabled"],itemChoice:["choices__item--choice"],description:["choices__description"],placeholder:["choices__placeholder"],group:["choices__group"],groupHeading:["choices__heading"],button:["choices__button"],activeState:["is-active"],focusState:["is-focused"],openState:["is-open"],disabledState:["is-disabled"],highlightedState:["is-highlighted"],selectedState:["is-selected"],flippedState:["is-flipped"],loadingState:["is-loading"],notice:["choices__notice"],addChoice:["choices__item--selectable","add-choice"],noResults:["has-no-results"],noChoices:["has-no-choices"]},appendGroupInSearch:!1},V={groups:function(e,t){var i=e,n=!0;switch(t.type){case l:i.push(t.group);break;case h:i=[];break;default:n=!1}return{state:i,update:n}},items:function(e,t){var i=e,n=!0;switch(t.type){case u:var s=t.item;s.selected=!0,(o=s.element)&&(o.selected=!0,o.setAttribute("selected","")),i.push(s),i.forEach((function(e){e.highlighted=!1}));break;case d:var o,a=t.item;if(a.selected=!1,o=a.element){o.selected=!1,o.removeAttribute("selected");var c=o.parentElement;c&&K(c)&&c.type===O&&(c.value="")}i=i.filter((function(e){return e.id!==a.id}));break;case r:i=i.filter((function(e){return e.id!==t.choice.id}));break;case p:var h=t;i.forEach((function(e){e.id===h.item.id&&(e.highlighted=h.highlighted)}));break;default:n=!1}return{state:i,update:n}},choices:function(e,t){var i=e,n=!0;switch(t.type){case o:i.push(t.choice);break;case r:i=i.filter((function(e){return e.id!==t.choice.id}));break;case u:case d:break;case a:var s=[];t.results.forEach((function(e){s[e.item.id]=e})),i.forEach((function(e){var t=s[e.id];void 0!==t?(e.score=t.score,e.rank=t.rank,e.active=!0):(e.score=0,e.rank=0,e.active=!1)}));break;case c:i.forEach((function(e){e.active=t.active}));break;case h:i=[];break;default:n=!1}return{state:i,update:n}}},$=function(){function e(){this._state=this.defaultState,this._listeners=[],this._txn=0}return Object.defineProperty(e.prototype,"defaultState",{get:function(){return{groups:[],items:[],choices:[]}},enumerable:!1,configurable:!0}),e.prototype.changeSet=function(e){return{groups:e,items:e,choices:e}},e.prototype.reset=function(){this._state=this.defaultState;var e=this.changeSet(!0);this._txn?this._changeSet=e:this._listeners.forEach((function(t){return t(e)}))},e.prototype.subscribe=function(e){this._listeners.push(e)},e.prototype.dispatch=function(e){var t=this._state,i=!1,n=this._changeSet||this.changeSet(!1);Object.keys(V).forEach((function(s){var o=V[s](t[s],e);o.update&&(i=!0,n[s]=!0,t[s]=o.state)})),i&&(this._txn?this._changeSet=n:this._listeners.forEach((function(e){return e(n)})))},e.prototype.withTxn=function(e){this._txn++;try{e()}finally{if(this._txn=Math.max(0,this._txn-1),!this._txn){var t=this._changeSet;t&&(this._changeSet=void 0,this._listeners.forEach((function(e){return e(t)})))}}},Object.defineProperty(e.prototype,"state",{get:function(){return this._state},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"items",{get:function(){return this.state.items},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"highlightedActiveItems",{get:function(){return this.items.filter((function(e){return!e.disabled&&e.active&&e.highlighted}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"choices",{get:function(){return this.state.choices},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeChoices",{get:function(){return this.choices.filter((function(e){return e.active}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"searchableChoices",{get:function(){return this.choices.filter((function(e){return!e.disabled&&!e.placeholder}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"groups",{get:function(){return this.state.groups},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeGroups",{get:function(){var e=this;return this.state.groups.filter((function(t){var i=t.active&&!t.disabled,n=e.state.choices.some((function(e){return e.active&&!e.disabled}));return i&&n}),[])},enumerable:!1,configurable:!0}),e.prototype.inTxn=function(){return this._txn>0},e.prototype.getChoiceById=function(e){return this.activeChoices.find((function(t){return t.id===e}))},e.prototype.getGroupById=function(e){return this.groups.find((function(t){return t.id===e}))},e}(),q="no-choices",W="no-results",G="add-choice";function U(e,t,i){return(t=function(e){var t=function(e){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var i=t.call(e,"string");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}function z(e,t){var i=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),i.push.apply(i,n)}return i}function J(e){for(var t=1;t`Missing ${e} property in key`,se=e=>`Property 'weight' in key '${e}' must be a positive integer`,oe=Object.prototype.hasOwnProperty;class re{constructor(e){this._keys=[],this._keyMap={};let t=0;e.forEach((e=>{let i=ae(e);this._keys.push(i),this._keyMap[i.id]=i,t+=i.weight})),this._keys.forEach((e=>{e.weight/=t}))}get(e){return this._keyMap[e]}keys(){return this._keys}toJSON(){return JSON.stringify(this._keys)}}function ae(e){let t=null,i=null,n=null,s=1,o=null;if(Q(e)||X(e))n=e,t=ce(e),i=he(e);else{if(!oe.call(e,"name"))throw new Error(ne("name"));const r=e.name;if(n=r,oe.call(e,"weight")&&(s=e.weight,s<=0))throw new Error(se(r));t=ce(r),i=he(r),o=e.getFn}return{path:t,id:i,weight:s,src:n,getFn:o}}function ce(e){return X(e)?e:e.split(".")}function he(e){return X(e)?e.join("."):e}const le={useExtendedSearch:!1,getFn:function(e,t){let i=[],n=!1;const s=(e,t,o)=>{if(ee(e))if(t[o]){const r=e[t[o]];if(!ee(r))return;if(o===t.length-1&&(Q(r)||Y(r)||function(e){return!0===e||!1===e||function(e){return Z(e)&&null!==e}(e)&&"[object Boolean]"==ie(e)}(r)))i.push(function(e){return null==e?"":function(e){if("string"==typeof e)return e;let t=e+"";return"0"==t&&1/e==-1/0?"-0":t}(e)}(r));else if(X(r)){n=!0;for(let e=0,i=r.length;ee.score===t.score?e.idx{this._keysMap[e.id]=t}))}create(){!this.isCreated&&this.docs.length&&(this.isCreated=!0,Q(this.docs[0])?this.docs.forEach(((e,t)=>{this._addString(e,t)})):this.docs.forEach(((e,t)=>{this._addObject(e,t)})),this.norm.clear())}add(e){const t=this.size();Q(e)?this._addString(e,t):this._addObject(e,t)}removeAt(e){this.records.splice(e,1);for(let t=e,i=this.size();t{let s=t.getFn?t.getFn(e):this.getFn(e,t.path);if(ee(s))if(X(s)){let e=[];const t=[{nestedArrIndex:-1,value:s}];for(;t.length;){const{nestedArrIndex:i,value:n}=t.pop();if(ee(n))if(Q(n)&&!te(n)){let t={v:n,i:i,n:this.norm.get(n)};e.push(t)}else X(n)&&n.forEach(((e,i)=>{t.push({nestedArrIndex:i,value:e})}))}i.$[n]=e}else if(Q(s)&&!te(s)){let e={v:s,n:this.norm.get(s)};i.$[n]=e}})),this.records.push(i)}toJSON(){return{keys:this.keys,records:this.records}}}function fe(e,t,{getFn:i=ue.getFn,fieldNormWeight:n=ue.fieldNormWeight}={}){const s=new pe({getFn:i,fieldNormWeight:n});return s.setKeys(e.map(ae)),s.setSources(t),s.create(),s}function me(e,{errors:t=0,currentLocation:i=0,expectedLocation:n=0,distance:s=ue.distance,ignoreLocation:o=ue.ignoreLocation}={}){const r=t/e.length;if(o)return r;const a=Math.abs(n-i);return s?r+a/s:a?1:r}const ve=32;function ge(e){let t={};for(let i=0,n=e.length;i{this.chunks.push({pattern:e,alphabet:ge(e),startIndex:t})},l=this.pattern.length;if(l>ve){let e=0;const t=l%ve,i=l-t;for(;e{const{isMatch:f,score:m,indices:v}=function(e,t,i,{location:n=ue.location,distance:s=ue.distance,threshold:o=ue.threshold,findAllMatches:r=ue.findAllMatches,minMatchCharLength:a=ue.minMatchCharLength,includeMatches:c=ue.includeMatches,ignoreLocation:h=ue.ignoreLocation}={}){if(t.length>ve)throw new Error("Pattern length exceeds max of 32.");const l=t.length,u=e.length,d=Math.max(0,Math.min(n,u));let p=o,f=d;const m=a>1||c,v=m?Array(u):[];let g;for(;(g=e.indexOf(t,f))>-1;){let e=me(t,{currentLocation:g,expectedLocation:d,distance:s,ignoreLocation:h});if(p=Math.min(e,p),f=g+l,m){let e=0;for(;e=c;o-=1){let r=o-1,a=i[e.charAt(r)];if(m&&(v[r]=+!!a),C[o]=(C[o+1]<<1|1)&a,n&&(C[o]|=(_[o+1]|_[o])<<1|1|_[o+1]),C[o]&E&&(y=me(t,{errors:n,currentLocation:r,expectedLocation:d,distance:s,ignoreLocation:h}),y<=p)){if(p=y,f=r,f<=d)break;c=Math.max(1,2*d-f)}}if(me(t,{errors:n+1,currentLocation:d,expectedLocation:d,distance:s,ignoreLocation:h})>p)break;_=C}const C={isMatch:f>=0,score:Math.max(.001,y)};if(m){const e=function(e=[],t=ue.minMatchCharLength){let i=[],n=-1,s=-1,o=0;for(let r=e.length;o=t&&i.push([n,s]),n=-1)}return e[o-1]&&o-n>=t&&i.push([n,o-1]),i}(v,a);e.length?c&&(C.indices=e):C.isMatch=!1}return C}(e,t,d,{location:n+p,distance:s,threshold:o,findAllMatches:r,minMatchCharLength:a,includeMatches:i,ignoreLocation:c});f&&(u=!0),l+=m,f&&v&&(h=[...h,...v])}));let d={isMatch:u,score:u?l/this.chunks.length:1};return u&&i&&(d.indices=h),d}}class ye{constructor(e){this.pattern=e}static isMultiMatch(e){return be(e,this.multiRegex)}static isSingleMatch(e){return be(e,this.singleRegex)}search(){}}function be(e,t){const i=e.match(t);return i?i[1]:null}class Ee extends ye{constructor(e,{location:t=ue.location,threshold:i=ue.threshold,distance:n=ue.distance,includeMatches:s=ue.includeMatches,findAllMatches:o=ue.findAllMatches,minMatchCharLength:r=ue.minMatchCharLength,isCaseSensitive:a=ue.isCaseSensitive,ignoreLocation:c=ue.ignoreLocation}={}){super(e),this._bitapSearch=new _e(e,{location:t,threshold:i,distance:n,includeMatches:s,findAllMatches:o,minMatchCharLength:r,isCaseSensitive:a,ignoreLocation:c})}static get type(){return"fuzzy"}static get multiRegex(){return/^"(.*)"$/}static get singleRegex(){return/^(.*)$/}search(e){return this._bitapSearch.searchIn(e)}}class Ce extends ye{constructor(e){super(e)}static get type(){return"include"}static get multiRegex(){return/^'"(.*)"$/}static get singleRegex(){return/^'(.*)$/}search(e){let t,i=0;const n=[],s=this.pattern.length;for(;(t=e.indexOf(this.pattern,i))>-1;)i=t+s,n.push([t,i-1]);const o=!!n.length;return{isMatch:o,score:o?0:1,indices:n}}}const Se=[class extends ye{constructor(e){super(e)}static get type(){return"exact"}static get multiRegex(){return/^="(.*)"$/}static get singleRegex(){return/^=(.*)$/}search(e){const t=e===this.pattern;return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},Ce,class extends ye{constructor(e){super(e)}static get type(){return"prefix-exact"}static get multiRegex(){return/^\^"(.*)"$/}static get singleRegex(){return/^\^(.*)$/}search(e){const t=e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,this.pattern.length-1]}}},class extends ye{constructor(e){super(e)}static get type(){return"inverse-prefix-exact"}static get multiRegex(){return/^!\^"(.*)"$/}static get singleRegex(){return/^!\^(.*)$/}search(e){const t=!e.startsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends ye{constructor(e){super(e)}static get type(){return"inverse-suffix-exact"}static get multiRegex(){return/^!"(.*)"\$$/}static get singleRegex(){return/^!(.*)\$$/}search(e){const t=!e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},class extends ye{constructor(e){super(e)}static get type(){return"suffix-exact"}static get multiRegex(){return/^"(.*)"\$$/}static get singleRegex(){return/^(.*)\$$/}search(e){const t=e.endsWith(this.pattern);return{isMatch:t,score:t?0:1,indices:[e.length-this.pattern.length,e.length-1]}}},class extends ye{constructor(e){super(e)}static get type(){return"inverse-exact"}static get multiRegex(){return/^!"(.*)"$/}static get singleRegex(){return/^!(.*)$/}search(e){const t=-1===e.indexOf(this.pattern);return{isMatch:t,score:t?0:1,indices:[0,e.length-1]}}},Ee],we=Se.length,Ie=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,Ae=new Set([Ee.type,Ce.type]);const xe=[];function Le(e,t){for(let i=0,n=xe.length;i!(!e[Oe]&&!e.$or),Ne=e=>({[Oe]:Object.keys(e).map((t=>({[t]:e[t]})))});function Fe(e,t,{auto:i=!0}={}){const n=e=>{let s=Object.keys(e);const o=(e=>!!e[Me])(e);if(!o&&s.length>1&&!Te(e))return n(Ne(e));if((e=>!X(e)&&Z(e)&&!Te(e))(e)){const n=o?e[Me]:s[0],r=o?e.$val:e[n];if(!Q(r))throw new Error((e=>`Invalid value for key ${e}`)(n));const a={keyId:he(n),pattern:r};return i&&(a.searcher=Le(r,t)),a}let r={children:[],operator:s[0]};return s.forEach((t=>{const i=e[t];X(i)&&i.forEach((e=>{r.children.push(n(e))}))})),r};return Te(e)||(e=Ne(e)),n(e)}function De(e,t){const i=e.matches;t.matches=[],ee(i)&&i.forEach((e=>{if(!ee(e.indices)||!e.indices.length)return;const{indices:i,value:n}=e;let s={indices:i,value:n};e.key&&(s.key=e.key.src),e.idx>-1&&(s.refIndex=e.idx),t.matches.push(s)}))}function ke(e,t){t.score=e.score}class Pe{constructor(e,t={},i){this.options=J(J({},ue),t),this._keyStore=new re(this.options.keys),this.setCollection(e,i)}setCollection(e,t){if(this._docs=e,t&&!(t instanceof pe))throw new Error("Incorrect 'index' type");this._myIndex=t||fe(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}add(e){ee(e)&&(this._docs.push(e),this._myIndex.add(e))}remove(e=()=>!1){const t=[];for(let i=0,n=this._docs.length;i{let i=1;e.matches.forEach((({key:e,norm:n,score:s})=>{const o=e?e.weight:null;i*=Math.pow(0===s&&o?Number.EPSILON:s,(o||1)*(t?1:n))})),e.score=i}))}(a,{ignoreFieldNorm:r}),s&&a.sort(o),Y(t)&&t>-1&&(a=a.slice(0,t)),function(e,t,{includeMatches:i=ue.includeMatches,includeScore:n=ue.includeScore}={}){const s=[];return i&&s.push(De),n&&s.push(ke),e.map((e=>{const{idx:i}=e,n={item:t[i],refIndex:i};return s.length&&s.forEach((t=>{t(e,n)})),n}))}(a,this._docs,{includeMatches:i,includeScore:n})}_searchStringList(e){const t=Le(e,this.options),{records:i}=this._myIndex,n=[];return i.forEach((({v:e,i:i,n:s})=>{if(!ee(e))return;const{isMatch:o,score:r,indices:a}=t.searchIn(e);o&&n.push({item:e,idx:i,matches:[{score:r,value:e,norm:s,indices:a}]})})),n}_searchLogical(e){const t=Fe(e,this.options),i=(e,t,n)=>{if(!e.children){const{keyId:i,searcher:s}=e,o=this._findMatches({key:this._keyStore.get(i),value:this._myIndex.getValueForItemAtKeyId(t,i),searcher:s});return o&&o.length?[{idx:n,item:t,matches:o}]:[]}const s=[];for(let o=0,r=e.children.length;o{if(ee(e)){let r=i(t,e,o);r.length&&(n[o]||(n[o]={idx:o,item:e,matches:[]},s.push(n[o])),r.forEach((({matches:e})=>{n[o].matches.push(...e)})))}})),s}_searchObjectList(e){const t=Le(e,this.options),{keys:i,records:n}=this._myIndex,s=[];return n.forEach((({$:e,i:n})=>{if(!ee(e))return;let o=[];i.forEach(((i,n)=>{o.push(...this._findMatches({key:i,value:e[n],searcher:t}))})),o.length&&s.push({idx:n,item:e,matches:o})})),s}_findMatches({key:e,value:t,searcher:i}){if(!ee(t))return[];let n=[];if(X(t))t.forEach((({v:t,i:s,n:o})=>{if(!ee(t))return;const{isMatch:r,score:a,indices:c}=i.searchIn(t);r&&n.push({score:a,key:e,value:t,idx:s,norm:o,indices:c})}));else{const{v:s,n:o}=t,{isMatch:r,score:a,indices:c}=i.searchIn(s);r&&n.push({score:a,key:e,value:s,norm:o,indices:c})}return n}}Pe.version="7.0.0",Pe.createIndex=fe,Pe.parseIndex=function(e,{getFn:t=ue.getFn,fieldNormWeight:i=ue.fieldNormWeight}={}){const{keys:n,records:s}=e,o=new pe({getFn:t,fieldNormWeight:i});return o.setKeys(n),o.setIndexRecords(s),o},Pe.config=ue,Pe.parseQuery=Fe,function(...e){xe.push(...e)}(class{constructor(e,{isCaseSensitive:t=ue.isCaseSensitive,includeMatches:i=ue.includeMatches,minMatchCharLength:n=ue.minMatchCharLength,ignoreLocation:s=ue.ignoreLocation,findAllMatches:o=ue.findAllMatches,location:r=ue.location,threshold:a=ue.threshold,distance:c=ue.distance}={}){this.query=null,this.options={isCaseSensitive:t,includeMatches:i,minMatchCharLength:n,findAllMatches:o,ignoreLocation:s,location:r,threshold:a,distance:c},this.pattern=t?e:e.toLowerCase(),this.query=function(e,t={}){return e.split("|").map((e=>{let i=e.trim().split(Ie).filter((e=>e&&!!e.trim())),n=[];for(let e=0,s=i.length;e0;return M.setAttribute("role",P?"treeitem":"option"),P&&(k.groupId="".concat(y)),k.choice="",k.id=g,k.value=O,k.selectText=i,S&&(k.labelClass=w(S).join(" ")),I&&(k.labelDescription=I),A?((r=M.classList).add.apply(r,w(f)),k.choiceDisabled="",M.setAttribute("aria-disabled","true")):((a=M.classList).add.apply(a,w(d)),k.choiceSelectable=""),M},input:function(e,t){var i=e.classNames,n=i.input,s=i.inputCloned,o=e.labelId,r=document.createElement("input");return r.type="search",r.className="".concat(w(n).join(" ")," ").concat(w(s).join(" ")),r.autocomplete="off",r.autocapitalize="off",r.spellcheck=!1,r.setAttribute("role","textbox"),r.setAttribute("aria-autocomplete","list"),t?r.setAttribute("aria-label",t):o||Ke(this._docRoot,this.passedElement.element.id,r),r},dropdown:function(e){var t,i,n=e.classNames,s=n.list,o=n.listDropdown,r=document.createElement("div");return(t=r.classList).add.apply(t,w(s)),(i=r.classList).add.apply(i,w(o)),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var s=e.classNames,o=s.itemChoice,r=s.addChoice,a=s.noResults,c=s.noChoices,h=s.notice;void 0===i&&(i="");var l=n(n(n([],w(s.item),!0),w(o),!0),w(h),!0);switch(i){case G:l.push.apply(l,w(r));break;case W:l.push.apply(l,w(a));break;case q:l.push.apply(l,w(c))}var u=document.createElement("div");return u.innerHTML=t,u.className=l.join(" "),i===G&&(u.dataset.choiceSelectable="",u.dataset.choice=""),u},option:function(e){var t=E(e.label),i=new Option(t,e.value,!1,e.selected),n=e.labelClass,s=e.labelDescription;return n&&(i.dataset.labelClass=w(n).join(" ")),s&&(i.dataset.labelDescription=s),Re(i,e.customProperties),i.disabled=e.disabled,e.selected&&i.setAttribute("selected",""),i}},Be="-ms-scroll-limit"in document.documentElement.style&&"-ms-ime-align"in document.documentElement.style,Ve={},$e=function(e){if(e){var t=e.dataset.id;return t?parseInt(t,10):void 0}};return function(){function e(t,n){void 0===t&&(t="[data-choice]"),void 0===n&&(n={});var s=this;this.initialisedOK=void 0,this._hasNonChoicePlaceholder=!1,this._lastAddedChoiceId=0,this._lastAddedGroupId=0;var o=e.defaults;this.config=i(i(i({},o.allOptions),o.options),n),f.forEach((function(e){s.config[e]=i(i(i({},o.allOptions[e]),o.options[e]),n[e])}));var r=this.config;r.silent||this._validateConfig();var a=r.shadowRoot||document.documentElement;this._docRoot=a;var c="string"==typeof t?a.querySelector(t):t;if(!c||"object"!=typeof c||"INPUT"!==c.tagName&&!K(c)){if(!c&&"string"==typeof t)throw TypeError("Selector ".concat(t," failed to find an element"));throw TypeError("Expected one of the following types text|select-one|select-multiple")}if(this._elementType=c.type,this._isTextElement=this._elementType===L,(this._isTextElement||1!==r.maxItemCount)&&(r.singleModeForMultiSelect=!1),r.singleModeForMultiSelect&&(this._elementType=M),this._isSelectOneElement=this._elementType===O,this._isSelectMultipleElement=this._elementType===M,this._isSelectElement=this._isSelectOneElement||this._isSelectMultipleElement,this._canAddUserChoices=this._isTextElement&&r.addItems||this._isSelectElement&&r.addChoices,["auto","always"].includes("".concat(r.renderSelectedChoices))||(r.renderSelectedChoices="auto"),r.closeDropdownOnSelect="auto"===r.closeDropdownOnSelect?this._isTextElement||this._isSelectOneElement||r.singleModeForMultiSelect:P(r.closeDropdownOnSelect),r.placeholder&&(r.placeholderValue?this._hasNonChoicePlaceholder=!0:c.dataset.placeholder&&(this._hasNonChoicePlaceholder=!0,r.placeholderValue=c.dataset.placeholder)),n.addItemFilter&&"function"!=typeof n.addItemFilter){var h=n.addItemFilter instanceof RegExp?n.addItemFilter:new RegExp(n.addItemFilter);r.addItemFilter=h.test.bind(h)}if(this.passedElement=this._isTextElement?new k({element:c,classNames:r.classNames}):new H({element:c,classNames:r.classNames,template:function(e){return s._templates.option(e)},extractPlaceholder:r.placeholder&&!this._hasNonChoicePlaceholder}),this.initialised=!1,this._store=new $,this._currentValue="",r.searchEnabled=!this._isTextElement&&r.searchEnabled||this._elementType===M,this._canSearch=r.searchEnabled,this._isScrollingOnIe=!1,this._highlightPosition=0,this._wasTap=!0,this._placeholderValue=this._generatePlaceholderValue(),this._baseId=function(e){var t=e.id||e.name&&"".concat(e.name,"-").concat(v(2))||v(4);return t=t.replace(/(:|\.|\[|\]|,)/g,""),"".concat("choices-","-").concat(t)}(c),this._direction=this.passedElement.dir,!this._direction){var l=window.getComputedStyle(this.passedElement.element).direction;l!==window.getComputedStyle(document.documentElement).direction&&(this._direction=l)}if(this._idNames={itemChoice:"item-choice"},this._templates=o.templates,this._render=this._render.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this),this._onKeyUp=this._onKeyUp.bind(this),this._onKeyDown=this._onKeyDown.bind(this),this._onInput=this._onInput.bind(this),this._onClick=this._onClick.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onMouseDown=this._onMouseDown.bind(this),this._onMouseOver=this._onMouseOver.bind(this),this._onFormReset=this._onFormReset.bind(this),this._onSelectKey=this._onSelectKey.bind(this),this._onEnterKey=this._onEnterKey.bind(this),this._onEscapeKey=this._onEscapeKey.bind(this),this._onDirectionKey=this._onDirectionKey.bind(this),this._onDeleteKey=this._onDeleteKey.bind(this),this.passedElement.isActive)return r.silent||console.warn("Trying to initialise Choices on element already initialised",{element:t}),this.initialised=!0,void(this.initialisedOK=!1);this.init(),this._initialItems=this._store.items.map((function(e){return e.value}))}return Object.defineProperty(e,"defaults",{get:function(){return Object.preventExtensions({get options(){return Ve},get allOptions(){return B},get templates(){return He}})},enumerable:!1,configurable:!0}),e.prototype.init=function(){if(!this.initialised&&void 0===this.initialisedOK){this._searcher=new je(this.config),this._loadChoices(),this._createTemplates(),this._createElements(),this._createStructure(),this._isTextElement&&!this.config.addItems||this.passedElement.element.hasAttribute("disabled")||this.passedElement.element.closest("fieldset:disabled")?this.disable():(this.enable(),this._addEventListeners()),this._initStore(),this.initialised=!0,this.initialisedOK=!0;var e=this.config.callbackOnInit;e&&"function"==typeof e&&e.call(this)}},e.prototype.destroy=function(){this.initialised&&(this._removeEventListeners(),this.passedElement.reveal(),this.containerOuter.unwrap(this.passedElement.element),this._store._listeners=[],this.clearStore(),this._stopSearch(),this._templates=e.defaults.templates,this.initialised=!1,this.initialisedOK=void 0)},e.prototype.enable=function(){var e=this.passedElement,t=this.containerOuter;return e.isDisabled&&e.enable(),t.isDisabled&&(this._addEventListeners(),this.input.enable(),t.enable(),this._render()),this},e.prototype.disable=function(){var e=this.passedElement,t=this.containerOuter;return e.isDisabled||e.disable(),t.isDisabled||(this._removeEventListeners(),this.input.disable(),t.disable(),this._render()),this},e.prototype.highlightItem=function(e,t){if(void 0===t&&(t=!0),!e||!e.id)return this;var i=this._store.choices.find((function(t){return t.id===e.id}));return!i||i.highlighted||(this._store.dispatch(m(i,!0)),t&&this.passedElement.triggerEvent("highlightItem",this._getChoiceForOutput(i))),this},e.prototype.unhighlightItem=function(e,t){if(void 0===t&&(t=!0),!e||!e.id)return this;var i=this._store.choices.find((function(t){return t.id===e.id}));return i&&i.highlighted?(this._store.dispatch(m(i,!1)),t&&this.passedElement.triggerEvent("highlightItem",this._getChoiceForOutput(i)),this):this},e.prototype.highlightAll=function(){var e=this;return this._store.withTxn((function(){e._store.items.forEach((function(t){return e.highlightItem(t)}))})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.withTxn((function(){e._store.items.forEach((function(t){return e.unhighlightItem(t)}))})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.withTxn((function(){t._store.items.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)}))})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.withTxn((function(){t._store.items.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)}))})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.withTxn((function(){t._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)}))})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent("showDropdown")})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent("hideDropdown")})),this):this},e.prototype.getValue=function(e){var t=this;void 0===e&&(e=!1);var i=this._store.items.reduce((function(i,n){var s=e?n.value:t._getChoiceForOutput(n);return i.push(s),i}),[]);return this._isSelectOneElement||this.config.singleModeForMultiSelect?i[0]:i},e.prototype.setValue=function(e){var t=this;return this.initialisedOK?(this._store.withTxn((function(){e.forEach((function(e){e&&t._addChoice(R(e,!1))}))})),this._searcher.reset(),this):(this._warnChoicesInitFailed("setValue"),this)},e.prototype.setChoiceByValue=function(e){var t=this;return this.initialisedOK?(this._isTextElement||(this._store.withTxn((function(){(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)}))})),this._searcher.reset()),this):(this._warnChoicesInitFailed("setChoiceByValue"),this)},e.prototype.setChoices=function(e,t,n,s){var o=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===n&&(n="label"),void 0===s&&(s=!1),!this.initialisedOK)return this._warnChoicesInitFailed("setChoices"),this;if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(s&&this.clearChoices(),"function"==typeof e){var r=e(this);if("function"==typeof Promise&&r instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return o._handleLoadingState(!0)})).then((function(){return r})).then((function(e){return o.setChoices(e,t,n,s)})).catch((function(e){o.config.silent||console.error(e)})).then((function(){return o._handleLoadingState(!1)})).then((function(){return o}));if(!Array.isArray(r))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof r));return this.setChoices(r,t,n,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._store.withTxn((function(){var s="value"===t,r="label"===n;e.forEach((function(e){if("choices"in e){var a=e;r||(a=i(i({},a),{label:a[n]})),o._addGroup(R(a,!0))}else{var c=e;r&&s||(c=i(i({},c),{value:c[t],label:c[n]})),o._addChoice(R(c,!1))}}))})),this._searcher.reset(),this},e.prototype.refresh=function(e,t,i){var n=this;return void 0===e&&(e=!1),void 0===t&&(t=!1),void 0===i&&(i=!1),this._isSelectElement?(this._store.withTxn((function(){var s=n.passedElement.optionsAsChoices(),o={};i||n._store.items.forEach((function(e){e.id&&e.active&&e.selected&&!e.disabled&&(o[e.value]=!0)})),s.forEach((function(e){if(!("choices"in e)){var t=e;i?t.selected=!1:o[t.value]&&(t.selected=!0)}})),n.clearStore(),n._addPredefinedChoices(s,t,e),n._isSearching&&n._searchChoices(n.input.value)})),this):(this.config.silent||console.warn("refresh method can only be used on choices backed by a element'); + } + return this; + } + this._store.withTxn(function () { + var choicesFromOptions = _this.passedElement.optionsAsChoices(); + var items = _this._store.items; + // Build the list of items which require preserving + var existingItems = {}; + if (!deselectAll) { + items.forEach(function (choice) { + if (choice.id && choice.active && choice.selected && !choice.disabled) { + existingItems[choice.value] = true; + } + }); + } + choicesFromOptions.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + return; + } + var choice = groupOrChoice; + if (deselectAll) { + choice.selected = false; + } + else if (existingItems[choice.value]) { + choice.selected = true; + } + }); + _this.clearStore(); + /* @todo only generate add events for the added options instead of all + if (withEvents) { + items.forEach((choice) => { + if (existingItems[choice.value]) { + this.passedElement.triggerEvent( + EventType.removeItem, + this._getChoiceForEvent(choice), + ); + } + }); + } + */ + // load new choices & items + _this._addPredefinedChoices(choicesFromOptions, selectFirstOption, withEvents); + // re-do search if required + if (_this._isSearching) { + _this._searchChoices(_this.input.value); + } + }); + return this; + }; + Choices.prototype.removeChoice = function (value) { + var choice = this._store.choices.find(function (c) { return c.value === value; }); + if (!choice) { + return this; + } + this._store.dispatch(removeChoice(choice)); + // @todo integrate with Store + this._searcher.reset(); + if (choice.selected) { + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.clearChoices = function () { + this.passedElement.element.innerHTML = ''; + this._store.dispatch(clearChoices()); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearStore = function () { + this._store.reset(); + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearInput = function () { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); + this._clearNotice(); + if (this._isSearching) { + this._stopSearch(); + } + return this; + }; + Choices.prototype._validateConfig = function () { + var config = this.config; + var invalidConfigOptions = diff(config, DEFAULT_CONFIG); + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + if (config.allowHTML && config.allowHtmlUserInput) { + if (config.addItems) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addItems all being true is strongly not recommended and may lead to XSS attacks'); + } + if (config.addChoices) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addChoices all being true is strongly not recommended and may lead to XSS attacks'); + } + } + }; + Choices.prototype._render = function (changes) { + if (changes === void 0) { changes = { choices: true, groups: true, items: true }; } + if (this._store.inTxn()) { + return; + } + if (this._isSelectElement) { + if (changes.choices || changes.groups) { + this._renderChoices(); + } + } + if (changes.items) { + this._renderItems(); + } + }; + Choices.prototype._renderChoices = function () { + var _this = this; + this.choiceList.clear(); + if (!this._canAddItems()) { + return; // block rendering choices if the input limit is reached. + } + var config = this.config; + var _a = this._store, activeGroups = _a.activeGroups, activeChoices = _a.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + var noChoices = true; + if (activeChoices.length) { + if (config.resetScrollPosition) { + requestAnimationFrame(function () { return _this.choiceList.scrollToTop(); }); + } + // If we have grouped options + if (activeGroups.length && !this._isSearching) { + if (!this._hasNonChoicePlaceholder) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { return activeChoice.placeholder && activeChoice.groupId === -1; }); + if (activePlaceholders.length) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + } + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } + else { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } + noChoices = !choiceListFragment.childNodes.length; + } + var notice = this._notice; + if (noChoices) { + if (!notice) { + this._notice = { + text: resolveStringFunction(config.noChoicesText), + type: NoticeTypes.noChoices, + }; + } + } + else if (notice && notice.type === NoticeTypes.noChoices) { + this._notice = undefined; + } + this._renderNotice(); + if (!noChoices) { + this.choiceList.element.append(choiceListFragment); + this._highlightChoice(); + } + }; + Choices.prototype._renderItems = function () { + var items = this._store.items || []; + this.itemList.clear(); + // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) + var itemListFragment = this._createItemsFragment(items); + // If we have items to add, append them + if (itemListFragment.childNodes.length) { + this.itemList.element.append(itemListFragment); + } + }; + Choices.prototype._createGroupsFragment = function (groups, choices, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + var config = this.config; + var getGroupChoices = function (group) { + return choices.filter(function (choice) { + if (_this._isSelectOneElement) { + return choice.groupId === group.id; + } + return choice.groupId === group.id && (config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; + // If sorting is enabled, filter groups + if (config.shouldSort) { + groups.sort(config.sorter); + } + // Add Choices without group first, regardless of sort, otherwise they won't be distinguishable + // from the last group + var choicesWithoutGroup = choices.filter(function (c) { return !c.groupId; }); + if (choicesWithoutGroup.length) { + this._createChoicesFragment(choicesWithoutGroup, fragment, false); + } + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + if (groupChoices.length) { + var dropdownGroup = _this._templates.choiceGroup(_this.config, group); + fragment.appendChild(dropdownGroup); + _this._createChoicesFragment(groupChoices, fragment, true); + } + }); + return fragment; + }; + Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + if (withinGroup === void 0) { withinGroup = false; } + // Create a fragment to store our list items (so we don't have to update the DOM for each item) + var _a = this, config = _a.config, isSearching = _a._isSearching, isSelectOneElement = _a._isSelectOneElement; + var searchResultLimit = config.searchResultLimit, renderChoiceLimit = config.renderChoiceLimit; + var groupLookup = []; + var appendGroupInSearch = config.appendGroupInSearch && isSearching; + if (appendGroupInSearch) { + this._store.groups.forEach(function (group) { + groupLookup[group.id] = group.label; + }); + } + if (this._isSelectElement) { + var backingOptions = choices.filter(function (choice) { return !choice.element; }); + if (backingOptions.length) { + this.passedElement.addOptions(backingOptions); + } + } + var skipSelected = config.renderSelectedChoices === 'auto' && !isSelectOneElement; + var placeholderChoices = []; + var normalChoices = []; + choices.forEach(function (choice) { + if ((isSearching && !choice.rank) || (skipSelected && choice.selected)) { + return; + } + if (_this._hasNonChoicePlaceholder || !choice.placeholder) { + normalChoices.push(choice); + } + else { + placeholderChoices.push(choice); + } + }); + if (isSearching) { + // sortByRank is used to ensure stable sorting, as scores are non-unique + // this additionally ensures fuseOptions.sortFn is not ignored + normalChoices.sort(sortByRank); + } + else if (config.shouldSort) { + normalChoices.sort(config.sorter); + } + var sortedChoices = isSelectOneElement && placeholderChoices.length ? __spreadArray(__spreadArray([], placeholderChoices, true), normalChoices, true) : normalChoices; + var choiceLimit = sortedChoices.length; + var limit = choiceLimit; + if (isSearching && searchResultLimit > 0) { + limit = searchResultLimit; + } + else if (renderChoiceLimit > 0 && !withinGroup) { + limit = renderChoiceLimit; + } + if (limit < choiceLimit) { + choiceLimit = limit; + } + choiceLimit--; + // Add each choice to dropdown within range + sortedChoices.every(function (choice, index) { + var dropdownItem = _this._templates.choice(config, choice, config.itemSelectText); + if (appendGroupInSearch && choice.groupId > 0) { + var groupName = groupLookup[choice.groupId]; + if (groupName) { + dropdownItem.innerHTML += " (".concat(groupName, ")"); + } + } + fragment.appendChild(dropdownItem); + return index < choiceLimit; + }); + return fragment; + }; + Choices.prototype._createItemsFragment = function (items, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + // Create fragment to add elements to + var config = this.config; + var shouldSortItems = config.shouldSortItems, sorter = config.sorter, removeItemButton = config.removeItemButton, delimiter = config.delimiter; + // If sorting is enabled, filter items + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sorter); + } + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items.map(function (_a) { + var value = _a.value; + return value; + }).join(delimiter); + } + var addItemToFragment = function (item) { + // Create new list element + var listItem = _this._templates.item(config, item, removeItemButton); + // Append it to list + fragment.appendChild(listItem); + }; + // Add each list item to list + items.forEach(addItemToFragment); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder && !items.length) { + addItemToFragment(mapInputToChoice({ + selected: true, + value: '', + label: this.config.placeholderValue || '', + active: true, + placeholder: true, + }, false)); + } + return fragment; + }; + Choices.prototype._displayNotice = function (text, type, openDropdown) { + if (openDropdown === void 0) { openDropdown = true; } + var oldNotice = this._notice; + if (oldNotice && + ((oldNotice.type === type && oldNotice.text === text) || + (oldNotice.type === NoticeTypes.addChoice && + (type === NoticeTypes.noResults || type === NoticeTypes.noChoices)))) { + if (openDropdown) { + this.showDropdown(true); + } + return; + } + this._clearNotice(); + this._notice = text + ? { + text: text, + type: type, + } + : undefined; + this._renderNotice(); + if (openDropdown && text) { + this.showDropdown(true); + } + }; + Choices.prototype._clearNotice = function () { + if (!this._notice) { + return; + } + var noticeElement = this.choiceList.element.querySelector(getClassNamesSelector(this.config.classNames.notice)); + if (noticeElement) { + noticeElement.remove(); + } + this._notice = undefined; + }; + Choices.prototype._renderNotice = function () { + var noticeConf = this._notice; + if (noticeConf) { + var notice = this._templates.notice(this.config, noticeConf.text, noticeConf.type); + this.choiceList.prepend(notice); + } + }; + Choices.prototype._getChoiceForOutput = function (choice, keyCode) { + if (!choice) { + return undefined; + } + var group = choice.groupId > 0 ? this._store.getGroupById(choice.groupId) : null; + return { + id: choice.id, + highlighted: choice.highlighted, + labelClass: choice.labelClass, + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + disabled: choice.disabled, + active: choice.active, + label: choice.label, + placeholder: choice.placeholder, + value: choice.value, + groupValue: group && group.label ? group.label : undefined, + element: choice.element, + keyCode: keyCode, + }; + }; + Choices.prototype._triggerChange = function (value) { + if (value === undefined || value === null) { + return; + } + this.passedElement.triggerEvent("change" /* EventType.change */, { + value: value, + }); + }; + Choices.prototype._handleButtonAction = function (element) { + var items = this._store.items; + if (!items.length || !this.config.removeItems || !this.config.removeItemButton) { + return; + } + var id = element && parseDataSetId(element.parentNode); + var itemToRemove = id && items.find(function (item) { return item.id === id; }); + if (!itemToRemove) { + return; + } + // Remove item associated with button + this._removeItem(itemToRemove); + this._triggerChange(itemToRemove.value); + if (this._isSelectOneElement && !this._hasNonChoicePlaceholder) { + var placeholderChoice = this._store.choices.reverse().find(function (choice) { return !choice.disabled && choice.placeholder; }); + if (placeholderChoice) { + this._addItem(placeholderChoice); + if (placeholderChoice.value) { + this._triggerChange(placeholderChoice.value); + } + } + } + }; + Choices.prototype._handleItemAction = function (element, hasShiftKey) { + var _this = this; + if (hasShiftKey === void 0) { hasShiftKey = false; } + var items = this._store.items; + if (!items.length || !this.config.removeItems || this._isSelectOneElement) { + return; + } + var id = parseDataSetId(element); + if (!id) { + return; + } + // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + items.forEach(function (item) { + if (item.id === id && !item.highlighted) { + _this.highlightItem(item); + } + else if (!hasShiftKey && item.highlighted) { + _this.unhighlightItem(item); + } + }); + // Focus input as without focus, a user cannot do anything with a + // highlighted item + this.input.focus(); + }; + Choices.prototype._handleChoiceAction = function (element) { + var _this = this; + // If we are clicking on an option + var id = parseDataSetId(element); + var choice = id && this._store.getChoiceById(id); + if (!choice || choice.disabled) { + return false; + } + var hasActiveDropdown = this.dropdown.isActive; + if (!choice.selected) { + if (!this._canAddItems()) { + return true; // causes _onEnterKey to early out + } + this._store.withTxn(function () { + _this._addItem(choice, true, true); + _this.clearInput(); + _this.unhighlightAll(); + }); + this._triggerChange(choice.value); + } + // We want to close the dropdown if we are dealing with a single select box + if (hasActiveDropdown && this.config.closeDropdownOnSelect) { + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + return true; + }; + Choices.prototype._handleBackspace = function (items) { + var config = this.config; + if (!config.removeItems || !items.length) { + return; + } + var lastItem = items[items.length - 1]; + var hasHighlightedItems = items.some(function (item) { return item.highlighted; }); + // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items + if (config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + this._removeItem(lastItem); + this._triggerChange(lastItem.value); + } + else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } + this.removeHighlightedItems(true); + } + }; + Choices.prototype._loadChoices = function () { + var _a; + var config = this.config; + if (this._isTextElement) { + // Assign preset items from passed object first + this._presetChoices = config.items.map(function (e) { return mapInputToChoice(e, false); }); + // Add any values passed from attribute + var value = this.passedElement.value; + if (value) { + var elementItems = value + .split(config.delimiter) + .map(function (e) { return mapInputToChoice(e, false); }); + this._presetChoices = this._presetChoices.concat(elementItems); + } + this._presetChoices.forEach(function (choice) { + choice.selected = true; + }); + } + else if (this._isSelectElement) { + // Assign preset choices from passed object + this._presetChoices = config.choices.map(function (e) { return mapInputToChoice(e, true); }); + // Create array of choices from option elements + var choicesFromOptions = this.passedElement.optionsAsChoices(); + if (choicesFromOptions) { + (_a = this._presetChoices).push.apply(_a, choicesFromOptions); + } + } + }; + Choices.prototype._handleLoadingState = function (setLoading) { + if (setLoading === void 0) { setLoading = true; } + var config = this.config; + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); + if (this._isSelectOneElement) { + this.itemList.clear(); + this.itemList.element.append(this._templates.placeholder(config, config.loadingText)); + } + else { + this.input.placeholder = config.loadingText; + } + } + else { + this.enable(); + this.containerOuter.removeLoadingState(); + if (!this._isSelectOneElement) { + this.input.placeholder = this._placeholderValue || ''; + } + } + }; + Choices.prototype._handleSearch = function (value) { + if (!this.input.isFocussed) { + return; + } + var choices = this._store.choices; + var _a = this.config, searchFloor = _a.searchFloor, searchChoices = _a.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { return !option.active; }); + // Check that we have a value to search and the input was an alphanumeric character + if (value !== null && typeof value !== 'undefined' && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; + if (resultCount !== null) { + // Trigger search event + this.passedElement.triggerEvent("search" /* EventType.search */, { + value: value, + resultCount: resultCount, + }); + } + } + else if (hasUnactiveChoices) { + this._stopSearch(); + } + }; + Choices.prototype._canAddItems = function () { + var config = this.config; + var maxItemCount = config.maxItemCount, maxItemText = config.maxItemText; + if (!config.singleModeForMultiSelect && maxItemCount > 0 && maxItemCount <= this._store.items.length) { + this._displayNotice(typeof maxItemText === 'function' ? maxItemText(maxItemCount) : maxItemText, NoticeTypes.addChoice); + return false; + } + return true; + }; + Choices.prototype._canCreateItem = function (value) { + var config = this.config; + var canAddItem = true; + var notice = ''; + if (canAddItem && typeof config.addItemFilter === 'function' && !config.addItemFilter(value)) { + canAddItem = false; + notice = resolveNoticeFunction(config.customAddItemText, value); + } + if (canAddItem) { + var foundChoice = this._store.choices.find(function (choice) { return config.valueComparer(choice.value, value); }); + if (this._isSelectElement) { + // for exact matches, do not prompt to add it as a custom choice + if (foundChoice) { + this._displayNotice('', NoticeTypes.addChoice); + return false; + } + } + else if (this._isTextElement && !config.duplicateItemsAllowed) { + if (foundChoice) { + canAddItem = false; + notice = resolveNoticeFunction(config.uniqueItemText, value); + } + } + } + if (canAddItem) { + notice = resolveNoticeFunction(config.addItemText, value); + } + if (notice) { + this._displayNotice(notice, NoticeTypes.addChoice); + } + return canAddItem; + }; + Choices.prototype._searchChoices = function (value) { + var newValue = value.trim().replace(/\s{2,}/, ' '); + // signal input didn't change search + if (!newValue.length || newValue === this._currentValue) { + return null; + } + var searcher = this._searcher; + if (searcher.isEmptyIndex()) { + searcher.index(this._store.searchableChoices); + } + // If new value matches the desired length and is not the same as the current value with a space + var results = searcher.search(newValue); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + var notice = this._notice; + var noticeType = notice && notice.type; + if (noticeType !== NoticeTypes.addChoice) { + if (!results.length) { + this._displayNotice(resolveStringFunction(this.config.noResultsText), NoticeTypes.noResults); + } + else if (noticeType === NoticeTypes.noResults) { + this._clearNotice(); + } + } + this._store.dispatch(filterChoices(results)); + return results.length; + }; + Choices.prototype._stopSearch = function () { + var wasSearching = this._isSearching; + this._currentValue = ''; + this._isSearching = false; + if (wasSearching) { + this._store.dispatch(activateChoices(true)); + } + }; + Choices.prototype._addEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + // capture events - can cancel event processing or propagation + documentElement.addEventListener('touchend', this._onTouchEnd, true); + outerElement.addEventListener('keydown', this._onKeyDown, true); + outerElement.addEventListener('mousedown', this._onMouseDown, true); + // passive events - doesn't call `preventDefault` or `stopPropagation` + documentElement.addEventListener('click', this._onClick, { passive: true }); + documentElement.addEventListener('touchmove', this._onTouchMove, { + passive: true, + }); + this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { + passive: true, + }); + if (this._isSelectOneElement) { + outerElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + outerElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + } + inputElement.addEventListener('keyup', this._onKeyUp, { + passive: true, + }); + inputElement.addEventListener('input', this._onInput, { + passive: true, + }); + inputElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + inputElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + if (inputElement.form) { + inputElement.form.addEventListener('reset', this._onFormReset, { + passive: true, + }); + } + this.input.addEventListeners(); + }; + Choices.prototype._removeEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + documentElement.removeEventListener('touchend', this._onTouchEnd, true); + outerElement.removeEventListener('keydown', this._onKeyDown, true); + outerElement.removeEventListener('mousedown', this._onMouseDown, true); + documentElement.removeEventListener('click', this._onClick); + documentElement.removeEventListener('touchmove', this._onTouchMove); + this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); + if (this._isSelectOneElement) { + outerElement.removeEventListener('focus', this._onFocus); + outerElement.removeEventListener('blur', this._onBlur); + } + inputElement.removeEventListener('keyup', this._onKeyUp); + inputElement.removeEventListener('input', this._onInput); + inputElement.removeEventListener('focus', this._onFocus); + inputElement.removeEventListener('blur', this._onBlur); + if (inputElement.form) { + inputElement.form.removeEventListener('reset', this._onFormReset); + } + this.input.removeEventListeners(); + }; + Choices.prototype._onKeyDown = function (event) { + var keyCode = event.keyCode; + var items = this._store.items; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.element.hasChildNodes(); + /* + See: + https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key + https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values + https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs + https://stackoverflow.com/a/70866532 - "Unidentified" for mobile + http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7) + + Logic: when a key event is sent, `event.key` represents its printable value _or_ one + of a large list of special values indicating meta keys/functionality. In addition, + key events for compose functionality contain a value of `Dead` when mid-composition. + + I can't quite verify it, but non-English IMEs may also be able to generate key codes + for code points in the surrogate-pair range, which could potentially be seen as having + key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that + alone. + + Here, key.length === 1 means we know for sure the input was printable and not a special + `key` value. When the length is greater than 1, it could be either a printable surrogate + pair or a special `key` value. We can tell the difference by checking if the _character + code_ value (not code point!) is in the "surrogate pair" range or not. + + We don't use .codePointAt because an invalid code point would return 65535, which wouldn't + pass the >= 0x10000 check we would otherwise use. + + > ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points + > of each plane are noncharacters: U+FFFE and U+FFFF on the BMP... + */ + var wasPrintableChar = event.key.length === 1 || + (event.key.length === 2 && event.key.charCodeAt(0) >= 0xd800) || + event.key === 'Unidentified'; + if (!this._isTextElement && !hasActiveDropdown) { + this.showDropdown(); + if (!this.input.isFocussed && wasPrintableChar) { + /* + We update the input value with the pressed key as + the input was not focussed at the time of key press + therefore does not have the value of the key. + */ + this.input.value += event.key; + } + } + switch (keyCode) { + case 65 /* KeyCodeMap.A_KEY */: + return this._onSelectKey(event, hasItems); + case 13 /* KeyCodeMap.ENTER_KEY */: + return this._onEnterKey(event, hasActiveDropdown); + case 27 /* KeyCodeMap.ESC_KEY */: + return this._onEscapeKey(event, hasActiveDropdown); + case 38 /* KeyCodeMap.UP_KEY */: + case 33 /* KeyCodeMap.PAGE_UP_KEY */: + case 40 /* KeyCodeMap.DOWN_KEY */: + case 34 /* KeyCodeMap.PAGE_DOWN_KEY */: + return this._onDirectionKey(event, hasActiveDropdown); + case 8 /* KeyCodeMap.DELETE_KEY */: + case 46 /* KeyCodeMap.BACK_KEY */: + return this._onDeleteKey(event, items, hasFocusedInput); + } + }; + Choices.prototype._onKeyUp = function ( /* event: KeyboardEvent */) { + this._canSearch = this.config.searchEnabled; + }; + Choices.prototype._onInput = function ( /* event: InputEvent */) { + var value = this.input.value; + if (!value) { + if (this._isTextElement) { + this.hideDropdown(true); + } + else { + this._stopSearch(); + } + this._clearNotice(); + return; + } + if (!this._canAddItems()) { + return; + } + if (this._canSearch) { + // do the search even if the entered text can not be added + this._handleSearch(value); + } + if (!this._canAddUserChoices) { + return; + } + // determine if a notice needs to be displayed for why a search result can't be added + this._canCreateItem(value); + if (this._isSelectElement) { + this._highlightPosition = 0; // reset to select the notice and/or exact match + this._highlightChoice(); + } + }; + Choices.prototype._onSelectKey = function (event, hasItems) { + var ctrlKey = event.ctrlKey, metaKey = event.metaKey; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; + // If CTRL + A or CMD + A have been pressed and there are items to select + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + if (shouldHightlightAll) { + this.highlightAll(); + } + } + }; + Choices.prototype._onEnterKey = function (event, hasActiveDropdown) { + var _this = this; + var config = this.config; + var value = this.input.value; + var target = event.target; + var targetWasRemoveButton = target && target.hasAttribute('data-button'); + event.preventDefault(); + if (targetWasRemoveButton) { + this._handleButtonAction(target); + return; + } + if (!hasActiveDropdown) { + if (this._isSelectElement || this._notice) { + this.showDropdown(); + } + return; + } + var highlightedChoice = this.dropdown.element.querySelector(getClassNamesSelector(config.classNames.highlightedState)); + if (highlightedChoice && this._handleChoiceAction(highlightedChoice)) { + return; + } + if (!target || !value) { + this.hideDropdown(true); + return; + } + if (!this._canAddItems()) { + return; + } + var addedItem = false; + this._store.withTxn(function () { + addedItem = _this._findAndSelectChoiceByValue(value, true); + if (!addedItem) { + if (!_this._canAddUserChoices) { + return; + } + if (!_this._canCreateItem(value)) { + return; + } + var sanitisedValue = sanitise(value); + var userValue = config.allowHtmlUserInput || sanitisedValue === value ? value : { escaped: sanitisedValue, raw: value }; + _this._addChoice(mapInputToChoice({ + value: userValue, + label: userValue, + selected: true, + }, false), true, true); + addedItem = true; + } + _this.clearInput(); + _this.unhighlightAll(); + }); + if (!addedItem) { + return; + } + this._triggerChange(value); + if (config.closeDropdownOnSelect) { + this.hideDropdown(true); + } + }; + Choices.prototype._onEscapeKey = function (event, hasActiveDropdown) { + if (hasActiveDropdown) { + event.stopPropagation(); + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + }; + Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) { + var keyCode = event.keyCode, metaKey = event.metaKey; + // If up or down key is pressed, traverse through options + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === 40 /* KeyCodeMap.DOWN_KEY */ || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ ? 1 : -1; + var skipKey = metaKey || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ || keyCode === 33 /* KeyCodeMap.PAGE_UP_KEY */; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl = void 0; + if (skipKey) { + if (directionInt > 0) { + nextEl = this.dropdown.element.querySelector("".concat(selectableChoiceIdentifier, ":last-of-type")); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + else { + var currentEl = this.dropdown.element.querySelector(getClassNamesSelector(this.config.classNames.highlightedState)); + if (currentEl) { + nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChildElement(nextEl, directionInt); + } + this._highlightChoice(nextEl); + } + // Prevent default to maintain cursor position whilst + // traversing dropdown options + event.preventDefault(); + } + }; + Choices.prototype._onDeleteKey = function (event, items, hasFocusedInput) { + var target = event.target; + // If backspace or delete key is pressed and the input has no value + if (!this._isSelectOneElement && !target.value && hasFocusedInput) { + this._handleBackspace(items); + event.preventDefault(); + } + }; + Choices.prototype._onTouchMove = function () { + if (this._wasTap) { + this._wasTap = false; + } + }; + Choices.prototype._onTouchEnd = function (event) { + var target = (event || event.touches[0]).target; + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } + else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } + // Prevents focus event firing + event.stopPropagation(); + } + this._wasTap = true; + }; + /** + * Handles mousedown event in capture mode for containetOuter.element + */ + Choices.prototype._onMouseDown = function (event) { + var target = event.target; + if (!(target instanceof HTMLElement)) { + return; + } + // If we have our mouse down on the scrollbar and are on IE11... + if (IS_IE11 && this.choiceList.element.contains(target)) { + // check if click was on a scrollbar area + var firstChoice = this.choiceList.element.firstElementChild; + this._isScrollingOnIe = + this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; + } + if (target === this.input.element) { + return; + } + var item = target.closest('[data-button],[data-item],[data-choice]'); + if (item instanceof HTMLElement) { + var hasShiftKey = event.shiftKey; + var dataset = item.dataset; + if ('button' in dataset) { + this._handleButtonAction(item); + } + else if ('item' in dataset) { + this._handleItemAction(item, hasShiftKey); + } + else if ('choice' in dataset) { + this._handleChoiceAction(item); + } + } + event.preventDefault(); + }; + /** + * Handles mouseover event over this.dropdown + * @param {MouseEvent} event + */ + Choices.prototype._onMouseOver = function (_a) { + var target = _a.target; + if (target instanceof HTMLElement && 'choice' in target.dataset) { + this._highlightChoice(target); + } + }; + Choices.prototype._onClick = function (_a) { + var target = _a.target; + var containerOuter = this.containerOuter; + var clickWasWithinContainer = containerOuter.element.contains(target); + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } + else { + this.showDropdown(); + containerOuter.element.focus(); + } + } + else if (this._isSelectOneElement && + target !== this.input.element && + !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } + else { + var hasHighlightedItems = !!this._store.highlightedActiveItems.length; + if (hasHighlightedItems) { + this.unhighlightAll(); + } + containerOuter.removeFocusState(); + this.hideDropdown(true); + } + }; + Choices.prototype._onFocus = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var focusWasWithinContainer = target && containerOuter.element.contains(target); + if (!focusWasWithinContainer) { + return; + } + var targetIsInput = target === this.input.element; + var focusActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput) { + containerOuter.addFocusState(); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.addFocusState(); + if (targetIsInput) { + _this.showDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput) { + _this.showDropdown(true); + // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown + containerOuter.addFocusState(); + } + }, + _b); + focusActions[this._elementType](); + }; + Choices.prototype._onBlur = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var blurWasWithinContainer = target && containerOuter.element.contains(target); + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeChoices = this._store.activeChoices; + var hasHighlightedItems_1 = activeChoices.some(function (item) { return item.highlighted; }); + var targetIsInput_1 = target === this.input.element; + var blurActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + _this.hideDropdown(true); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.removeFocusState(); + if (targetIsInput_1 || (target === containerOuter.element && !_this._canSearch)) { + _this.hideDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + _this.hideDropdown(true); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + } + }, + _b); + blurActions[this._elementType](); + } + else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + }; + Choices.prototype._onFormReset = function () { + var _this = this; + this._store.withTxn(function () { + _this.clearInput(); + _this.hideDropdown(); + _this.refresh(false, false, true); + if (_this._initialItems.length) { + _this.setChoiceByValue(_this._initialItems); + } + }); + }; + Choices.prototype._highlightChoice = function (el) { + var _a; + if (el === void 0) { el = null; } + var highlightedState = this.config.classNames.highlightedState; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); + if (!choices.length) { + return; + } + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(getClassNamesSelector(highlightedState))); + // Remove any highlighted choices + highlightedChoices.forEach(function (choice) { + var _a; + (_a = choice.classList).remove.apply(_a, getClassNames(highlightedState)); + choice.setAttribute('aria-selected', 'false'); + }); + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } + else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } + else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + if (!passedEl) { + passedEl = choices[0]; + } + } + (_a = passedEl.classList).add.apply(_a, getClassNames(highlightedState)); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent("highlightChoice" /* EventType.highlightChoice */, { + el: passedEl, + }); + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + }; + Choices.prototype._addItem = function (item, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + var id = item.id; + if (!id) { + throw new TypeError('item.id must be set before _addItem is called for a choice/item'); + } + if (this.config.singleModeForMultiSelect || this._isSelectOneElement) { + this.removeActiveItems(id); + } + this._store.dispatch(addItem(item)); + if (withEvents) { + this.passedElement.triggerEvent("addItem" /* EventType.addItem */, this._getChoiceForOutput(item)); + if (userTriggered) { + this.passedElement.triggerEvent("choice" /* EventType.choice */, this._getChoiceForOutput(item)); + } + } + }; + Choices.prototype._removeItem = function (item) { + var id = item.id; + if (!id) { + return; + } + this._store.dispatch(removeItem(item)); + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(item)); + }; + Choices.prototype._addChoice = function (choice, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + if (choice.id) { + throw new TypeError('Can not re-add a choice which has already been added'); + } + // Generate unique id, in-place update is required so chaining _addItem works as expected + this._lastAddedChoiceId++; + choice.id = this._lastAddedChoiceId; + choice.elementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choice.id); + var _a = this.config, prependValue = _a.prependValue, appendValue = _a.appendValue; + if (prependValue) { + choice.value = prependValue + choice.value; + } + if (appendValue) { + choice.value += appendValue.toString(); + } + if ((prependValue || appendValue) && choice.element) { + choice.element.value = choice.value; + } + this._store.dispatch(addChoice(choice)); + if (choice.selected) { + this._addItem(choice, withEvents, userTriggered); + } + }; + Choices.prototype._addGroup = function (group, withEvents) { + var _this = this; + if (withEvents === void 0) { withEvents = true; } + if (group.id) { + throw new TypeError('Can not re-add a group which has already been added'); + } + this._store.dispatch(addGroup(group)); + if (!group.choices) { + return; + } + // add unique id for the group(s), and do not store the full list of choices in this group + var g = group; + this._lastAddedGroupId++; + g.id = this._lastAddedGroupId; + var id = group.id, choices = group.choices; + g.choices = []; + choices.forEach(function (item) { + item.groupId = id; + if (group.disabled) { + item.disabled = true; + } + _this._addChoice(item, withEvents); + }); + }; + Choices.prototype._createTemplates = function () { + var _this = this; + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; + if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { + userTemplates = callbackOnCreateTemplates.call(this, strToEl, escapeForTemplate); + } + var templating = {}; + Object.keys(this._templates).forEach(function (name) { + if (name in userTemplates) { + templating[name] = userTemplates[name].bind(_this); + } + else { + templating[name] = _this._templates[name].bind(_this); + } + }); + this._templates = templating; + }; + Choices.prototype._createElements = function () { + var templating = this._templates; + var config = this.config; + var position = config.position, classNames = config.classNames; + var elementType = this._elementType; + this.containerOuter = new Container({ + element: templating.containerOuter(config, this._direction, this._isSelectElement, this._isSelectOneElement, config.searchEnabled, elementType, config.labelId), + classNames: classNames, + type: elementType, + position: position, + }); + this.containerInner = new Container({ + element: templating.containerInner(config), + classNames: classNames, + type: elementType, + position: position, + }); + this.input = new Input({ + element: templating.input(config, this._placeholderValue), + classNames: classNames, + type: elementType, + preventPaste: !config.paste, + }); + this.choiceList = new List({ + element: templating.choiceList(config, this._isSelectOneElement), + }); + this.itemList = new List({ + element: templating.itemList(config, this._isSelectOneElement), + }); + this.dropdown = new Dropdown({ + element: templating.dropdown(config), + classNames: classNames, + type: elementType, + }); + }; + Choices.prototype._createStructure = function () { + var _a = this, containerInner = _a.containerInner, containerOuter = _a.containerOuter, passedElement = _a.passedElement, dropdown = _a.dropdown, input = _a.input; + // Hide original element + passedElement.conceal(); + // Wrap input in container preserving DOM ordering + containerInner.wrap(passedElement.element); + // Wrapper inner container with outer container + containerOuter.wrap(containerInner.element); + if (this._isSelectOneElement) { + input.placeholder = this.config.searchPlaceholderValue || ''; + } + else { + if (this._placeholderValue) { + input.placeholder = this._placeholderValue; + } + input.setWidth(); + } + containerOuter.element.appendChild(containerInner.element); + containerOuter.element.appendChild(dropdown.element); + containerInner.element.appendChild(this.itemList.element); + dropdown.element.appendChild(this.choiceList.element); + if (!this._isSelectOneElement) { + containerInner.element.appendChild(input.element); + } + else if (this.config.searchEnabled) { + dropdown.element.insertBefore(input.element, dropdown.element.firstChild); + } + this._highlightPosition = 0; + this._isSearching = false; + }; + Choices.prototype._initStore = function () { + var _this = this; + this._store.subscribe(this._render); + this._store.withTxn(function () { + _this._addPredefinedChoices(_this._presetChoices, _this._isSelectOneElement && !_this._hasNonChoicePlaceholder, false); + }); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder) { + this._render({ choices: false, groups: false, items: true }); + } + }; + Choices.prototype._addPredefinedChoices = function (choices, selectFirstOption, withEvents) { + var _this = this; + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (withEvents === void 0) { withEvents = true; } + if (selectFirstOption) { + /** + * If there is a selected choice already or the choice is not the first in + * the array, add each choice normally. + * + * Otherwise we pre-select the first enabled choice in the array ("select-one" only) + */ + var noSelectedChoices = choices.findIndex(function (choice) { return choice.selected; }) === -1; + if (noSelectedChoices) { + choices.some(function (choice) { + if (choice.disabled || 'choices' in choice) { + return false; + } + choice.selected = true; + return true; + }); + } + } + choices.forEach(function (item) { + if ('choices' in item) { + if (_this._isSelectElement) { + _this._addGroup(item, withEvents); + } + } + else { + _this._addChoice(item, withEvents); + } + }); + }; + Choices.prototype._findAndSelectChoiceByValue = function (value, userTriggered) { + var _this = this; + if (userTriggered === void 0) { userTriggered = false; } + var choices = this._store.choices; + // Check 'value' property exists and the choice isn't already selected + var foundChoice = choices.find(function (choice) { return _this.config.valueComparer(choice.value, value); }); + if (foundChoice && !foundChoice.disabled && !foundChoice.selected) { + this._addItem(foundChoice, true, userTriggered); + return true; + } + return false; + }; + Choices.prototype._generatePlaceholderValue = function () { + var config = this.config; + if (!config.placeholder) { + return null; + } + if (this._hasNonChoicePlaceholder) { + return config.placeholderValue; + } + if (this._isSelectElement) { + var placeholderOption = this.passedElement.placeholderOption; + return placeholderOption ? placeholderOption.text : null; + } + return null; + }; + Choices.prototype._warnChoicesInitFailed = function (caller) { + if (this.config.silent) { + return; + } + if (!this.initialised) { + throw new TypeError("".concat(caller, " called on a non-initialised instance of Choices")); + } + else if (!this.initialisedOK) { + throw new TypeError("".concat(caller, " called for an element which has multiple instances of Choices initialised on it")); + } + }; + Choices.version = '11.0.0-rc7'; + return Choices; +}()); + +export { Choices as default }; diff --git a/public/assets/scripts/choices.search-basic.js b/public/assets/scripts/choices.search-basic.js new file mode 100644 index 000000000..2c6bf16b9 --- /dev/null +++ b/public/assets/scripts/choices.search-basic.js @@ -0,0 +1,4715 @@ +/*! choices.js v11.0.0-rc7 | © 2024 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Choices = factory()); +})(this, (function () { 'use strict'; + + /****************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise, SuppressedError, Symbol */ + + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || { + __proto__: [] + } instanceof Array && function (d, b) { + d.__proto__ = b; + } || function (d, b) { + for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + function __extends(d, b) { + if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + var __assign = function () { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); + } + typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; + }; + + var ActionType = { + ADD_CHOICE: 'ADD_CHOICE', + REMOVE_CHOICE: 'REMOVE_CHOICE', + FILTER_CHOICES: 'FILTER_CHOICES', + ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', + CLEAR_CHOICES: 'CLEAR_CHOICES', + ADD_GROUP: 'ADD_GROUP', + ADD_ITEM: 'ADD_ITEM', + REMOVE_ITEM: 'REMOVE_ITEM', + HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', + }; + + var ObjectsInConfig = ['fuseOptions', 'classNames']; + + var addChoice = function (choice) { return ({ + type: ActionType.ADD_CHOICE, + choice: choice, + }); }; + var removeChoice = function (choice) { return ({ + type: ActionType.REMOVE_CHOICE, + choice: choice, + }); }; + var filterChoices = function (results) { return ({ + type: ActionType.FILTER_CHOICES, + results: results, + }); }; + var activateChoices = function (active) { + return ({ + type: ActionType.ACTIVATE_CHOICES, + active: active, + }); + }; + var clearChoices = function () { return ({ + type: ActionType.CLEAR_CHOICES, + }); }; + + var addGroup = function (group) { return ({ + type: ActionType.ADD_GROUP, + group: group, + }); }; + + var addItem = function (item) { return ({ + type: ActionType.ADD_ITEM, + item: item, + }); }; + var removeItem = function (item) { return ({ + type: ActionType.REMOVE_ITEM, + item: item, + }); }; + var highlightItem = function (item, highlighted) { return ({ + type: ActionType.HIGHLIGHT_ITEM, + item: item, + highlighted: highlighted, + }); }; + + /* eslint-disable @typescript-eslint/no-explicit-any */ + var getRandomNumber = function (min, max) { return Math.floor(Math.random() * (max - min) + min); }; + var generateChars = function (length) { + return Array.from({ length: length }, function () { return getRandomNumber(0, 36).toString(36); }).join(''); + }; + var generateId = function (element, prefix) { + var id = element.id || (element.name && "".concat(element.name, "-").concat(generateChars(2))) || generateChars(4); + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = "".concat(prefix, "-").concat(id); + return id; + }; + var getAdjacentEl = function (startEl, selector, direction) { + if (direction === void 0) { direction = 1; } + var prop = "".concat(direction > 0 ? 'next' : 'previous', "ElementSibling"); + var sibling = startEl[prop]; + while (sibling) { + if (sibling.matches(selector)) { + return sibling; + } + sibling = sibling[prop]; + } + return null; + }; + var isScrolledIntoView = function (element, parent, direction) { + if (direction === void 0) { direction = 1; } + var isVisible; + if (direction > 0) { + // In view from bottom + isVisible = parent.scrollTop + parent.offsetHeight >= element.offsetTop + element.offsetHeight; + } + else { + // In view from top + isVisible = element.offsetTop >= parent.scrollTop; + } + return isVisible; + }; + var sanitise = function (value) { + if (typeof value !== 'string') { + if (value === null || value === undefined) { + return ''; + } + if (typeof value === 'object') { + if ('raw' in value) { + return sanitise(value.raw); + } + if ('trusted' in value) { + return value.trusted; + } + } + return value; + } + return value + .replace(/&/g, '&') + .replace(/>/g, '>') + .replace(/ 0 ? this.element.scrollTop + elementPos - listScrollPosition : element.offsetTop; + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); + }; + List.prototype._scrollDown = function (scrollPos, strength, destination) { + var easing = (destination - scrollPos) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos + distance; + }; + List.prototype._scrollUp = function (scrollPos, strength, destination) { + var easing = (scrollPos - destination) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos - distance; + }; + List.prototype._animateScroll = function (destination, direction) { + var _this = this; + var strength = SCROLLING_SPEED; + var choiceListScrollTop = this.element.scrollTop; + var continueAnimation = false; + if (direction > 0) { + this._scrollDown(choiceListScrollTop, strength, destination); + if (choiceListScrollTop < destination) { + continueAnimation = true; + } + } + else { + this._scrollUp(choiceListScrollTop, strength, destination); + if (choiceListScrollTop > destination) { + continueAnimation = true; + } + } + if (continueAnimation) { + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); + } + }; + return List; + }()); + + var WrappedElement = /** @class */ (function () { + function WrappedElement(_a) { + var element = _a.element, classNames = _a.classNames; + this.element = element; + this.classNames = classNames; + this.isDisabled = false; + } + Object.defineProperty(WrappedElement.prototype, "isActive", { + get: function () { + return this.element.dataset.choice === 'active'; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(WrappedElement.prototype, "dir", { + get: function () { + return this.element.dir; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(WrappedElement.prototype, "value", { + get: function () { + return this.element.value; + }, + set: function (value) { + this.element.setAttribute('value', value); + this.element.value = value; + }, + enumerable: false, + configurable: true + }); + WrappedElement.prototype.conceal = function () { + var _a; + var el = this.element; + // Hide passed input + (_a = el.classList).add.apply(_a, getClassNames(this.classNames.input)); + el.hidden = true; + // Remove element from tab index + el.tabIndex = -1; + // Backup original styles if any + var origStyle = el.getAttribute('style'); + if (origStyle) { + el.setAttribute('data-choice-orig-style', origStyle); + } + el.setAttribute('data-choice', 'active'); + }; + WrappedElement.prototype.reveal = function () { + var _a; + var el = this.element; + // Reinstate passed element + (_a = el.classList).remove.apply(_a, getClassNames(this.classNames.input)); + el.hidden = false; + el.removeAttribute('tabindex'); + // Recover original styles if any + var origStyle = el.getAttribute('data-choice-orig-style'); + if (origStyle) { + el.removeAttribute('data-choice-orig-style'); + el.setAttribute('style', origStyle); + } + else { + el.removeAttribute('style'); + } + el.removeAttribute('data-choice'); + }; + WrappedElement.prototype.enable = function () { + var element = this.element; + element.removeAttribute('disabled'); + element.disabled = false; + this.isDisabled = false; + }; + WrappedElement.prototype.disable = function () { + var element = this.element; + element.setAttribute('disabled', ''); + element.disabled = true; + this.isDisabled = true; + }; + WrappedElement.prototype.triggerEvent = function (eventType, data) { + dispatchEvent(this.element, eventType, data || {}); + }; + return WrappedElement; + }()); + + var WrappedInput = /** @class */ (function (_super) { + __extends(WrappedInput, _super); + function WrappedInput() { + return _super !== null && _super.apply(this, arguments) || this; + } + return WrappedInput; + }(WrappedElement)); + + var coerceBool = function (arg, defaultValue) { + if (defaultValue === void 0) { defaultValue = true; } + return typeof arg === 'undefined' ? defaultValue : !!arg; + }; + var stringToHtmlClass = function (input) { + if (typeof input === 'string') { + // eslint-disable-next-line no-param-reassign + input = input.split(' ').filter(function (s) { return s.length; }); + } + if (Array.isArray(input) && input.length) { + return input; + } + return undefined; + }; + var mapInputToChoice = function (value, allowGroup) { + if (typeof value === 'string') { + var result_1 = mapInputToChoice({ + value: value, + label: value, + }, false); + return result_1; + } + var groupOrChoice = value; + if ('choices' in groupOrChoice) { + if (!allowGroup) { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup + throw new TypeError("optGroup is not allowed"); + } + var group = groupOrChoice; + var choices = group.choices.map(function (e) { return mapInputToChoice(e, false); }); + var result_2 = { + id: 0, // actual ID will be assigned during _addGroup + label: unwrapStringForRaw(group.label) || group.value, + active: !!choices.length, + disabled: !!group.disabled, + choices: choices, + }; + return result_2; + } + var choice = groupOrChoice; + var result = { + id: 0, // actual ID will be assigned during _addChoice + groupId: 0, // actual ID will be assigned during _addGroup but before _addChoice + score: 0, // used in search + rank: 0, // used in search, stable sort order + value: choice.value, + label: choice.label || choice.value, + active: coerceBool(choice.active), + selected: coerceBool(choice.selected, false), + disabled: coerceBool(choice.disabled, false), + placeholder: coerceBool(choice.placeholder, false), + highlighted: false, + labelClass: stringToHtmlClass(choice.labelClass), + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + }; + return result; + }; + + var isHtmlInputElement = function (e) { return e.tagName === 'INPUT'; }; + var isHtmlSelectElement = function (e) { return e.tagName === 'SELECT'; }; + var isHtmlOption = function (e) { return e.tagName === 'OPTION'; }; + var isHtmlOptgroup = function (e) { return e.tagName === 'OPTGROUP'; }; + + var WrappedSelect = /** @class */ (function (_super) { + __extends(WrappedSelect, _super); + function WrappedSelect(_a) { + var element = _a.element, classNames = _a.classNames, template = _a.template, extractPlaceholder = _a.extractPlaceholder; + var _this = _super.call(this, { element: element, classNames: classNames }) || this; + _this.template = template; + _this.extractPlaceholder = extractPlaceholder; + return _this; + } + Object.defineProperty(WrappedSelect.prototype, "placeholderOption", { + get: function () { + return (this.element.querySelector('option[value=""]') || + // Backward compatibility layer for the non-standard placeholder attribute supported in older versions. + this.element.querySelector('option[placeholder]')); + }, + enumerable: false, + configurable: true + }); + WrappedSelect.prototype.addOptions = function (choices) { + var _this = this; + var fragment = document.createDocumentFragment(); + choices.forEach(function (obj) { + var choice = obj; + if (choice.element) { + return; + } + var option = _this.template(choice); + fragment.appendChild(option); + choice.element = option; + }); + this.element.appendChild(fragment); + }; + WrappedSelect.prototype.optionsAsChoices = function () { + var _this = this; + var choices = []; + this.element.querySelectorAll(':scope > option, :scope > optgroup').forEach(function (e) { + if (isHtmlOption(e)) { + choices.push(_this._optionToChoice(e)); + } + else if (isHtmlOptgroup(e)) { + choices.push(_this._optgroupToChoice(e)); + } + // todo: hr as empty optgroup, requires displaying empty opt-groups to be useful + }); + return choices; + }; + // eslint-disable-next-line class-methods-use-this + WrappedSelect.prototype._optionToChoice = function (option) { + // option.value returns the label if there is no value attribute, which can break legacy placeholder attribute support + if (!option.hasAttribute('value') && option.hasAttribute('placeholder')) { + option.setAttribute('value', ''); + option.value = ''; + } + var dataset = option.dataset; + return { + id: 0, + groupId: 0, + score: 0, + rank: 0, + value: option.value, + label: option.innerHTML, + element: option, + active: true, + // this returns true if nothing is selected on initial load, which will break placeholder support + selected: this.extractPlaceholder ? option.selected : option.hasAttribute('selected'), + disabled: option.disabled, + highlighted: false, + placeholder: this.extractPlaceholder && (!option.value || option.hasAttribute('placeholder')), + labelClass: typeof dataset.labelClass !== 'undefined' ? stringToHtmlClass(dataset.labelClass) : undefined, + labelDescription: typeof dataset.labelDescription !== 'undefined' ? dataset.labelDescription : undefined, + customProperties: parseCustomProperties(dataset.customProperties), + }; + }; + WrappedSelect.prototype._optgroupToChoice = function (optgroup) { + var _this = this; + var options = optgroup.querySelectorAll('option'); + var choices = Array.from(options).map(function (option) { return _this._optionToChoice(option); }); + return { + id: 0, + label: optgroup.label || '', + element: optgroup, + active: !!choices.length, + disabled: optgroup.disabled, + choices: choices, + }; + }; + return WrappedSelect; + }(WrappedElement)); + + var DEFAULT_CLASSNAMES = { + containerOuter: ['choices'], + containerInner: ['choices__inner'], + input: ['choices__input'], + inputCloned: ['choices__input--cloned'], + list: ['choices__list'], + listItems: ['choices__list--multiple'], + listSingle: ['choices__list--single'], + listDropdown: ['choices__list--dropdown'], + item: ['choices__item'], + itemSelectable: ['choices__item--selectable'], + itemDisabled: ['choices__item--disabled'], + itemChoice: ['choices__item--choice'], + description: ['choices__description'], + placeholder: ['choices__placeholder'], + group: ['choices__group'], + groupHeading: ['choices__heading'], + button: ['choices__button'], + activeState: ['is-active'], + focusState: ['is-focused'], + openState: ['is-open'], + disabledState: ['is-disabled'], + highlightedState: ['is-highlighted'], + selectedState: ['is-selected'], + flippedState: ['is-flipped'], + loadingState: ['is-loading'], + notice: ['choices__notice'], + addChoice: ['choices__item--selectable', 'add-choice'], + noResults: ['has-no-results'], + noChoices: ['has-no-choices'], + }; + var DEFAULT_CONFIG = { + items: [], + choices: [], + silent: false, + renderChoiceLimit: -1, + maxItemCount: -1, + closeDropdownOnSelect: 'auto', + singleModeForMultiSelect: false, + addChoices: false, + addItems: true, + addItemFilter: function (value) { return !!value && value !== ''; }, + removeItems: true, + removeItemButton: false, + removeItemButtonAlignLeft: false, + editItems: false, + allowHTML: false, + allowHtmlUserInput: false, + duplicateItemsAllowed: true, + delimiter: ',', + paste: true, + searchEnabled: true, + searchChoices: true, + searchFloor: 1, + searchResultLimit: 4, + searchFields: ['label', 'value'], + position: 'auto', + resetScrollPosition: true, + shouldSort: true, + shouldSortItems: false, + sorter: sortByAlpha, + shadowRoot: null, + placeholder: true, + placeholderValue: null, + searchPlaceholderValue: null, + prependValue: null, + appendValue: null, + renderSelectedChoices: 'auto', + loadingText: 'Loading...', + noResultsText: 'No results found', + noChoicesText: 'No choices to choose from', + itemSelectText: 'Press to select', + uniqueItemText: 'Only unique values can be added', + customAddItemText: 'Only values matching specific conditions can be added', + addItemText: function (value) { return "Press Enter to add \"".concat(value, "\""); }, + removeItemIconText: function () { return "Remove item"; }, + removeItemLabelText: function (value) { return "Remove item: ".concat(value); }, + maxItemText: function (maxItemCount) { return "Only ".concat(maxItemCount, " values can be added"); }, + valueComparer: function (value1, value2) { return value1 === value2; }, + fuseOptions: { + includeScore: true, + }, + labelId: '', + callbackOnInit: null, + callbackOnCreateTemplates: null, + classNames: DEFAULT_CLASSNAMES, + appendGroupInSearch: false, + }; + + function items(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_ITEM: { + var item = action.item; + item.selected = true; + var el = item.element; + if (el) { + el.selected = true; + el.setAttribute('selected', ''); + } + state.push(item); + state.forEach(function (choice) { + choice.highlighted = false; + }); + break; + } + case ActionType.REMOVE_ITEM: { + var item_1 = action.item; + item_1.selected = false; + var el = item_1.element; + if (el) { + el.selected = false; + el.removeAttribute('selected'); + // For a select-one, if all options are deselected, the first item is selected. To set a black value, select.value needs to be set + var select = el.parentElement; + if (select && isHtmlSelectElement(select) && select.type === SELECT_ONE_TYPE) { + select.value = ''; + } + } + state = state.filter(function (choice) { return choice.id !== item_1.id; }); + break; + } + case ActionType.REMOVE_CHOICE: { + state = state.filter(function (item) { return item.id !== action.choice.id; }); + break; + } + case ActionType.HIGHLIGHT_ITEM: { + var highlightItemAction_1 = action; + state.forEach(function (choice) { + if (choice.id === highlightItemAction_1.item.id) { + choice.highlighted = highlightItemAction_1.highlighted; + } + }); + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; + } + + function groups(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_GROUP: { + state.push(action.group); + break; + } + case ActionType.CLEAR_CHOICES: { + state = []; + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; + } + + /* eslint-disable */ + function choices(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_CHOICE: { + /* + A disabled choice appears in the choice dropdown but cannot be selected + A selected choice has been added to the passed input's value (added as an item) + An active choice appears within the choice dropdown + */ + state.push(action.choice); + break; + } + case ActionType.REMOVE_CHOICE: { + state = state.filter(function (obj) { return obj.id !== action.choice.id; }); + break; + } + case ActionType.ADD_ITEM: + case ActionType.REMOVE_ITEM: { + break; + } + case ActionType.FILTER_CHOICES: { + // avoid O(n^2) algorithm complexity when searching/filtering choices + var scoreLookup_1 = []; + action.results.forEach(function (result) { + scoreLookup_1[result.item.id] = result; + }); + state.forEach(function (choice) { + var result = scoreLookup_1[choice.id]; + if (result !== undefined) { + choice.score = result.score; + choice.rank = result.rank; + choice.active = true; + } + else { + choice.score = 0; + choice.rank = 0; + choice.active = false; + } + }); + break; + } + case ActionType.ACTIVATE_CHOICES: { + state.forEach(function (choice) { + choice.active = action.active; + }); + break; + } + case ActionType.CLEAR_CHOICES: { + state = []; + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; + } + + var reducers = { + groups: groups, + items: items, + choices: choices, + }; + var Store = /** @class */ (function () { + function Store() { + this._state = this.defaultState; + this._listeners = []; + this._txn = 0; + } + Object.defineProperty(Store.prototype, "defaultState", { + // eslint-disable-next-line class-methods-use-this + get: function () { + return { + groups: [], + items: [], + choices: [], + }; + }, + enumerable: false, + configurable: true + }); + // eslint-disable-next-line class-methods-use-this + Store.prototype.changeSet = function (init) { + return { + groups: init, + items: init, + choices: init, + }; + }; + Store.prototype.reset = function () { + this._state = this.defaultState; + var changes = this.changeSet(true); + if (this._txn) { + this._changeSet = changes; + } + else { + this._listeners.forEach(function (l) { return l(changes); }); + } + }; + Store.prototype.subscribe = function (onChange) { + this._listeners.push(onChange); + }; + Store.prototype.dispatch = function (action) { + var state = this._state; + var hasChanges = false; + var changes = this._changeSet || this.changeSet(false); + Object.keys(reducers).forEach(function (key) { + var stateUpdate = reducers[key](state[key], action); + if (stateUpdate.update) { + hasChanges = true; + changes[key] = true; + state[key] = stateUpdate.state; + } + }); + if (hasChanges) { + if (this._txn) { + this._changeSet = changes; + } + else { + this._listeners.forEach(function (l) { return l(changes); }); + } + } + }; + Store.prototype.withTxn = function (func) { + this._txn++; + try { + func(); + } + finally { + this._txn = Math.max(0, this._txn - 1); + if (!this._txn) { + var changeSet_1 = this._changeSet; + if (changeSet_1) { + this._changeSet = undefined; + this._listeners.forEach(function (l) { return l(changeSet_1); }); + } + } + } + }; + Object.defineProperty(Store.prototype, "state", { + /** + * Get store object + */ + get: function () { + return this._state; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "items", { + /** + * Get items from store + */ + get: function () { + return this.state.items; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "highlightedActiveItems", { + /** + * Get highlighted items from store + */ + get: function () { + return this.items.filter(function (item) { return !item.disabled && item.active && item.highlighted; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "choices", { + /** + * Get choices from store + */ + get: function () { + return this.state.choices; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeChoices", { + /** + * Get active choices from store + */ + get: function () { + return this.choices.filter(function (choice) { return choice.active; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "searchableChoices", { + /** + * Get choices that can be searched (excluding placeholders) + */ + get: function () { + return this.choices.filter(function (choice) { return !choice.disabled && !choice.placeholder; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "groups", { + /** + * Get groups from store + */ + get: function () { + return this.state.groups; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeGroups", { + /** + * Get active groups from store + */ + get: function () { + var _this = this; + return this.state.groups.filter(function (group) { + var isActive = group.active && !group.disabled; + var hasActiveOptions = _this.state.choices.some(function (choice) { return choice.active && !choice.disabled; }); + return isActive && hasActiveOptions; + }, []); + }, + enumerable: false, + configurable: true + }); + Store.prototype.inTxn = function () { + return this._txn > 0; + }; + /** + * Get single choice by it's ID + */ + Store.prototype.getChoiceById = function (id) { + return this.activeChoices.find(function (choice) { return choice.id === id; }); + }; + /** + * Get group by group id + */ + Store.prototype.getGroupById = function (id) { + return this.groups.find(function (group) { return group.id === id; }); + }; + return Store; + }()); + + var NoticeTypes = { + noChoices: 'no-choices', + noResults: 'no-results', + addChoice: 'add-choice', + generic: '', + }; + + function _defineProperty(e, r, t) { + return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { + value: t, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[r] = t, e; + } + function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), t.push.apply(t, o); + } + return t; + } + function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; + } + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; + } + + /** + * Fuse.js v7.0.0 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2023 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + + function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); + } + + // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js + const INFINITY = 1 / 0; + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + let result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; + } + function toString(value) { + return value == null ? '' : baseToString(value); + } + function isString(value) { + return typeof value === 'string'; + } + function isNumber(value) { + return typeof value === 'number'; + } + + // Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js + function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; + } + function isObject(value) { + return typeof value === 'object'; + } + + // Checks if `value` is object-like. + function isObjectLike(value) { + return isObject(value) && value !== null; + } + function isDefined(value) { + return value !== undefined && value !== null; + } + function isBlank(value) { + return !value.trim().length; + } + + // Gets the `toStringTag` of `value`. + // Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js + function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); + } + const EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; + const LOGICAL_SEARCH_UNAVAILABLE = 'Logical search is not available'; + const INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; + const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = key => `Invalid value for key ${key}`; + const PATTERN_LENGTH_TOO_LARGE = max => `Pattern length exceeds max of ${max}.`; + const MISSING_KEY_PROPERTY = name => `Missing ${name} property in key`; + const INVALID_KEY_WEIGHT_VALUE = key => `Property 'weight' in key '${key}' must be a positive integer`; + const hasOwn = Object.prototype.hasOwnProperty; + class KeyStore { + constructor(keys) { + this._keys = []; + this._keyMap = {}; + let totalWeight = 0; + keys.forEach(key => { + let obj = createKey(key); + this._keys.push(obj); + this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); + + // Normalize weights so that their sum is equal to 1 + this._keys.forEach(key => { + key.weight /= totalWeight; + }); + } + get(keyId) { + return this._keyMap[keyId]; + } + keys() { + return this._keys; + } + toJSON() { + return JSON.stringify(this._keys); + } + } + function createKey(key) { + let path = null; + let id = null; + let src = null; + let weight = 1; + let getFn = null; + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + const name = key.name; + src = name; + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + path = createKeyPath(name); + id = createKeyId(name); + getFn = key.getFn; + } + return { + path, + id, + weight, + src, + getFn + }; + } + function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); + } + function createKeyId(key) { + return isArray(key) ? key.join('.') : key; + } + function get(obj, path) { + let list = []; + let arr = false; + const deepGet = (obj, path, index) => { + if (!isDefined(obj)) { + return; + } + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + let key = path[index]; + const value = obj[key]; + if (!isDefined(value)) { + return; + } + + // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; + // Search each item in the array. + for (let i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; + + // Backwards compatibility (since path used to be a string) + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; + } + const MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 + }; + const BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1 + }; + const FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 + }; + const AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false, + // The weight to determine how much field length norm effects scoring. + fieldNormWeight: 1 + }; + var Config = _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, BasicOptions), MatchOptions), FuzzyOptions), AdvancedOptions); + const SPACE = /[^ ]+/g; + + // Field-length norm: the shorter the field, the higher the weight. + // Set to 3 decimals to reduce index size. + function norm(weight = 1, mantissa = 3) { + const cache = new Map(); + const m = Math.pow(10, mantissa); + return { + get(value) { + const numTokens = value.match(SPACE).length; + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + // Default function is 1/sqrt(x), weight makes that variable + const norm = 1 / Math.pow(numTokens, 0.5 * weight); + + // In place of `toFixed(mantissa)`, for faster computation + const n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear() { + cache.clear(); + } + }; + } + class FuseIndex { + constructor({ + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight + } = {}) { + this.norm = norm(fieldNormWeight, 3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + setSources(docs = []) { + this.docs = docs; + } + setIndexRecords(records = []) { + this.records = records; + } + setKeys(keys = []) { + this.keys = keys; + this._keysMap = {}; + keys.forEach((key, idx) => { + this._keysMap[key.id] = idx; + }); + } + create() { + if (this.isCreated || !this.docs.length) { + return; + } + this.isCreated = true; + + // List is Array + if (isString(this.docs[0])) { + this.docs.forEach((doc, docIndex) => { + this._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach((doc, docIndex) => { + this._addObject(doc, docIndex); + }); + } + this.norm.clear(); + } + // Adds a doc to the end of the index + add(doc) { + const idx = this.size(); + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } + // Removes the doc at the specified index of the index + removeAt(idx) { + this.records.splice(idx, 1); + + // Change ref index of every subsquent doc + for (let i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + size() { + return this.records.length; + } + _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + let record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + _addObject(doc, docIndex) { + let record = { + i: docIndex, + $: {} + }; + + // Iterate over every key (i.e, path), and fetch the value at that key + this.keys.forEach((key, keyIndex) => { + let value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path); + if (!isDefined(value)) { + return; + } + if (isArray(value)) { + let subRecords = []; + const stack = [{ + nestedArrIndex: -1, + value + }]; + while (stack.length) { + const { + nestedArrIndex, + value + } = stack.pop(); + if (!isDefined(value)) { + continue; + } + if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + i: nestedArrIndex, + n: this.norm.get(value) + }; + subRecords.push(subRecord); + } else if (isArray(value)) { + value.forEach((item, k) => { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } else ; + } + record.$[keyIndex] = subRecords; + } else if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + n: this.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + toJSON() { + return { + keys: this.keys, + records: this.records + }; + } + } + function createIndex(keys, docs, { + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight + } = {}) { + const myIndex = new FuseIndex({ + getFn, + fieldNormWeight + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; + } + function parseIndex(data, { + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight + } = {}) { + const { + keys, + records + } = data; + const myIndex = new FuseIndex({ + getFn, + fieldNormWeight + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; + } + function computeScore$1(pattern, { + errors = 0, + currentLocation = 0, + expectedLocation = 0, + distance = Config.distance, + ignoreLocation = Config.ignoreLocation + } = {}) { + const accuracy = errors / pattern.length; + if (ignoreLocation) { + return accuracy; + } + const proximity = Math.abs(expectedLocation - currentLocation); + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + return accuracy + proximity / distance; + } + function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) { + let indices = []; + let start = -1; + let end = -1; + let i = 0; + for (let len = matchmask.length; i < len; i += 1) { + let match = matchmask[i]; + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + start = -1; + } + } + + // (i-1 - start) + 1 => i - start + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + return indices; + } + + // Machine word size + const MAX_BITS = 32; + function search(text, pattern, patternAlphabet, { + location = Config.location, + distance = Config.distance, + threshold = Config.threshold, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + includeMatches = Config.includeMatches, + ignoreLocation = Config.ignoreLocation + } = {}) { + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + const patternLen = pattern.length; + // Set starting location at beginning text and initialize the alphabet. + const textLen = text.length; + // Handle the case when location > text.length + const expectedLocation = Math.max(0, Math.min(location, textLen)); + // Highest score beyond which we give up. + let currentThreshold = threshold; + // Is there a nearby exact match? (speedup) + let bestLocation = expectedLocation; + + // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + const computeMatches = minMatchCharLength > 1 || includeMatches; + // A mask of the matches, used for building the indices + const matchMask = computeMatches ? Array(textLen) : []; + let index; + + // Get all exact matches, here for speed up + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + let score = computeScore$1(pattern, { + currentLocation: index, + expectedLocation, + distance, + ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + if (computeMatches) { + let i = 0; + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } + + // Reset the best location + bestLocation = -1; + let lastBitArr = []; + let finalScore = 1; + let binMax = patternLen + textLen; + const mask = 1 << patternLen - 1; + for (let i = 0; i < patternLen; i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + let binMin = 0; + let binMid = binMax; + while (binMin < binMid) { + const score = computeScore$1(pattern, { + errors: i, + currentLocation: expectedLocation + binMid, + expectedLocation, + distance, + ignoreLocation + }); + if (score <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } + + // Use the result from this iteration as the maximum for the next. + binMax = binMid; + let start = Math.max(1, expectedLocation - binMid + 1); + let finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; + + // Initialize the bit array + let bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << i) - 1; + for (let j = finish; j >= start; j -= 1) { + let currentLocation = j - 1; + let charMatch = patternAlphabet[text.charAt(currentLocation)]; + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } + + // First pass: exact match + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; + + // Subsequent passes: fuzzy match + if (i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + if (bitArr[j] & mask) { + finalScore = computeScore$1(pattern, { + errors: i, + currentLocation, + expectedLocation, + distance, + ignoreLocation + }); + + // This match will almost certainly be better than any existing match. + // But check anyway. + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; + + // Already passed `loc`, downhill from here on in. + if (bestLocation <= expectedLocation) { + break; + } + + // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } + + // No hope for a (better) match at greater error levels. + const score = computeScore$1(pattern, { + errors: i + 1, + currentLocation: expectedLocation, + expectedLocation, + distance, + ignoreLocation + }); + if (score > currentThreshold) { + break; + } + lastBitArr = bitArr; + } + const result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + if (computeMatches) { + const indices = convertMaskToIndices(matchMask, minMatchCharLength); + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + return result; + } + function createPatternAlphabet(pattern) { + let mask = {}; + for (let i = 0, len = pattern.length; i < len; i += 1) { + const char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + return mask; + } + class BitapSearch { + constructor(pattern, { + location = Config.location, + threshold = Config.threshold, + distance = Config.distance, + includeMatches = Config.includeMatches, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + isCaseSensitive = Config.isCaseSensitive, + ignoreLocation = Config.ignoreLocation + } = {}) { + this.options = { + location, + threshold, + distance, + includeMatches, + findAllMatches, + minMatchCharLength, + isCaseSensitive, + ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + if (!this.pattern.length) { + return; + } + const addChunk = (pattern, startIndex) => { + this.chunks.push({ + pattern, + alphabet: createPatternAlphabet(pattern), + startIndex + }); + }; + const len = this.pattern.length; + if (len > MAX_BITS) { + let i = 0; + const remainder = len % MAX_BITS; + const end = len - remainder; + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + if (remainder) { + const startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + searchIn(text) { + const { + isCaseSensitive, + includeMatches + } = this.options; + if (!isCaseSensitive) { + text = text.toLowerCase(); + } + + // Exact match + if (this.pattern === text) { + let result = { + isMatch: true, + score: 0 + }; + if (includeMatches) { + result.indices = [[0, text.length - 1]]; + } + return result; + } + + // Otherwise, use Bitap algorithm + const { + location, + distance, + threshold, + findAllMatches, + minMatchCharLength, + ignoreLocation + } = this.options; + let allIndices = []; + let totalScore = 0; + let hasMatches = false; + this.chunks.forEach(({ + pattern, + alphabet, + startIndex + }) => { + const { + isMatch, + score, + indices + } = search(text, pattern, alphabet, { + location: location + startIndex, + distance, + threshold, + findAllMatches, + minMatchCharLength, + includeMatches, + ignoreLocation + }); + if (isMatch) { + hasMatches = true; + } + totalScore += score; + if (isMatch && indices) { + allIndices = [...allIndices, ...indices]; + } + }); + let result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + return result; + } + } + const registeredSearchers = []; + function createSearcher(pattern, options) { + for (let i = 0, len = registeredSearchers.length; i < len; i += 1) { + let searcherClass = registeredSearchers[i]; + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + return new BitapSearch(pattern, options); + } + const LogicalOperator = { + AND: '$and', + OR: '$or' + }; + const KeyType = { + PATH: '$path', + PATTERN: '$val' + }; + const isExpression = query => !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); + const isPath = query => !!query[KeyType.PATH]; + const isLeaf = query => !isArray(query) && isObject(query) && !isExpression(query); + const convertToExplicit = query => ({ + [LogicalOperator.AND]: Object.keys(query).map(key => ({ + [key]: query[key] + })) + }); + + // When `auto` is `true`, the parse function will infer and initialize and add + // the appropriate `Searcher` instance + function parse(query, options, { + auto = true + } = {}) { + const next = query => { + let keys = Object.keys(query); + const isQueryPath = isPath(query); + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + if (isLeaf(query)) { + const key = isQueryPath ? query[KeyType.PATH] : keys[0]; + const pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + const obj = { + keyId: createKeyId(key), + pattern + }; + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + return obj; + } + let node = { + children: [], + operator: keys[0] + }; + keys.forEach(key => { + const value = query[key]; + if (isArray(value)) { + value.forEach(item => { + node.children.push(next(item)); + }); + } + }); + return node; + }; + if (!isExpression(query)) { + query = convertToExplicit(query); + } + return next(query); + } + + // Practical scoring function + function computeScore(results, { + ignoreFieldNorm = Config.ignoreFieldNorm + }) { + results.forEach(result => { + let totalScore = 1; + result.matches.forEach(({ + key, + norm, + score + }) => { + const weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); + } + function transformMatches(result, data) { + const matches = result.matches; + data.matches = []; + if (!isDefined(matches)) { + return; + } + matches.forEach(match => { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + const { + indices, + value + } = match; + let obj = { + indices, + value + }; + if (match.key) { + obj.key = match.key.src; + } + if (match.idx > -1) { + obj.refIndex = match.idx; + } + data.matches.push(obj); + }); + } + function transformScore(result, data) { + data.score = result.score; + } + function format(results, docs, { + includeMatches = Config.includeMatches, + includeScore = Config.includeScore + } = {}) { + const transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(result => { + const { + idx + } = result; + const data = { + item: docs[idx], + refIndex: idx + }; + if (transformers.length) { + transformers.forEach(transformer => { + transformer(result, data); + }); + } + return data; + }); + } + class Fuse { + constructor(docs, options = {}, index) { + this.options = _objectSpread2(_objectSpread2({}, Config), options); + if (this.options.useExtendedSearch && !false) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + setCollection(docs, index) { + this._docs = docs; + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn, + fieldNormWeight: this.options.fieldNormWeight + }); + } + add(doc) { + if (!isDefined(doc)) { + return; + } + this._docs.push(doc); + this._myIndex.add(doc); + } + remove(predicate = ( /* doc, idx */) => false) { + const results = []; + for (let i = 0, len = this._docs.length; i < len; i += 1) { + const doc = this._docs[i]; + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + return results; + } + removeAt(idx) { + this._docs.splice(idx, 1); + this._myIndex.removeAt(idx); + } + getIndex() { + return this._myIndex; + } + search(query, { + limit = -1 + } = {}) { + const { + includeMatches, + includeScore, + shouldSort, + sortFn, + ignoreFieldNorm + } = this.options; + let results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore(results, { + ignoreFieldNorm + }); + if (shouldSort) { + results.sort(sortFn); + } + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + return format(results, this._docs, { + includeMatches, + includeScore + }); + } + _searchStringList(query) { + const searcher = createSearcher(query, this.options); + const { + records + } = this._myIndex; + const results = []; + + // Iterate over every string in the index + records.forEach(({ + v: text, + i: idx, + n: norm + }) => { + if (!isDefined(text)) { + return; + } + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + results.push({ + item: text, + idx, + matches: [{ + score, + value: text, + norm, + indices + }] + }); + } + }); + return results; + } + _searchLogical(query) { + { + throw new Error(LOGICAL_SEARCH_UNAVAILABLE); + } + } + _searchObjectList(query) { + const searcher = createSearcher(query, this.options); + const { + keys, + records + } = this._myIndex; + const results = []; + + // List is Array + records.forEach(({ + $: item, + i: idx + }) => { + if (!isDefined(item)) { + return; + } + let matches = []; + + // Iterate over every key (i.e, path), and fetch the value at that key + keys.forEach((key, keyIndex) => { + matches.push(...this._findMatches({ + key, + value: item[keyIndex], + searcher + })); + }); + if (matches.length) { + results.push({ + idx, + item, + matches + }); + } + }); + return results; + } + _findMatches({ + key, + value, + searcher + }) { + if (!isDefined(value)) { + return []; + } + let matches = []; + if (isArray(value)) { + value.forEach(({ + v: text, + i: idx, + n: norm + }) => { + if (!isDefined(text)) { + return; + } + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + matches.push({ + score, + key, + value: text, + idx, + norm, + indices + }); + } + }); + } else { + const { + v: text, + n: norm + } = value; + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + matches.push({ + score, + key, + value: text, + norm, + indices + }); + } + } + return matches; + } + } + Fuse.version = '7.0.0'; + Fuse.createIndex = createIndex; + Fuse.parseIndex = parseIndex; + Fuse.config = Config; + { + Fuse.parseQuery = parse; + } + + var SearchByFuse = /** @class */ (function () { + function SearchByFuse(config) { + this._haystack = []; + this._fuseOptions = __assign(__assign({}, config.fuseOptions), { keys: __spreadArray([], config.searchFields, true), includeMatches: true }); + } + SearchByFuse.prototype.index = function (data) { + this._haystack = data; + if (this._fuse) { + this._fuse.setCollection(data); + } + }; + SearchByFuse.prototype.reset = function () { + this._haystack = []; + this._fuse = undefined; + }; + SearchByFuse.prototype.isEmptyIndex = function () { + return !this._haystack.length; + }; + SearchByFuse.prototype.search = function (needle) { + if (!this._fuse) { + { + this._fuse = new Fuse(this._haystack, this._fuseOptions); + } + } + var results = this._fuse.search(needle); + return results.map(function (value, i) { + return { + item: value.item, + score: value.score || 0, + rank: i + 1, // If value.score is used for sorting, this can create non-stable sorts! + }; + }); + }; + return SearchByFuse; + }()); + + function getSearcher(config) { + { + return new SearchByFuse(config); + } + } + + /** + * Helpers to create HTML elements used by Choices + * Can be overridden by providing `callbackOnCreateTemplates` option. + * `Choices.defaults.templates` allows access to the default template methods from `callbackOnCreateTemplates` + */ + var isEmptyObject = function (obj) { + // eslint-disable-next-line no-restricted-syntax + for (var prop in obj) { + if (Object.prototype.hasOwnProperty.call(obj, prop)) { + return false; + } + } + return true; + }; + var assignCustomProperties = function (el, customProperties) { + if (!customProperties) { + return; + } + var dataset = el.dataset; + if (typeof customProperties === 'string') { + dataset.customProperties = customProperties; + } + else if (typeof customProperties === 'object' && !isEmptyObject(customProperties)) { + dataset.customProperties = JSON.stringify(customProperties); + } + }; + var addAriaLabel = function (docRoot, id, element) { + var label = id && docRoot.querySelector("label[for='".concat(id, "']")); + var text = label && label.innerText; + if (text) { + element.setAttribute('aria-label', text); + } + }; + var templates = { + containerOuter: function (_a, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType, labelId) { + var containerOuter = _a.classNames.containerOuter; + var div = document.createElement('div'); + div.className = getClassNames(containerOuter).join(' '); + div.dataset.type = passedElementType; + if (dir) { + div.dir = dir; + } + if (isSelectOneElement) { + div.tabIndex = 0; + } + if (isSelectElement) { + div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox'); + if (searchEnabled) { + div.setAttribute('aria-autocomplete', 'list'); + } + else if (!labelId) { + addAriaLabel(this._docRoot, this.passedElement.element.id, div); + } + div.setAttribute('aria-haspopup', 'true'); + div.setAttribute('aria-expanded', 'false'); + } + if (labelId) { + div.setAttribute('aria-labelledby', labelId); + } + return div; + }, + containerInner: function (_a) { + var containerInner = _a.classNames.containerInner; + var div = document.createElement('div'); + div.className = getClassNames(containerInner).join(' '); + return div; + }, + itemList: function (_a, isSelectOneElement) { + var searchEnabled = _a.searchEnabled, _b = _a.classNames, list = _b.list, listSingle = _b.listSingle, listItems = _b.listItems; + var div = document.createElement('div'); + div.className = "".concat(getClassNames(list).join(' '), " ").concat(isSelectOneElement ? getClassNames(listSingle).join(' ') : getClassNames(listItems).join(' ')); + if (this._isSelectElement && searchEnabled) { + div.setAttribute('role', 'listbox'); + } + return div; + }, + placeholder: function (_a, value) { + var allowHTML = _a.allowHTML, placeholder = _a.classNames.placeholder; + var div = document.createElement('div'); + div.className = getClassNames(placeholder).join(' '); + div.innerHTML = escapeForTemplate(allowHTML, value); + return div; + }, + item: function (_a, _b, removeItemButton) { + var _c, _d, _e; + var allowHTML = _a.allowHTML, removeItemButtonAlignLeft = _a.removeItemButtonAlignLeft, removeItemIconText = _a.removeItemIconText, removeItemLabelText = _a.removeItemLabelText, _f = _a.classNames, item = _f.item, button = _f.button, highlightedState = _f.highlightedState, itemSelectable = _f.itemSelectable, placeholder = _f.placeholder; + var id = _b.id, value = _b.value, label = _b.label, labelClass = _b.labelClass, labelDescription = _b.labelDescription, customProperties = _b.customProperties, disabled = _b.disabled, highlighted = _b.highlighted, isPlaceholder = _b.placeholder; + var rawValue = unwrapStringForRaw(value); + var div = document.createElement('div'); + div.className = getClassNames(item).join(' '); + if (labelClass) { + var spanLabel = document.createElement('span'); + spanLabel.innerHTML = escapeForTemplate(allowHTML, label); + spanLabel.className = getClassNames(labelClass).join(' '); + div.appendChild(spanLabel); + } + else { + div.innerHTML = escapeForTemplate(allowHTML, label); + } + var dataset = div.dataset; + dataset.item = ''; + dataset.id = id; + dataset.value = rawValue; + if (labelClass) { + dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + dataset.labelDescription = labelDescription; + } + assignCustomProperties(div, customProperties); + if (disabled || this.containerOuter.isDisabled) { + div.setAttribute('aria-disabled', 'true'); + } + if (this._isSelectElement) { + div.setAttribute('aria-selected', 'true'); + div.setAttribute('role', 'option'); + } + if (isPlaceholder) { + (_c = div.classList).add.apply(_c, getClassNames(placeholder)); + dataset.placeholder = ''; + } + (_d = div.classList).add.apply(_d, (highlighted ? getClassNames(highlightedState) : getClassNames(itemSelectable))); + if (removeItemButton) { + if (disabled) { + (_e = div.classList).remove.apply(_e, getClassNames(itemSelectable)); + } + dataset.deletable = ''; + var removeButton = document.createElement('button'); + removeButton.type = 'button'; + removeButton.className = getClassNames(button).join(' '); + removeButton.innerHTML = resolveNoticeFunction(removeItemIconText, value); + var REMOVE_ITEM_LABEL = resolveNoticeFunction(removeItemLabelText, value); + if (REMOVE_ITEM_LABEL) { + removeButton.setAttribute('aria-label', REMOVE_ITEM_LABEL); + } + removeButton.dataset.button = ''; + if (removeItemButtonAlignLeft) { + div.insertAdjacentElement('afterbegin', removeButton); + } + else { + div.appendChild(removeButton); + } + } + return div; + }, + choiceList: function (_a, isSelectOneElement) { + var list = _a.classNames.list; + var div = document.createElement('div'); + div.className = getClassNames(list).join(' '); + if (!isSelectOneElement) { + div.setAttribute('aria-multiselectable', 'true'); + } + div.setAttribute('role', 'listbox'); + return div; + }, + choiceGroup: function (_a, _b) { + var allowHTML = _a.allowHTML, _c = _a.classNames, group = _c.group, groupHeading = _c.groupHeading, itemDisabled = _c.itemDisabled; + var id = _b.id, label = _b.label, disabled = _b.disabled; + var rawLabel = unwrapStringForRaw(label); + var div = document.createElement('div'); + div.className = "".concat(getClassNames(group).join(' '), " ").concat(disabled ? getClassNames(itemDisabled).join(' ') : ''); + div.setAttribute('role', 'group'); + var dataset = div.dataset; + dataset.group = ''; + dataset.id = id; + dataset.value = rawLabel; + if (disabled) { + div.setAttribute('aria-disabled', 'true'); + } + var heading = document.createElement('div'); + heading.className = getClassNames(groupHeading).join(' '); + heading.innerHTML = escapeForTemplate(allowHTML, label); + div.appendChild(heading); + return div; + }, + choice: function (_a, _b, selectText) { + var _c, _d, _e, _f, _g; + var allowHTML = _a.allowHTML, _h = _a.classNames, item = _h.item, itemChoice = _h.itemChoice, itemSelectable = _h.itemSelectable, selectedState = _h.selectedState, itemDisabled = _h.itemDisabled, description = _h.description, placeholder = _h.placeholder; + var id = _b.id, value = _b.value, label = _b.label, groupId = _b.groupId, elementId = _b.elementId, labelClass = _b.labelClass, labelDescription = _b.labelDescription, isDisabled = _b.disabled, isSelected = _b.selected, isPlaceholder = _b.placeholder; + var rawValue = unwrapStringForRaw(value); + var div = document.createElement('div'); + div.id = elementId; + div.className = "".concat(getClassNames(item).join(' '), " ").concat(getClassNames(itemChoice).join(' ')); + var describedBy = div; + if (labelClass) { + var spanLabel = document.createElement('span'); + spanLabel.innerHTML = escapeForTemplate(allowHTML, label); + spanLabel.className = getClassNames(labelClass).join(' '); + describedBy = spanLabel; + div.appendChild(spanLabel); + } + else { + div.innerHTML = escapeForTemplate(allowHTML, label); + } + if (labelDescription) { + var descId = "".concat(elementId, "-description"); + describedBy.setAttribute('aria-describedby', descId); + var spanDesc = document.createElement('span'); + spanDesc.innerHTML = escapeForTemplate(allowHTML, labelDescription); + spanDesc.id = descId; + (_c = spanDesc.classList).add.apply(_c, getClassNames(description)); + div.appendChild(spanDesc); + } + if (isSelected) { + (_d = div.classList).add.apply(_d, getClassNames(selectedState)); + } + if (isPlaceholder) { + (_e = div.classList).add.apply(_e, getClassNames(placeholder)); + } + var dataset = div.dataset; + var showGroupId = groupId && groupId > 0; + div.setAttribute('role', showGroupId ? 'treeitem' : 'option'); + if (showGroupId) { + dataset.groupId = "".concat(groupId); + } + dataset.choice = ''; + dataset.id = id; + dataset.value = rawValue; + dataset.selectText = selectText; + if (labelClass) { + dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + dataset.labelDescription = labelDescription; + } + if (isDisabled) { + (_f = div.classList).add.apply(_f, getClassNames(itemDisabled)); + dataset.choiceDisabled = ''; + div.setAttribute('aria-disabled', 'true'); + } + else { + (_g = div.classList).add.apply(_g, getClassNames(itemSelectable)); + dataset.choiceSelectable = ''; + } + return div; + }, + input: function (_a, placeholderValue) { + var _b = _a.classNames, input = _b.input, inputCloned = _b.inputCloned, labelId = _a.labelId; + var inp = document.createElement('input'); + inp.type = 'search'; + inp.className = "".concat(getClassNames(input).join(' '), " ").concat(getClassNames(inputCloned).join(' ')); + inp.autocomplete = 'off'; + inp.autocapitalize = 'off'; + inp.spellcheck = false; + inp.setAttribute('role', 'textbox'); + inp.setAttribute('aria-autocomplete', 'list'); + if (placeholderValue) { + inp.setAttribute('aria-label', placeholderValue); + } + else if (!labelId) { + addAriaLabel(this._docRoot, this.passedElement.element.id, inp); + } + return inp; + }, + dropdown: function (_a) { + var _b, _c; + var _d = _a.classNames, list = _d.list, listDropdown = _d.listDropdown; + var div = document.createElement('div'); + (_b = div.classList).add.apply(_b, getClassNames(list)); + (_c = div.classList).add.apply(_c, getClassNames(listDropdown)); + div.setAttribute('aria-expanded', 'false'); + return div; + }, + notice: function (_a, innerHTML, type) { + var _b = _a.classNames, item = _b.item, itemChoice = _b.itemChoice, addChoice = _b.addChoice, noResults = _b.noResults, noChoices = _b.noChoices, noticeItem = _b.notice; + if (type === void 0) { type = NoticeTypes.generic; } + var classes = __spreadArray(__spreadArray(__spreadArray([], getClassNames(item), true), getClassNames(itemChoice), true), getClassNames(noticeItem), true); + // eslint-disable-next-line default-case + switch (type) { + case NoticeTypes.addChoice: + classes.push.apply(classes, getClassNames(addChoice)); + break; + case NoticeTypes.noResults: + classes.push.apply(classes, getClassNames(noResults)); + break; + case NoticeTypes.noChoices: + classes.push.apply(classes, getClassNames(noChoices)); + break; + } + var notice = document.createElement('div'); + notice.innerHTML = innerHTML; + notice.className = classes.join(' '); + if (type === NoticeTypes.addChoice) { + notice.dataset.choiceSelectable = ''; + notice.dataset.choice = ''; + } + return notice; + }, + option: function (choice) { + // HtmlOptionElement's label value does not support HTML, so the avoid double escaping unwrap the untrusted string. + var labelValue = unwrapStringForRaw(choice.label); + var opt = new Option(labelValue, choice.value, false, choice.selected); + var labelClass = choice.labelClass, labelDescription = choice.labelDescription; + if (labelClass) { + opt.dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + opt.dataset.labelDescription = labelDescription; + } + assignCustomProperties(opt, choice.customProperties); + opt.disabled = choice.disabled; + if (choice.selected) { + opt.setAttribute('selected', ''); + } + return opt; + }, + }; + + /** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ + var IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && + '-ms-ime-align' in document.documentElement.style; + var USER_DEFAULTS = {}; + var parseDataSetId = function (element) { + if (!element) { + return undefined; + } + var id = element.dataset.id; + return id ? parseInt(id, 10) : undefined; + }; + /** + * Choices + * @author Josh Johnson + */ + var Choices = /** @class */ (function () { + function Choices(element, userConfig) { + if (element === void 0) { element = '[data-choice]'; } + if (userConfig === void 0) { userConfig = {}; } + var _this = this; + this.initialisedOK = undefined; + this._hasNonChoicePlaceholder = false; + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + var defaults = Choices.defaults; + this.config = __assign(__assign(__assign({}, defaults.allOptions), defaults.options), userConfig); + ObjectsInConfig.forEach(function (key) { + _this.config[key] = __assign(__assign(__assign({}, defaults.allOptions[key]), defaults.options[key]), userConfig[key]); + }); + var config = this.config; + if (!config.silent) { + this._validateConfig(); + } + var docRoot = config.shadowRoot || document.documentElement; + this._docRoot = docRoot; + var passedElement = typeof element === 'string' ? docRoot.querySelector(element) : element; + if (!passedElement || + typeof passedElement !== 'object' || + !(isHtmlInputElement(passedElement) || isHtmlSelectElement(passedElement))) { + if (!passedElement && typeof element === 'string') { + throw TypeError("Selector ".concat(element, " failed to find an element")); + } + throw TypeError("Expected one of the following types text|select-one|select-multiple"); + } + this._elementType = passedElement.type; + this._isTextElement = this._elementType === TEXT_TYPE; + if (this._isTextElement || config.maxItemCount !== 1) { + config.singleModeForMultiSelect = false; + } + if (config.singleModeForMultiSelect) { + this._elementType = SELECT_MULTIPLE_TYPE; + } + this._isSelectOneElement = this._elementType === SELECT_ONE_TYPE; + this._isSelectMultipleElement = this._elementType === SELECT_MULTIPLE_TYPE; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; + this._canAddUserChoices = (this._isTextElement && config.addItems) || (this._isSelectElement && config.addChoices); + if (!['auto', 'always'].includes("".concat(config.renderSelectedChoices))) { + config.renderSelectedChoices = 'auto'; + } + if (config.closeDropdownOnSelect === 'auto') { + config.closeDropdownOnSelect = this._isTextElement || this._isSelectOneElement || config.singleModeForMultiSelect; + } + else { + config.closeDropdownOnSelect = coerceBool(config.closeDropdownOnSelect); + } + if (config.placeholder) { + if (config.placeholderValue) { + this._hasNonChoicePlaceholder = true; + } + else if (passedElement.dataset.placeholder) { + this._hasNonChoicePlaceholder = true; + config.placeholderValue = passedElement.dataset.placeholder; + } + } + if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { + var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); + config.addItemFilter = re.test.bind(re); + } + if (this._isTextElement) { + this.passedElement = new WrappedInput({ + element: passedElement, + classNames: config.classNames, + }); + } + else { + var selectEl = passedElement; + this.passedElement = new WrappedSelect({ + element: selectEl, + classNames: config.classNames, + template: function (data) { return _this._templates.option(data); }, + extractPlaceholder: config.placeholder && !this._hasNonChoicePlaceholder, + }); + } + this.initialised = false; + this._store = new Store(); + this._currentValue = ''; + config.searchEnabled = (!this._isTextElement && config.searchEnabled) || this._elementType === SELECT_MULTIPLE_TYPE; + this._canSearch = config.searchEnabled; + this._isScrollingOnIe = false; + this._highlightPosition = 0; + this._wasTap = true; + this._placeholderValue = this._generatePlaceholderValue(); + this._baseId = generateId(passedElement, 'choices-'); + /** + * setting direction in cases where it's explicitly set on passedElement + * or when calculated direction is different from the document + */ + this._direction = this.passedElement.dir; + if (!this._direction) { + var elementDirection = window.getComputedStyle(this.passedElement.element).direction; + var documentDirection = window.getComputedStyle(document.documentElement).direction; + if (elementDirection !== documentDirection) { + this._direction = elementDirection; + } + } + this._idNames = { + itemChoice: 'item-choice', + }; + this._templates = defaults.templates; + this._render = this._render.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + this._onKeyUp = this._onKeyUp.bind(this); + this._onKeyDown = this._onKeyDown.bind(this); + this._onInput = this._onInput.bind(this); + this._onClick = this._onClick.bind(this); + this._onTouchMove = this._onTouchMove.bind(this); + this._onTouchEnd = this._onTouchEnd.bind(this); + this._onMouseDown = this._onMouseDown.bind(this); + this._onMouseOver = this._onMouseOver.bind(this); + this._onFormReset = this._onFormReset.bind(this); + this._onSelectKey = this._onSelectKey.bind(this); + this._onEnterKey = this._onEnterKey.bind(this); + this._onEscapeKey = this._onEscapeKey.bind(this); + this._onDirectionKey = this._onDirectionKey.bind(this); + this._onDeleteKey = this._onDeleteKey.bind(this); + // If element has already been initialised with Choices, fail silently + if (this.passedElement.isActive) { + if (!config.silent) { + console.warn('Trying to initialise Choices on element already initialised', { element: element }); + } + this.initialised = true; + this.initialisedOK = false; + return; + } + // Let's go + this.init(); + // preserve the selected item list after setup for form reset + this._initialItems = this._store.items.map(function (choice) { return choice.value; }); + } + Object.defineProperty(Choices, "defaults", { + get: function () { + return Object.preventExtensions({ + get options() { + return USER_DEFAULTS; + }, + get allOptions() { + return DEFAULT_CONFIG; + }, + get templates() { + return templates; + }, + }); + }, + enumerable: false, + configurable: true + }); + Choices.prototype.init = function () { + if (this.initialised || this.initialisedOK !== undefined) { + return; + } + this._searcher = getSearcher(this.config); + this._loadChoices(); + this._createTemplates(); + this._createElements(); + this._createStructure(); + if ((this._isTextElement && !this.config.addItems) || + this.passedElement.element.hasAttribute('disabled') || + !!this.passedElement.element.closest('fieldset:disabled')) { + this.disable(); + } + else { + this.enable(); + this._addEventListeners(); + } + // should be triggered **after** disabled state to avoid additional re-draws + this._initStore(); + this.initialised = true; + this.initialisedOK = true; + var callbackOnInit = this.config.callbackOnInit; + // Run callback if it is a function + if (callbackOnInit && typeof callbackOnInit === 'function') { + callbackOnInit.call(this); + } + }; + Choices.prototype.destroy = function () { + if (!this.initialised) { + return; + } + this._removeEventListeners(); + this.passedElement.reveal(); + this.containerOuter.unwrap(this.passedElement.element); + this._store._listeners = []; // prevents select/input value being wiped + this.clearStore(); + this._stopSearch(); + this._templates = Choices.defaults.templates; + this.initialised = false; + this.initialisedOK = undefined; + }; + Choices.prototype.enable = function () { + var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter; + if (passedElement.isDisabled) { + passedElement.enable(); + } + if (containerOuter.isDisabled) { + this._addEventListeners(); + this.input.enable(); + containerOuter.enable(); + this._render(); + } + return this; + }; + Choices.prototype.disable = function () { + var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter; + if (!passedElement.isDisabled) { + passedElement.disable(); + } + if (!containerOuter.isDisabled) { + this._removeEventListeners(); + this.input.disable(); + containerOuter.disable(); + this._render(); + } + return this; + }; + Choices.prototype.highlightItem = function (item, runEvent) { + if (runEvent === void 0) { runEvent = true; } + if (!item || !item.id) { + return this; + } + var choice = this._store.choices.find(function (c) { return c.id === item.id; }); + if (!choice || choice.highlighted) { + return this; + } + this._store.dispatch(highlightItem(choice, true)); + if (runEvent) { + this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.unhighlightItem = function (item, runEvent) { + if (runEvent === void 0) { runEvent = true; } + if (!item || !item.id) { + return this; + } + var choice = this._store.choices.find(function (c) { return c.id === item.id; }); + if (!choice || !choice.highlighted) { + return this; + } + this._store.dispatch(highlightItem(choice, false)); + if (runEvent) { + this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.highlightAll = function () { + var _this = this; + this._store.withTxn(function () { + _this._store.items.forEach(function (item) { return _this.highlightItem(item); }); + }); + return this; + }; + Choices.prototype.unhighlightAll = function () { + var _this = this; + this._store.withTxn(function () { + _this._store.items.forEach(function (item) { return _this.unhighlightItem(item); }); + }); + return this; + }; + Choices.prototype.removeActiveItemsByValue = function (value) { + var _this = this; + this._store.withTxn(function () { + _this._store.items.filter(function (item) { return item.value === value; }).forEach(function (item) { return _this._removeItem(item); }); + }); + return this; + }; + Choices.prototype.removeActiveItems = function (excludedId) { + var _this = this; + this._store.withTxn(function () { + _this._store.items.filter(function (_a) { + var id = _a.id; + return id !== excludedId; + }).forEach(function (item) { return _this._removeItem(item); }); + }); + return this; + }; + Choices.prototype.removeHighlightedItems = function (runEvent) { + var _this = this; + if (runEvent === void 0) { runEvent = false; } + this._store.withTxn(function () { + _this._store.highlightedActiveItems.forEach(function (item) { + _this._removeItem(item); + // If this action was performed by the user + // trigger the event + if (runEvent) { + _this._triggerChange(item.value); + } + }); + }); + return this; + }; + Choices.prototype.showDropdown = function (preventInputFocus) { + var _this = this; + if (this.dropdown.isActive) { + return this; + } + requestAnimationFrame(function () { + _this.dropdown.show(); + _this.containerOuter.open(_this.dropdown.distanceFromTopWindow); + if (!preventInputFocus && _this._canSearch) { + _this.input.focus(); + } + _this.passedElement.triggerEvent("showDropdown" /* EventType.showDropdown */); + }); + return this; + }; + Choices.prototype.hideDropdown = function (preventInputBlur) { + var _this = this; + if (!this.dropdown.isActive) { + return this; + } + requestAnimationFrame(function () { + _this.dropdown.hide(); + _this.containerOuter.close(); + if (!preventInputBlur && _this._canSearch) { + _this.input.removeActiveDescendant(); + _this.input.blur(); + } + _this.passedElement.triggerEvent("hideDropdown" /* EventType.hideDropdown */); + }); + return this; + }; + Choices.prototype.getValue = function (valueOnly) { + var _this = this; + if (valueOnly === void 0) { valueOnly = false; } + var values = this._store.items.reduce(function (selectedItems, item) { + var itemValue = valueOnly ? item.value : _this._getChoiceForOutput(item); + selectedItems.push(itemValue); + return selectedItems; + }, []); + return this._isSelectOneElement || this.config.singleModeForMultiSelect ? values[0] : values; + }; + Choices.prototype.setValue = function (items) { + var _this = this; + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setValue'); + return this; + } + this._store.withTxn(function () { + items.forEach(function (value) { + if (value) { + _this._addChoice(mapInputToChoice(value, false)); + } + }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.setChoiceByValue = function (value) { + var _this = this; + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoiceByValue'); + return this; + } + if (this._isTextElement) { + return this; + } + this._store.withTxn(function () { + // If only one value has been passed, convert to array + var choiceValue = Array.isArray(value) ? value : [value]; + // Loop through each value and + choiceValue.forEach(function (val) { return _this._findAndSelectChoiceByValue(val); }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + /** + * Set choices of select input via an array of objects (or function that returns array of object or promise of it), + * a value field name and a label field name. + * This behaves the same as passing items via the choices option but can be called after initialising Choices. + * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. + * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). + * + * **Input types affected:** select-one, select-multiple + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([ + * {value: 'One', label: 'Label One', disabled: true}, + * {value: 'Two', label: 'Label Two', selected: true}, + * {value: 'Three', label: 'Label Three'}, + * ], 'value', 'label', false); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices(async () => { + * try { + * const items = await fetch('/items'); + * return items.json() + * } catch(err) { + * console.error(err) + * } + * }); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([{ + * label: 'Group one', + * id: 1, + * disabled: false, + * choices: [ + * {value: 'Child One', label: 'Child One', selected: true}, + * {value: 'Child Two', label: 'Child Two', disabled: true}, + * {value: 'Child Three', label: 'Child Three'}, + * ] + * }, + * { + * label: 'Group two', + * id: 2, + * disabled: false, + * choices: [ + * {value: 'Child Four', label: 'Child Four', disabled: true}, + * {value: 'Child Five', label: 'Child Five'}, + * {value: 'Child Six', label: 'Child Six', customProperties: { + * description: 'Custom description about child six', + * random: 'Another random custom property' + * }}, + * ] + * }], 'value', 'label', false); + * ``` + */ + Choices.prototype.setChoices = function (choicesArrayOrFetcher, value, label, replaceChoices) { + var _this = this; + if (choicesArrayOrFetcher === void 0) { choicesArrayOrFetcher = []; } + if (value === void 0) { value = 'value'; } + if (label === void 0) { label = 'label'; } + if (replaceChoices === void 0) { replaceChoices = false; } + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoices'); + return this; + } + if (!this._isSelectElement) { + throw new TypeError("setChoices can't be used with INPUT based Choices"); + } + if (typeof value !== 'string' || !value) { + throw new TypeError("value parameter must be a name of 'value' field in passed objects"); + } + // Clear choices if needed + if (replaceChoices) { + this.clearChoices(); + } + if (typeof choicesArrayOrFetcher === 'function') { + // it's a choices fetcher function + var fetcher_1 = choicesArrayOrFetcher(this); + if (typeof Promise === 'function' && fetcher_1 instanceof Promise) { + // that's a promise + // eslint-disable-next-line no-promise-executor-return + return new Promise(function (resolve) { return requestAnimationFrame(resolve); }) + .then(function () { return _this._handleLoadingState(true); }) + .then(function () { return fetcher_1; }) + .then(function (data) { return _this.setChoices(data, value, label, replaceChoices); }) + .catch(function (err) { + if (!_this.config.silent) { + console.error(err); + } + }) + .then(function () { return _this._handleLoadingState(false); }) + .then(function () { return _this; }); + } + // function returned something else than promise, let's check if it's an array of choices + if (!Array.isArray(fetcher_1)) { + throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof fetcher_1)); + } + // recursion with results, it's sync and choices were cleared already + return this.setChoices(fetcher_1, value, label, false); + } + if (!Array.isArray(choicesArrayOrFetcher)) { + throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices"); + } + this.containerOuter.removeLoadingState(); + this._store.withTxn(function () { + var isDefaultValue = value === 'value'; + var isDefaultLabel = label === 'label'; + choicesArrayOrFetcher.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + var group = groupOrChoice; + if (!isDefaultLabel) { + group = __assign(__assign({}, group), { label: group[label] }); + } + _this._addGroup(mapInputToChoice(group, true)); + } + else { + var choice = groupOrChoice; + if (!isDefaultLabel || !isDefaultValue) { + choice = __assign(__assign({}, choice), { value: choice[value], label: choice[label] }); + } + _this._addChoice(mapInputToChoice(choice, false)); + } + }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.refresh = function (withEvents, selectFirstOption, deselectAll) { + var _this = this; + if (withEvents === void 0) { withEvents = false; } + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (deselectAll === void 0) { deselectAll = false; } + if (!this._isSelectElement) { + if (!this.config.silent) { + console.warn('refresh method can only be used on choices backed by a element"),this)},e.prototype.removeChoice=function(e){var t=this._store.choices.find((function(t){return t.value===e}));return t?(this._store.dispatch(function(e){return{type:r,choice:e}}(t)),this._searcher.reset(),t.selected&&this.passedElement.triggerEvent("removeItem",this._getChoiceForOutput(t)),this):this},e.prototype.clearChoices=function(){return this.passedElement.element.innerHTML="",this._store.dispatch({type:l}),this._searcher.reset(),this},e.prototype.clearStore=function(){return this._store.reset(),this._lastAddedChoiceId=0,this._lastAddedGroupId=0,this._searcher.reset(),this},e.prototype.clearInput=function(){return this.input.clear(!this._isSelectOneElement),this._clearNotice(),this._isSearching&&this._stopSearch(),this},e.prototype._validateConfig=function(){var e,t,i,n=this.config,s=(e=V,t=Object.keys(n).sort(),i=Object.keys(e).sort(),t.filter((function(e){return i.indexOf(e)<0})));s.length&&console.warn("Unknown config option(s) passed",s.join(", ")),n.allowHTML&&n.allowHtmlUserInput&&(n.addItems&&console.warn("Warning: allowHTML/allowHtmlUserInput/addItems all being true is strongly not recommended and may lead to XSS attacks"),n.addChoices&&console.warn("Warning: allowHTML/allowHtmlUserInput/addChoices all being true is strongly not recommended and may lead to XSS attacks"))},e.prototype._render=function(e){void 0===e&&(e={choices:!0,groups:!0,items:!0}),this._store.inTxn()||(this._isSelectElement&&(e.choices||e.groups)&&this._renderChoices(),e.items&&this._renderItems())},e.prototype._renderChoices=function(){var e=this;if(this.choiceList.clear(),this._canAddItems()){var t=this.config,i=this._store,n=i.activeGroups,s=i.activeChoices,o=document.createDocumentFragment(),r=!0;if(s.length){if(t.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),n.length&&!this._isSearching){if(!this._hasNonChoicePlaceholder){var a=s.filter((function(e){return e.placeholder&&-1===e.groupId}));a.length&&(o=this._createChoicesFragment(a,o))}o=this._createGroupsFragment(n,s,o)}else o=this._createChoicesFragment(s,o);r=!o.childNodes.length}var c=this._notice;r?c||(this._notice={text:b(t.noChoicesText),type:G}):c&&c.type===G&&(this._notice=void 0),this._renderNotice(),r||(this.choiceList.element.append(o),this._highlightChoice())}},e.prototype._renderItems=function(){var e=this._store.items||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes.length&&this.itemList.element.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;void 0===i&&(i=document.createDocumentFragment());var s=this.config;s.shouldSort&&e.sort(s.sorter);var o=t.filter((function(e){return!e.groupId}));return o.length&&this._createChoicesFragment(o,i,!1),e.forEach((function(e){var o=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===s.renderSelectedChoices||!t.selected)}))}(e);if(o.length){var r=n._templates.choiceGroup(n.config,e);i.appendChild(r),n._createChoicesFragment(o,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var s=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var o=this,r=o.config,a=o._isSearching,c=o._isSelectOneElement,l=r.searchResultLimit,h=r.renderChoiceLimit,u=[],d=r.appendGroupInSearch&&a;if(d&&this._store.groups.forEach((function(e){u[e.id]=e.label})),this._isSelectElement){var p=e.filter((function(e){return!e.element}));p.length&&this.passedElement.addOptions(p)}var m="auto"===r.renderSelectedChoices&&!c,f=[],v=[];e.forEach((function(e){a&&!e.rank||m&&e.selected||(s._hasNonChoicePlaceholder||!e.placeholder?v.push(e):f.push(e))})),a?v.sort(C):r.shouldSort&&v.sort(r.sorter);var _=c&&f.length?n(n([],f,!0),v,!0):v,g=_.length,y=g;return a&&l>0?y=l:h>0&&!i&&(y=h),y0){var o=u[e.groupId];o&&(n.innerHTML+=" (".concat(o,")"))}return t.appendChild(n),i0?this._store.getGroupById(e.groupId):null;return{id:e.id,highlighted:e.highlighted,labelClass:e.labelClass,labelDescription:e.labelDescription,customProperties:e.customProperties,disabled:e.disabled,active:e.active,label:e.label,placeholder:e.placeholder,value:e.value,groupValue:i&&i.label?i.label:void 0,element:e.element,keyCode:t}}},e.prototype._triggerChange=function(e){null!=e&&this.passedElement.triggerEvent("change",{value:e})},e.prototype._handleButtonAction=function(e){var t=this._store.items;if(t.length&&this.config.removeItems&&this.config.removeItemButton){var i=e&&De(e.parentNode),n=i&&t.find((function(e){return e.id===i}));if(n&&(this._removeItem(n),this._triggerChange(n.value),this._isSelectOneElement&&!this._hasNonChoicePlaceholder)){var s=this._store.choices.reverse().find((function(e){return!e.disabled&&e.placeholder}));s&&(this._addItem(s),s.value&&this._triggerChange(s.value))}}},e.prototype._handleItemAction=function(e,t){var i=this;void 0===t&&(t=!1);var n=this._store.items;if(n.length&&this.config.removeItems&&!this._isSelectOneElement){var s=De(e);s&&(n.forEach((function(e){e.id!==s||e.highlighted?!t&&e.highlighted&&i.unhighlightItem(e):i.highlightItem(e)})),this.input.focus())}},e.prototype._handleChoiceAction=function(e){var t=this,i=De(e),n=i&&this._store.getChoiceById(i);if(!n||n.disabled)return!1;var s=this.dropdown.isActive;if(!n.selected){if(!this._canAddItems())return!0;this._store.withTxn((function(){t._addItem(n,!0,!0),t.clearInput(),t.unhighlightAll()})),this._triggerChange(n.value)}return s&&this.config.closeDropdownOnSelect&&(this.hideDropdown(!0),this.containerOuter.element.focus()),!0},e.prototype._handleBackspace=function(e){var t=this.config;if(t.removeItems&&e.length){var i=e[e.length-1],n=e.some((function(e){return e.highlighted}));t.editItems&&!n&&i?(this.input.value=i.value,this.input.setWidth(),this._removeItem(i),this._triggerChange(i.value)):(n||this.highlightItem(i,!1),this.removeHighlightedItems(!0))}},e.prototype._loadChoices=function(){var e,t=this.config;if(this._isTextElement){this._presetChoices=t.items.map((function(e){return H(e,!1)}));var i=this.passedElement.value;if(i){var n=i.split(t.delimiter).map((function(e){return H(e,!1)}));this._presetChoices=this._presetChoices.concat(n)}this._presetChoices.forEach((function(e){e.selected=!0}))}else if(this._isSelectElement){this._presetChoices=t.choices.map((function(e){return H(e,!0)}));var s=this.passedElement.optionsAsChoices();s&&(e=this._presetChoices).push.apply(e,s)}},e.prototype._handleLoadingState=function(e){void 0===e&&(e=!0);var t=this.config;e?(this.disable(),this.containerOuter.addLoadingState(),this._isSelectOneElement?(this.itemList.clear(),this.itemList.element.append(this._templates.placeholder(t,t.loadingText))):this.input.placeholder=t.loadingText):(this.enable(),this.containerOuter.removeLoadingState(),this._isSelectOneElement||(this.input.placeholder=this._placeholderValue||""))},e.prototype._handleSearch=function(e){if(this.input.isFocussed){var t=this.config,i=t.searchFloor,n=t.searchChoices,s=this._store.choices.some((function(e){return!e.active}));if(null!=e&&e.length>=i){var o=n?this._searchChoices(e):0;null!==o&&this.passedElement.triggerEvent("search",{value:e,resultCount:o})}else s&&this._stopSearch()}},e.prototype._canAddItems=function(){var e=this.config,t=e.maxItemCount,i=e.maxItemText;return!(!e.singleModeForMultiSelect&&t>0&&t<=this._store.items.length&&(this._displayNotice("function"==typeof i?i(t):i,W),1))},e.prototype._canCreateItem=function(e){var t=this.config,i=!0,n="";if(i&&"function"==typeof t.addItemFilter&&!t.addItemFilter(e)&&(i=!1,n=y(t.customAddItemText,e)),i){var s=this._store.choices.find((function(i){return t.valueComparer(i.value,e)}));if(this._isSelectElement){if(s)return this._displayNotice("",W),!1}else this._isTextElement&&!t.duplicateItemsAllowed&&s&&(i=!1,n=y(t.uniqueItemText,e))}return i&&(n=y(t.addItemText,e)),n&&this._displayNotice(n,W),i},e.prototype._searchChoices=function(e){var t=e.trim().replace(/\s{2,}/," ");if(!t.length||t===this._currentValue)return null;var i=this._searcher;i.isEmptyIndex()&&i.index(this._store.searchableChoices);var n=i.search(t);this._currentValue=t,this._highlightPosition=0,this._isSearching=!0;var s=this._notice,o=s&&s.type;return o!==W&&(n.length?o===U&&this._clearNotice():this._displayNotice(b(this.config.noResultsText),U)),this._store.dispatch(function(e){return{type:a,results:e}}(n)),n.length},e.prototype._stopSearch=function(){var e=this._isSearching;this._currentValue="",this._isSearching=!1,e&&this._store.dispatch({type:c,active:!0})},e.prototype._addEventListeners=function(){var e=this._docRoot,t=this.containerOuter.element,i=this.input.element;e.addEventListener("touchend",this._onTouchEnd,!0),t.addEventListener("keydown",this._onKeyDown,!0),t.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(t.addEventListener("focus",this._onFocus,{passive:!0}),t.addEventListener("blur",this._onBlur,{passive:!0})),i.addEventListener("keyup",this._onKeyUp,{passive:!0}),i.addEventListener("input",this._onInput,{passive:!0}),i.addEventListener("focus",this._onFocus,{passive:!0}),i.addEventListener("blur",this._onBlur,{passive:!0}),i.form&&i.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=this._docRoot,t=this.containerOuter.element,i=this.input.element;e.removeEventListener("touchend",this._onTouchEnd,!0),t.removeEventListener("keydown",this._onKeyDown,!0),t.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(t.removeEventListener("focus",this._onFocus),t.removeEventListener("blur",this._onBlur)),i.removeEventListener("keyup",this._onKeyUp),i.removeEventListener("input",this._onInput),i.removeEventListener("focus",this._onFocus),i.removeEventListener("blur",this._onBlur),i.form&&i.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.items,n=this.input.isFocussed,s=this.dropdown.isActive,o=this.itemList.element.hasChildNodes(),r=1===e.key.length||2===e.key.length&&e.key.charCodeAt(0)>=55296||"Unidentified"===e.key;switch(this._isTextElement||s||(this.showDropdown(),!this.input.isFocussed&&r&&(this.input.value+=e.key)),t){case 65:return this._onSelectKey(e,o);case 13:return this._onEnterKey(e,s);case 27:return this._onEscapeKey(e,s);case 38:case 33:case 40:case 34:return this._onDirectionKey(e,s);case 8:case 46:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(){this._canSearch=this.config.searchEnabled},e.prototype._onInput=function(){var e=this.input.value;if(!e)return this._isTextElement?this.hideDropdown(!0):this._stopSearch(),void this._clearNotice();this._canAddItems()&&(this._canSearch&&this._handleSearch(e),this._canAddUserChoices&&(this._canCreateItem(e),this._isSelectElement&&(this._highlightPosition=0,this._highlightChoice())))},e.prototype._onSelectKey=function(e,t){(e.ctrlKey||e.metaKey)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t){var i=this,n=this.config,s=this.input.value,o=e.target,r=o&&o.hasAttribute("data-button");if(e.preventDefault(),r)this._handleButtonAction(o);else if(t){var a=this.dropdown.element.querySelector(I(n.classNames.highlightedState));if(!a||!this._handleChoiceAction(a))if(o&&s){if(this._canAddItems()){var c=!1;this._store.withTxn((function(){if(!(c=i._findAndSelectChoiceByValue(s,!0))){if(!i._canAddUserChoices)return;if(!i._canCreateItem(s))return;var e=_(s),t=n.allowHtmlUserInput||e===s?s:{escaped:e,raw:s};i._addChoice(H({value:t,label:t,selected:!0},!1),!0,!0),c=!0}i.clearInput(),i.unhighlightAll()})),c&&(this._triggerChange(s),n.closeDropdownOnSelect&&this.hideDropdown(!0))}}else this.hideDropdown(!0)}else(this._isSelectElement||this._notice)&&this.showDropdown()},e.prototype._onEscapeKey=function(e,t){t&&(e.stopPropagation(),this.hideDropdown(!0),this.containerOuter.element.focus())},e.prototype._onDirectionKey=function(e,t){var i,n,s,o=e.keyCode,r=e.metaKey;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=40===o||34===o?1:-1,c="[data-choice-selectable]",l=void 0;if(r||34===o||33===o)l=this.dropdown.element.querySelector(a>0?"".concat(c,":last-of-type"):c);else{var h=this.dropdown.element.querySelector(I(this.config.classNames.highlightedState));l=h?function(e,t,i){void 0===i&&(i=1);for(var n="".concat(i>0?"next":"previous","ElementSibling"),s=e[n];s;){if(s.matches(t))return s;s=s[n]}return null}(h,c,a):this.dropdown.element.querySelector(c)}l&&(i=l,n=this.choiceList.element,void 0===(s=a)&&(s=1),(s>0?n.scrollTop+n.offsetHeight>=i.offsetTop+i.offsetHeight:i.offsetTop>=n.scrollTop)||this.choiceList.scrollToChildElement(l,a),this._highlightChoice(l)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){this._isSelectOneElement||e.target.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation()),this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(Me&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild;this._isScrollingOnIe="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetXthis._highlightPosition?n[this._highlightPosition]:n[n.length-1])||(s=n[0]),(t=s.classList).add.apply(t,S(i)),s.setAttribute("aria-selected","true"),this.passedElement.triggerEvent("highlightChoice",{el:s}),this.dropdown.isActive&&(this.input.setActiveDescendant(s.id),this.containerOuter.setActiveDescendant(s.id))}},e.prototype._addItem=function(e,t,i){void 0===t&&(t=!0),void 0===i&&(i=!1);var n=e.id;if(!n)throw new TypeError("item.id must be set before _addItem is called for a choice/item");(this.config.singleModeForMultiSelect||this._isSelectOneElement)&&this.removeActiveItems(n),this._store.dispatch(function(e){return{type:u,item:e}}(e)),t&&(this.passedElement.triggerEvent("addItem",this._getChoiceForOutput(e)),i&&this.passedElement.triggerEvent("choice",this._getChoiceForOutput(e)))},e.prototype._removeItem=function(e){e.id&&(this._store.dispatch(function(e){return{type:d,item:e}}(e)),this.passedElement.triggerEvent("removeItem",this._getChoiceForOutput(e)))},e.prototype._addChoice=function(e,t,i){if(void 0===t&&(t=!0),void 0===i&&(i=!1),e.id)throw new TypeError("Can not re-add a choice which has already been added");this._lastAddedChoiceId++,e.id=this._lastAddedChoiceId,e.elementId="".concat(this._baseId,"-").concat(this._idNames.itemChoice,"-").concat(e.id);var n=this.config,s=n.prependValue,r=n.appendValue;s&&(e.value=s+e.value),r&&(e.value+=r.toString()),(s||r)&&e.element&&(e.element.value=e.value),this._store.dispatch(function(e){return{type:o,choice:e}}(e)),e.selected&&this._addItem(e,t,i)},e.prototype._addGroup=function(e,t){var i=this;if(void 0===t&&(t=!0),e.id)throw new TypeError("Can not re-add a group which has already been added");if(this._store.dispatch(function(e){return{type:h,group:e}}(e)),e.choices){var n=e;this._lastAddedGroupId++,n.id=this._lastAddedGroupId;var s=e.id,o=e.choices;n.choices=[],o.forEach((function(n){n.groupId=s,e.disabled&&(n.disabled=!0),i._addChoice(n,t)}))}},e.prototype._createTemplates=function(){var e=this,t=this.config.callbackOnCreateTemplates,i={};t&&"function"==typeof t&&(i=t.call(this,g,w));var n={};Object.keys(this._templates).forEach((function(t){n[t]=t in i?i[t].bind(e):e._templates[t].bind(e)})),this._templates=n},e.prototype._createElements=function(){var e=this._templates,t=this.config,i=t.position,n=t.classNames,s=this._elementType;this.containerOuter=new N({element:e.containerOuter(t,this._direction,this._isSelectElement,this._isSelectOneElement,t.searchEnabled,s,t.labelId),classNames:n,type:s,position:i}),this.containerInner=new N({element:e.containerInner(t),classNames:n,type:s,position:i}),this.input=new M({element:e.input(t,this._placeholderValue),classNames:n,type:s,preventPaste:!t.paste}),this.choiceList=new F({element:e.choiceList(t,this._isSelectOneElement)}),this.itemList=new F({element:e.itemList(t,this._isSelectOneElement)}),this.dropdown=new O({element:e.dropdown(t),classNames:n,type:s})},e.prototype._createStructure=function(){var e=this,t=e.containerInner,i=e.containerOuter,n=e.passedElement,s=e.dropdown,o=e.input;n.conceal(),t.wrap(n.element),i.wrap(t.element),this._isSelectOneElement?o.placeholder=this.config.searchPlaceholderValue||"":(this._placeholderValue&&(o.placeholder=this._placeholderValue),o.setWidth()),i.element.appendChild(t.element),i.element.appendChild(s.element),t.element.appendChild(this.itemList.element),s.element.appendChild(this.choiceList.element),this._isSelectOneElement?this.config.searchEnabled&&s.element.insertBefore(o.element,s.element.firstChild):t.element.appendChild(o.element),this._highlightPosition=0,this._isSearching=!1},e.prototype._initStore=function(){var e=this;this._store.subscribe(this._render),this._store.withTxn((function(){e._addPredefinedChoices(e._presetChoices,e._isSelectOneElement&&!e._hasNonChoicePlaceholder,!1)})),this._isSelectOneElement&&this._hasNonChoicePlaceholder&&this._render({choices:!1,groups:!1,items:!0})},e.prototype._addPredefinedChoices=function(e,t,i){var n=this;void 0===t&&(t=!1),void 0===i&&(i=!0),t&&-1===e.findIndex((function(e){return e.selected}))&&e.some((function(e){return!e.disabled&&!("choices"in e)&&(e.selected=!0,!0)})),e.forEach((function(e){"choices"in e?n._isSelectElement&&n._addGroup(e,i):n._addChoice(e,i)}))},e.prototype._findAndSelectChoiceByValue=function(e,t){var i=this;void 0===t&&(t=!1);var n=this._store.choices.find((function(t){return i.config.valueComparer(t.value,e)}));return!(!n||n.disabled||n.selected||(this._addItem(n,!0,t),0))},e.prototype._generatePlaceholderValue=function(){var e=this.config;if(!e.placeholder)return null;if(this._hasNonChoicePlaceholder)return e.placeholderValue;if(this._isSelectElement){var t=this.passedElement.placeholderOption;return t?t.text:null}return null},e.prototype._warnChoicesInitFailed=function(e){if(!this.config.silent){if(!this.initialised)throw new TypeError("".concat(e," called on a non-initialised instance of Choices"));if(!this.initialisedOK)throw new TypeError("".concat(e," called for an element which has multiple instances of Choices initialised on it"))}},e.version="11.0.0-rc7",e}()})); diff --git a/public/assets/scripts/choices.search-basic.mjs b/public/assets/scripts/choices.search-basic.mjs new file mode 100644 index 000000000..e1d33eb76 --- /dev/null +++ b/public/assets/scripts/choices.search-basic.mjs @@ -0,0 +1,4707 @@ +/*! choices.js v11.0.0-rc7 | © 2024 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ + +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol */ + +var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || { + __proto__: [] + } instanceof Array && function (d, b) { + d.__proto__ = b; + } || function (d, b) { + for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); +}; +function __extends(d, b) { + if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} +var __assign = function () { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +function __spreadArray(to, from, pack) { + if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { + if (ar || !(i in from)) { + if (!ar) ar = Array.prototype.slice.call(from, 0, i); + ar[i] = from[i]; + } + } + return to.concat(ar || Array.prototype.slice.call(from)); +} +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +var ActionType = { + ADD_CHOICE: 'ADD_CHOICE', + REMOVE_CHOICE: 'REMOVE_CHOICE', + FILTER_CHOICES: 'FILTER_CHOICES', + ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', + CLEAR_CHOICES: 'CLEAR_CHOICES', + ADD_GROUP: 'ADD_GROUP', + ADD_ITEM: 'ADD_ITEM', + REMOVE_ITEM: 'REMOVE_ITEM', + HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', +}; + +var ObjectsInConfig = ['fuseOptions', 'classNames']; + +var addChoice = function (choice) { return ({ + type: ActionType.ADD_CHOICE, + choice: choice, +}); }; +var removeChoice = function (choice) { return ({ + type: ActionType.REMOVE_CHOICE, + choice: choice, +}); }; +var filterChoices = function (results) { return ({ + type: ActionType.FILTER_CHOICES, + results: results, +}); }; +var activateChoices = function (active) { + return ({ + type: ActionType.ACTIVATE_CHOICES, + active: active, + }); +}; +var clearChoices = function () { return ({ + type: ActionType.CLEAR_CHOICES, +}); }; + +var addGroup = function (group) { return ({ + type: ActionType.ADD_GROUP, + group: group, +}); }; + +var addItem = function (item) { return ({ + type: ActionType.ADD_ITEM, + item: item, +}); }; +var removeItem = function (item) { return ({ + type: ActionType.REMOVE_ITEM, + item: item, +}); }; +var highlightItem = function (item, highlighted) { return ({ + type: ActionType.HIGHLIGHT_ITEM, + item: item, + highlighted: highlighted, +}); }; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +var getRandomNumber = function (min, max) { return Math.floor(Math.random() * (max - min) + min); }; +var generateChars = function (length) { + return Array.from({ length: length }, function () { return getRandomNumber(0, 36).toString(36); }).join(''); +}; +var generateId = function (element, prefix) { + var id = element.id || (element.name && "".concat(element.name, "-").concat(generateChars(2))) || generateChars(4); + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = "".concat(prefix, "-").concat(id); + return id; +}; +var getAdjacentEl = function (startEl, selector, direction) { + if (direction === void 0) { direction = 1; } + var prop = "".concat(direction > 0 ? 'next' : 'previous', "ElementSibling"); + var sibling = startEl[prop]; + while (sibling) { + if (sibling.matches(selector)) { + return sibling; + } + sibling = sibling[prop]; + } + return null; +}; +var isScrolledIntoView = function (element, parent, direction) { + if (direction === void 0) { direction = 1; } + var isVisible; + if (direction > 0) { + // In view from bottom + isVisible = parent.scrollTop + parent.offsetHeight >= element.offsetTop + element.offsetHeight; + } + else { + // In view from top + isVisible = element.offsetTop >= parent.scrollTop; + } + return isVisible; +}; +var sanitise = function (value) { + if (typeof value !== 'string') { + if (value === null || value === undefined) { + return ''; + } + if (typeof value === 'object') { + if ('raw' in value) { + return sanitise(value.raw); + } + if ('trusted' in value) { + return value.trusted; + } + } + return value; + } + return value + .replace(/&/g, '&') + .replace(/>/g, '>') + .replace(/ 0 ? this.element.scrollTop + elementPos - listScrollPosition : element.offsetTop; + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); + }; + List.prototype._scrollDown = function (scrollPos, strength, destination) { + var easing = (destination - scrollPos) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos + distance; + }; + List.prototype._scrollUp = function (scrollPos, strength, destination) { + var easing = (scrollPos - destination) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos - distance; + }; + List.prototype._animateScroll = function (destination, direction) { + var _this = this; + var strength = SCROLLING_SPEED; + var choiceListScrollTop = this.element.scrollTop; + var continueAnimation = false; + if (direction > 0) { + this._scrollDown(choiceListScrollTop, strength, destination); + if (choiceListScrollTop < destination) { + continueAnimation = true; + } + } + else { + this._scrollUp(choiceListScrollTop, strength, destination); + if (choiceListScrollTop > destination) { + continueAnimation = true; + } + } + if (continueAnimation) { + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); + } + }; + return List; +}()); + +var WrappedElement = /** @class */ (function () { + function WrappedElement(_a) { + var element = _a.element, classNames = _a.classNames; + this.element = element; + this.classNames = classNames; + this.isDisabled = false; + } + Object.defineProperty(WrappedElement.prototype, "isActive", { + get: function () { + return this.element.dataset.choice === 'active'; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(WrappedElement.prototype, "dir", { + get: function () { + return this.element.dir; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(WrappedElement.prototype, "value", { + get: function () { + return this.element.value; + }, + set: function (value) { + this.element.setAttribute('value', value); + this.element.value = value; + }, + enumerable: false, + configurable: true + }); + WrappedElement.prototype.conceal = function () { + var _a; + var el = this.element; + // Hide passed input + (_a = el.classList).add.apply(_a, getClassNames(this.classNames.input)); + el.hidden = true; + // Remove element from tab index + el.tabIndex = -1; + // Backup original styles if any + var origStyle = el.getAttribute('style'); + if (origStyle) { + el.setAttribute('data-choice-orig-style', origStyle); + } + el.setAttribute('data-choice', 'active'); + }; + WrappedElement.prototype.reveal = function () { + var _a; + var el = this.element; + // Reinstate passed element + (_a = el.classList).remove.apply(_a, getClassNames(this.classNames.input)); + el.hidden = false; + el.removeAttribute('tabindex'); + // Recover original styles if any + var origStyle = el.getAttribute('data-choice-orig-style'); + if (origStyle) { + el.removeAttribute('data-choice-orig-style'); + el.setAttribute('style', origStyle); + } + else { + el.removeAttribute('style'); + } + el.removeAttribute('data-choice'); + }; + WrappedElement.prototype.enable = function () { + var element = this.element; + element.removeAttribute('disabled'); + element.disabled = false; + this.isDisabled = false; + }; + WrappedElement.prototype.disable = function () { + var element = this.element; + element.setAttribute('disabled', ''); + element.disabled = true; + this.isDisabled = true; + }; + WrappedElement.prototype.triggerEvent = function (eventType, data) { + dispatchEvent(this.element, eventType, data || {}); + }; + return WrappedElement; +}()); + +var WrappedInput = /** @class */ (function (_super) { + __extends(WrappedInput, _super); + function WrappedInput() { + return _super !== null && _super.apply(this, arguments) || this; + } + return WrappedInput; +}(WrappedElement)); + +var coerceBool = function (arg, defaultValue) { + if (defaultValue === void 0) { defaultValue = true; } + return typeof arg === 'undefined' ? defaultValue : !!arg; +}; +var stringToHtmlClass = function (input) { + if (typeof input === 'string') { + // eslint-disable-next-line no-param-reassign + input = input.split(' ').filter(function (s) { return s.length; }); + } + if (Array.isArray(input) && input.length) { + return input; + } + return undefined; +}; +var mapInputToChoice = function (value, allowGroup) { + if (typeof value === 'string') { + var result_1 = mapInputToChoice({ + value: value, + label: value, + }, false); + return result_1; + } + var groupOrChoice = value; + if ('choices' in groupOrChoice) { + if (!allowGroup) { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup + throw new TypeError("optGroup is not allowed"); + } + var group = groupOrChoice; + var choices = group.choices.map(function (e) { return mapInputToChoice(e, false); }); + var result_2 = { + id: 0, // actual ID will be assigned during _addGroup + label: unwrapStringForRaw(group.label) || group.value, + active: !!choices.length, + disabled: !!group.disabled, + choices: choices, + }; + return result_2; + } + var choice = groupOrChoice; + var result = { + id: 0, // actual ID will be assigned during _addChoice + groupId: 0, // actual ID will be assigned during _addGroup but before _addChoice + score: 0, // used in search + rank: 0, // used in search, stable sort order + value: choice.value, + label: choice.label || choice.value, + active: coerceBool(choice.active), + selected: coerceBool(choice.selected, false), + disabled: coerceBool(choice.disabled, false), + placeholder: coerceBool(choice.placeholder, false), + highlighted: false, + labelClass: stringToHtmlClass(choice.labelClass), + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + }; + return result; +}; + +var isHtmlInputElement = function (e) { return e.tagName === 'INPUT'; }; +var isHtmlSelectElement = function (e) { return e.tagName === 'SELECT'; }; +var isHtmlOption = function (e) { return e.tagName === 'OPTION'; }; +var isHtmlOptgroup = function (e) { return e.tagName === 'OPTGROUP'; }; + +var WrappedSelect = /** @class */ (function (_super) { + __extends(WrappedSelect, _super); + function WrappedSelect(_a) { + var element = _a.element, classNames = _a.classNames, template = _a.template, extractPlaceholder = _a.extractPlaceholder; + var _this = _super.call(this, { element: element, classNames: classNames }) || this; + _this.template = template; + _this.extractPlaceholder = extractPlaceholder; + return _this; + } + Object.defineProperty(WrappedSelect.prototype, "placeholderOption", { + get: function () { + return (this.element.querySelector('option[value=""]') || + // Backward compatibility layer for the non-standard placeholder attribute supported in older versions. + this.element.querySelector('option[placeholder]')); + }, + enumerable: false, + configurable: true + }); + WrappedSelect.prototype.addOptions = function (choices) { + var _this = this; + var fragment = document.createDocumentFragment(); + choices.forEach(function (obj) { + var choice = obj; + if (choice.element) { + return; + } + var option = _this.template(choice); + fragment.appendChild(option); + choice.element = option; + }); + this.element.appendChild(fragment); + }; + WrappedSelect.prototype.optionsAsChoices = function () { + var _this = this; + var choices = []; + this.element.querySelectorAll(':scope > option, :scope > optgroup').forEach(function (e) { + if (isHtmlOption(e)) { + choices.push(_this._optionToChoice(e)); + } + else if (isHtmlOptgroup(e)) { + choices.push(_this._optgroupToChoice(e)); + } + // todo: hr as empty optgroup, requires displaying empty opt-groups to be useful + }); + return choices; + }; + // eslint-disable-next-line class-methods-use-this + WrappedSelect.prototype._optionToChoice = function (option) { + // option.value returns the label if there is no value attribute, which can break legacy placeholder attribute support + if (!option.hasAttribute('value') && option.hasAttribute('placeholder')) { + option.setAttribute('value', ''); + option.value = ''; + } + var dataset = option.dataset; + return { + id: 0, + groupId: 0, + score: 0, + rank: 0, + value: option.value, + label: option.innerHTML, + element: option, + active: true, + // this returns true if nothing is selected on initial load, which will break placeholder support + selected: this.extractPlaceholder ? option.selected : option.hasAttribute('selected'), + disabled: option.disabled, + highlighted: false, + placeholder: this.extractPlaceholder && (!option.value || option.hasAttribute('placeholder')), + labelClass: typeof dataset.labelClass !== 'undefined' ? stringToHtmlClass(dataset.labelClass) : undefined, + labelDescription: typeof dataset.labelDescription !== 'undefined' ? dataset.labelDescription : undefined, + customProperties: parseCustomProperties(dataset.customProperties), + }; + }; + WrappedSelect.prototype._optgroupToChoice = function (optgroup) { + var _this = this; + var options = optgroup.querySelectorAll('option'); + var choices = Array.from(options).map(function (option) { return _this._optionToChoice(option); }); + return { + id: 0, + label: optgroup.label || '', + element: optgroup, + active: !!choices.length, + disabled: optgroup.disabled, + choices: choices, + }; + }; + return WrappedSelect; +}(WrappedElement)); + +var DEFAULT_CLASSNAMES = { + containerOuter: ['choices'], + containerInner: ['choices__inner'], + input: ['choices__input'], + inputCloned: ['choices__input--cloned'], + list: ['choices__list'], + listItems: ['choices__list--multiple'], + listSingle: ['choices__list--single'], + listDropdown: ['choices__list--dropdown'], + item: ['choices__item'], + itemSelectable: ['choices__item--selectable'], + itemDisabled: ['choices__item--disabled'], + itemChoice: ['choices__item--choice'], + description: ['choices__description'], + placeholder: ['choices__placeholder'], + group: ['choices__group'], + groupHeading: ['choices__heading'], + button: ['choices__button'], + activeState: ['is-active'], + focusState: ['is-focused'], + openState: ['is-open'], + disabledState: ['is-disabled'], + highlightedState: ['is-highlighted'], + selectedState: ['is-selected'], + flippedState: ['is-flipped'], + loadingState: ['is-loading'], + notice: ['choices__notice'], + addChoice: ['choices__item--selectable', 'add-choice'], + noResults: ['has-no-results'], + noChoices: ['has-no-choices'], +}; +var DEFAULT_CONFIG = { + items: [], + choices: [], + silent: false, + renderChoiceLimit: -1, + maxItemCount: -1, + closeDropdownOnSelect: 'auto', + singleModeForMultiSelect: false, + addChoices: false, + addItems: true, + addItemFilter: function (value) { return !!value && value !== ''; }, + removeItems: true, + removeItemButton: false, + removeItemButtonAlignLeft: false, + editItems: false, + allowHTML: false, + allowHtmlUserInput: false, + duplicateItemsAllowed: true, + delimiter: ',', + paste: true, + searchEnabled: true, + searchChoices: true, + searchFloor: 1, + searchResultLimit: 4, + searchFields: ['label', 'value'], + position: 'auto', + resetScrollPosition: true, + shouldSort: true, + shouldSortItems: false, + sorter: sortByAlpha, + shadowRoot: null, + placeholder: true, + placeholderValue: null, + searchPlaceholderValue: null, + prependValue: null, + appendValue: null, + renderSelectedChoices: 'auto', + loadingText: 'Loading...', + noResultsText: 'No results found', + noChoicesText: 'No choices to choose from', + itemSelectText: 'Press to select', + uniqueItemText: 'Only unique values can be added', + customAddItemText: 'Only values matching specific conditions can be added', + addItemText: function (value) { return "Press Enter to add \"".concat(value, "\""); }, + removeItemIconText: function () { return "Remove item"; }, + removeItemLabelText: function (value) { return "Remove item: ".concat(value); }, + maxItemText: function (maxItemCount) { return "Only ".concat(maxItemCount, " values can be added"); }, + valueComparer: function (value1, value2) { return value1 === value2; }, + fuseOptions: { + includeScore: true, + }, + labelId: '', + callbackOnInit: null, + callbackOnCreateTemplates: null, + classNames: DEFAULT_CLASSNAMES, + appendGroupInSearch: false, +}; + +function items(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_ITEM: { + var item = action.item; + item.selected = true; + var el = item.element; + if (el) { + el.selected = true; + el.setAttribute('selected', ''); + } + state.push(item); + state.forEach(function (choice) { + choice.highlighted = false; + }); + break; + } + case ActionType.REMOVE_ITEM: { + var item_1 = action.item; + item_1.selected = false; + var el = item_1.element; + if (el) { + el.selected = false; + el.removeAttribute('selected'); + // For a select-one, if all options are deselected, the first item is selected. To set a black value, select.value needs to be set + var select = el.parentElement; + if (select && isHtmlSelectElement(select) && select.type === SELECT_ONE_TYPE) { + select.value = ''; + } + } + state = state.filter(function (choice) { return choice.id !== item_1.id; }); + break; + } + case ActionType.REMOVE_CHOICE: { + state = state.filter(function (item) { return item.id !== action.choice.id; }); + break; + } + case ActionType.HIGHLIGHT_ITEM: { + var highlightItemAction_1 = action; + state.forEach(function (choice) { + if (choice.id === highlightItemAction_1.item.id) { + choice.highlighted = highlightItemAction_1.highlighted; + } + }); + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; +} + +function groups(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_GROUP: { + state.push(action.group); + break; + } + case ActionType.CLEAR_CHOICES: { + state = []; + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; +} + +/* eslint-disable */ +function choices(s, action) { + var state = s; + var update = true; + switch (action.type) { + case ActionType.ADD_CHOICE: { + /* + A disabled choice appears in the choice dropdown but cannot be selected + A selected choice has been added to the passed input's value (added as an item) + An active choice appears within the choice dropdown + */ + state.push(action.choice); + break; + } + case ActionType.REMOVE_CHOICE: { + state = state.filter(function (obj) { return obj.id !== action.choice.id; }); + break; + } + case ActionType.ADD_ITEM: + case ActionType.REMOVE_ITEM: { + break; + } + case ActionType.FILTER_CHOICES: { + // avoid O(n^2) algorithm complexity when searching/filtering choices + var scoreLookup_1 = []; + action.results.forEach(function (result) { + scoreLookup_1[result.item.id] = result; + }); + state.forEach(function (choice) { + var result = scoreLookup_1[choice.id]; + if (result !== undefined) { + choice.score = result.score; + choice.rank = result.rank; + choice.active = true; + } + else { + choice.score = 0; + choice.rank = 0; + choice.active = false; + } + }); + break; + } + case ActionType.ACTIVATE_CHOICES: { + state.forEach(function (choice) { + choice.active = action.active; + }); + break; + } + case ActionType.CLEAR_CHOICES: { + state = []; + break; + } + default: { + update = false; + break; + } + } + return { state: state, update: update }; +} + +var reducers = { + groups: groups, + items: items, + choices: choices, +}; +var Store = /** @class */ (function () { + function Store() { + this._state = this.defaultState; + this._listeners = []; + this._txn = 0; + } + Object.defineProperty(Store.prototype, "defaultState", { + // eslint-disable-next-line class-methods-use-this + get: function () { + return { + groups: [], + items: [], + choices: [], + }; + }, + enumerable: false, + configurable: true + }); + // eslint-disable-next-line class-methods-use-this + Store.prototype.changeSet = function (init) { + return { + groups: init, + items: init, + choices: init, + }; + }; + Store.prototype.reset = function () { + this._state = this.defaultState; + var changes = this.changeSet(true); + if (this._txn) { + this._changeSet = changes; + } + else { + this._listeners.forEach(function (l) { return l(changes); }); + } + }; + Store.prototype.subscribe = function (onChange) { + this._listeners.push(onChange); + }; + Store.prototype.dispatch = function (action) { + var state = this._state; + var hasChanges = false; + var changes = this._changeSet || this.changeSet(false); + Object.keys(reducers).forEach(function (key) { + var stateUpdate = reducers[key](state[key], action); + if (stateUpdate.update) { + hasChanges = true; + changes[key] = true; + state[key] = stateUpdate.state; + } + }); + if (hasChanges) { + if (this._txn) { + this._changeSet = changes; + } + else { + this._listeners.forEach(function (l) { return l(changes); }); + } + } + }; + Store.prototype.withTxn = function (func) { + this._txn++; + try { + func(); + } + finally { + this._txn = Math.max(0, this._txn - 1); + if (!this._txn) { + var changeSet_1 = this._changeSet; + if (changeSet_1) { + this._changeSet = undefined; + this._listeners.forEach(function (l) { return l(changeSet_1); }); + } + } + } + }; + Object.defineProperty(Store.prototype, "state", { + /** + * Get store object + */ + get: function () { + return this._state; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "items", { + /** + * Get items from store + */ + get: function () { + return this.state.items; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "highlightedActiveItems", { + /** + * Get highlighted items from store + */ + get: function () { + return this.items.filter(function (item) { return !item.disabled && item.active && item.highlighted; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "choices", { + /** + * Get choices from store + */ + get: function () { + return this.state.choices; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeChoices", { + /** + * Get active choices from store + */ + get: function () { + return this.choices.filter(function (choice) { return choice.active; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "searchableChoices", { + /** + * Get choices that can be searched (excluding placeholders) + */ + get: function () { + return this.choices.filter(function (choice) { return !choice.disabled && !choice.placeholder; }); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "groups", { + /** + * Get groups from store + */ + get: function () { + return this.state.groups; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeGroups", { + /** + * Get active groups from store + */ + get: function () { + var _this = this; + return this.state.groups.filter(function (group) { + var isActive = group.active && !group.disabled; + var hasActiveOptions = _this.state.choices.some(function (choice) { return choice.active && !choice.disabled; }); + return isActive && hasActiveOptions; + }, []); + }, + enumerable: false, + configurable: true + }); + Store.prototype.inTxn = function () { + return this._txn > 0; + }; + /** + * Get single choice by it's ID + */ + Store.prototype.getChoiceById = function (id) { + return this.activeChoices.find(function (choice) { return choice.id === id; }); + }; + /** + * Get group by group id + */ + Store.prototype.getGroupById = function (id) { + return this.groups.find(function (group) { return group.id === id; }); + }; + return Store; +}()); + +var NoticeTypes = { + noChoices: 'no-choices', + noResults: 'no-results', + addChoice: 'add-choice', + generic: '', +}; + +function _defineProperty(e, r, t) { + return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { + value: t, + enumerable: !0, + configurable: !0, + writable: !0 + }) : e[r] = t, e; +} +function ownKeys(e, r) { + var t = Object.keys(e); + if (Object.getOwnPropertySymbols) { + var o = Object.getOwnPropertySymbols(e); + r && (o = o.filter(function (r) { + return Object.getOwnPropertyDescriptor(e, r).enumerable; + })), t.push.apply(t, o); + } + return t; +} +function _objectSpread2(e) { + for (var r = 1; r < arguments.length; r++) { + var t = null != arguments[r] ? arguments[r] : {}; + r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { + _defineProperty(e, r, t[r]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { + Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); + }); + } + return e; +} +function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); +} +function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; +} + +/** + * Fuse.js v7.0.0 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2023 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +function isArray(value) { + return !Array.isArray ? getTag(value) === '[object Array]' : Array.isArray(value); +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/baseToString.js +const INFINITY = 1 / 0; +function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + let result = value + ''; + return result == '0' && 1 / value == -INFINITY ? '-0' : result; +} +function toString(value) { + return value == null ? '' : baseToString(value); +} +function isString(value) { + return typeof value === 'string'; +} +function isNumber(value) { + return typeof value === 'number'; +} + +// Adapted from: https://github.com/lodash/lodash/blob/master/isBoolean.js +function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && getTag(value) == '[object Boolean]'; +} +function isObject(value) { + return typeof value === 'object'; +} + +// Checks if `value` is object-like. +function isObjectLike(value) { + return isObject(value) && value !== null; +} +function isDefined(value) { + return value !== undefined && value !== null; +} +function isBlank(value) { + return !value.trim().length; +} + +// Gets the `toStringTag` of `value`. +// Adapted from: https://github.com/lodash/lodash/blob/master/.internal/getTag.js +function getTag(value) { + return value == null ? value === undefined ? '[object Undefined]' : '[object Null]' : Object.prototype.toString.call(value); +} +const EXTENDED_SEARCH_UNAVAILABLE = 'Extended search is not available'; +const LOGICAL_SEARCH_UNAVAILABLE = 'Logical search is not available'; +const INCORRECT_INDEX_TYPE = "Incorrect 'index' type"; +const LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY = key => `Invalid value for key ${key}`; +const PATTERN_LENGTH_TOO_LARGE = max => `Pattern length exceeds max of ${max}.`; +const MISSING_KEY_PROPERTY = name => `Missing ${name} property in key`; +const INVALID_KEY_WEIGHT_VALUE = key => `Property 'weight' in key '${key}' must be a positive integer`; +const hasOwn = Object.prototype.hasOwnProperty; +class KeyStore { + constructor(keys) { + this._keys = []; + this._keyMap = {}; + let totalWeight = 0; + keys.forEach(key => { + let obj = createKey(key); + this._keys.push(obj); + this._keyMap[obj.id] = obj; + totalWeight += obj.weight; + }); + + // Normalize weights so that their sum is equal to 1 + this._keys.forEach(key => { + key.weight /= totalWeight; + }); + } + get(keyId) { + return this._keyMap[keyId]; + } + keys() { + return this._keys; + } + toJSON() { + return JSON.stringify(this._keys); + } +} +function createKey(key) { + let path = null; + let id = null; + let src = null; + let weight = 1; + let getFn = null; + if (isString(key) || isArray(key)) { + src = key; + path = createKeyPath(key); + id = createKeyId(key); + } else { + if (!hasOwn.call(key, 'name')) { + throw new Error(MISSING_KEY_PROPERTY('name')); + } + const name = key.name; + src = name; + if (hasOwn.call(key, 'weight')) { + weight = key.weight; + if (weight <= 0) { + throw new Error(INVALID_KEY_WEIGHT_VALUE(name)); + } + } + path = createKeyPath(name); + id = createKeyId(name); + getFn = key.getFn; + } + return { + path, + id, + weight, + src, + getFn + }; +} +function createKeyPath(key) { + return isArray(key) ? key : key.split('.'); +} +function createKeyId(key) { + return isArray(key) ? key.join('.') : key; +} +function get(obj, path) { + let list = []; + let arr = false; + const deepGet = (obj, path, index) => { + if (!isDefined(obj)) { + return; + } + if (!path[index]) { + // If there's no path left, we've arrived at the object we care about. + list.push(obj); + } else { + let key = path[index]; + const value = obj[key]; + if (!isDefined(value)) { + return; + } + + // If we're at the last value in the path, and if it's a string/number/bool, + // add it to the list + if (index === path.length - 1 && (isString(value) || isNumber(value) || isBoolean(value))) { + list.push(toString(value)); + } else if (isArray(value)) { + arr = true; + // Search each item in the array. + for (let i = 0, len = value.length; i < len; i += 1) { + deepGet(value[i], path, index + 1); + } + } else if (path.length) { + // An object. Recurse further. + deepGet(value, path, index + 1); + } + } + }; + + // Backwards compatibility (since path used to be a string) + deepGet(obj, isString(path) ? path.split('.') : path, 0); + return arr ? list : list[0]; +} +const MatchOptions = { + // Whether the matches should be included in the result set. When `true`, each record in the result + // set will include the indices of the matched characters. + // These can consequently be used for highlighting purposes. + includeMatches: false, + // When `true`, the matching function will continue to the end of a search pattern even if + // a perfect match has already been located in the string. + findAllMatches: false, + // Minimum number of characters that must be matched before a result is considered a match + minMatchCharLength: 1 +}; +const BasicOptions = { + // When `true`, the algorithm continues searching to the end of the input even if a perfect + // match is found before the end of the same input. + isCaseSensitive: false, + // When true, the matching function will continue to the end of a search pattern even if + includeScore: false, + // List of properties that will be searched. This also supports nested properties. + keys: [], + // Whether to sort the result list, by score + shouldSort: true, + // Default sort function: sort by ascending score, ascending index + sortFn: (a, b) => a.score === b.score ? a.idx < b.idx ? -1 : 1 : a.score < b.score ? -1 : 1 +}; +const FuzzyOptions = { + // Approximately where in the text is the pattern expected to be found? + location: 0, + // At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match + // (of both letters and location), a threshold of '1.0' would match anything. + threshold: 0.6, + // Determines how close the match must be to the fuzzy location (specified above). + // An exact letter match which is 'distance' characters away from the fuzzy location + // would score as a complete mismatch. A distance of '0' requires the match be at + // the exact location specified, a threshold of '1000' would require a perfect match + // to be within 800 characters of the fuzzy location to be found using a 0.8 threshold. + distance: 100 +}; +const AdvancedOptions = { + // When `true`, it enables the use of unix-like search commands + useExtendedSearch: false, + // The get function to use when fetching an object's properties. + // The default will search nested paths *ie foo.bar.baz* + getFn: get, + // When `true`, search will ignore `location` and `distance`, so it won't matter + // where in the string the pattern appears. + // More info: https://fusejs.io/concepts/scoring-theory.html#fuzziness-score + ignoreLocation: false, + // When `true`, the calculation for the relevance score (used for sorting) will + // ignore the field-length norm. + // More info: https://fusejs.io/concepts/scoring-theory.html#field-length-norm + ignoreFieldNorm: false, + // The weight to determine how much field length norm effects scoring. + fieldNormWeight: 1 +}; +var Config = _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, BasicOptions), MatchOptions), FuzzyOptions), AdvancedOptions); +const SPACE = /[^ ]+/g; + +// Field-length norm: the shorter the field, the higher the weight. +// Set to 3 decimals to reduce index size. +function norm(weight = 1, mantissa = 3) { + const cache = new Map(); + const m = Math.pow(10, mantissa); + return { + get(value) { + const numTokens = value.match(SPACE).length; + if (cache.has(numTokens)) { + return cache.get(numTokens); + } + + // Default function is 1/sqrt(x), weight makes that variable + const norm = 1 / Math.pow(numTokens, 0.5 * weight); + + // In place of `toFixed(mantissa)`, for faster computation + const n = parseFloat(Math.round(norm * m) / m); + cache.set(numTokens, n); + return n; + }, + clear() { + cache.clear(); + } + }; +} +class FuseIndex { + constructor({ + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight + } = {}) { + this.norm = norm(fieldNormWeight, 3); + this.getFn = getFn; + this.isCreated = false; + this.setIndexRecords(); + } + setSources(docs = []) { + this.docs = docs; + } + setIndexRecords(records = []) { + this.records = records; + } + setKeys(keys = []) { + this.keys = keys; + this._keysMap = {}; + keys.forEach((key, idx) => { + this._keysMap[key.id] = idx; + }); + } + create() { + if (this.isCreated || !this.docs.length) { + return; + } + this.isCreated = true; + + // List is Array + if (isString(this.docs[0])) { + this.docs.forEach((doc, docIndex) => { + this._addString(doc, docIndex); + }); + } else { + // List is Array + this.docs.forEach((doc, docIndex) => { + this._addObject(doc, docIndex); + }); + } + this.norm.clear(); + } + // Adds a doc to the end of the index + add(doc) { + const idx = this.size(); + if (isString(doc)) { + this._addString(doc, idx); + } else { + this._addObject(doc, idx); + } + } + // Removes the doc at the specified index of the index + removeAt(idx) { + this.records.splice(idx, 1); + + // Change ref index of every subsquent doc + for (let i = idx, len = this.size(); i < len; i += 1) { + this.records[i].i -= 1; + } + } + getValueForItemAtKeyId(item, keyId) { + return item[this._keysMap[keyId]]; + } + size() { + return this.records.length; + } + _addString(doc, docIndex) { + if (!isDefined(doc) || isBlank(doc)) { + return; + } + let record = { + v: doc, + i: docIndex, + n: this.norm.get(doc) + }; + this.records.push(record); + } + _addObject(doc, docIndex) { + let record = { + i: docIndex, + $: {} + }; + + // Iterate over every key (i.e, path), and fetch the value at that key + this.keys.forEach((key, keyIndex) => { + let value = key.getFn ? key.getFn(doc) : this.getFn(doc, key.path); + if (!isDefined(value)) { + return; + } + if (isArray(value)) { + let subRecords = []; + const stack = [{ + nestedArrIndex: -1, + value + }]; + while (stack.length) { + const { + nestedArrIndex, + value + } = stack.pop(); + if (!isDefined(value)) { + continue; + } + if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + i: nestedArrIndex, + n: this.norm.get(value) + }; + subRecords.push(subRecord); + } else if (isArray(value)) { + value.forEach((item, k) => { + stack.push({ + nestedArrIndex: k, + value: item + }); + }); + } else ; + } + record.$[keyIndex] = subRecords; + } else if (isString(value) && !isBlank(value)) { + let subRecord = { + v: value, + n: this.norm.get(value) + }; + record.$[keyIndex] = subRecord; + } + }); + this.records.push(record); + } + toJSON() { + return { + keys: this.keys, + records: this.records + }; + } +} +function createIndex(keys, docs, { + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight +} = {}) { + const myIndex = new FuseIndex({ + getFn, + fieldNormWeight + }); + myIndex.setKeys(keys.map(createKey)); + myIndex.setSources(docs); + myIndex.create(); + return myIndex; +} +function parseIndex(data, { + getFn = Config.getFn, + fieldNormWeight = Config.fieldNormWeight +} = {}) { + const { + keys, + records + } = data; + const myIndex = new FuseIndex({ + getFn, + fieldNormWeight + }); + myIndex.setKeys(keys); + myIndex.setIndexRecords(records); + return myIndex; +} +function computeScore$1(pattern, { + errors = 0, + currentLocation = 0, + expectedLocation = 0, + distance = Config.distance, + ignoreLocation = Config.ignoreLocation +} = {}) { + const accuracy = errors / pattern.length; + if (ignoreLocation) { + return accuracy; + } + const proximity = Math.abs(expectedLocation - currentLocation); + if (!distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + return accuracy + proximity / distance; +} +function convertMaskToIndices(matchmask = [], minMatchCharLength = Config.minMatchCharLength) { + let indices = []; + let start = -1; + let end = -1; + let i = 0; + for (let len = matchmask.length; i < len; i += 1) { + let match = matchmask[i]; + if (match && start === -1) { + start = i; + } else if (!match && start !== -1) { + end = i - 1; + if (end - start + 1 >= minMatchCharLength) { + indices.push([start, end]); + } + start = -1; + } + } + + // (i-1 - start) + 1 => i - start + if (matchmask[i - 1] && i - start >= minMatchCharLength) { + indices.push([start, i - 1]); + } + return indices; +} + +// Machine word size +const MAX_BITS = 32; +function search(text, pattern, patternAlphabet, { + location = Config.location, + distance = Config.distance, + threshold = Config.threshold, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + includeMatches = Config.includeMatches, + ignoreLocation = Config.ignoreLocation +} = {}) { + if (pattern.length > MAX_BITS) { + throw new Error(PATTERN_LENGTH_TOO_LARGE(MAX_BITS)); + } + const patternLen = pattern.length; + // Set starting location at beginning text and initialize the alphabet. + const textLen = text.length; + // Handle the case when location > text.length + const expectedLocation = Math.max(0, Math.min(location, textLen)); + // Highest score beyond which we give up. + let currentThreshold = threshold; + // Is there a nearby exact match? (speedup) + let bestLocation = expectedLocation; + + // Performance: only computer matches when the minMatchCharLength > 1 + // OR if `includeMatches` is true. + const computeMatches = minMatchCharLength > 1 || includeMatches; + // A mask of the matches, used for building the indices + const matchMask = computeMatches ? Array(textLen) : []; + let index; + + // Get all exact matches, here for speed up + while ((index = text.indexOf(pattern, bestLocation)) > -1) { + let score = computeScore$1(pattern, { + currentLocation: index, + expectedLocation, + distance, + ignoreLocation + }); + currentThreshold = Math.min(score, currentThreshold); + bestLocation = index + patternLen; + if (computeMatches) { + let i = 0; + while (i < patternLen) { + matchMask[index + i] = 1; + i += 1; + } + } + } + + // Reset the best location + bestLocation = -1; + let lastBitArr = []; + let finalScore = 1; + let binMax = patternLen + textLen; + const mask = 1 << patternLen - 1; + for (let i = 0; i < patternLen; i += 1) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from the match location we can stray + // at this error level. + let binMin = 0; + let binMid = binMax; + while (binMin < binMid) { + const score = computeScore$1(pattern, { + errors: i, + currentLocation: expectedLocation + binMid, + expectedLocation, + distance, + ignoreLocation + }); + if (score <= currentThreshold) { + binMin = binMid; + } else { + binMax = binMid; + } + binMid = Math.floor((binMax - binMin) / 2 + binMin); + } + + // Use the result from this iteration as the maximum for the next. + binMax = binMid; + let start = Math.max(1, expectedLocation - binMid + 1); + let finish = findAllMatches ? textLen : Math.min(expectedLocation + binMid, textLen) + patternLen; + + // Initialize the bit array + let bitArr = Array(finish + 2); + bitArr[finish + 1] = (1 << i) - 1; + for (let j = finish; j >= start; j -= 1) { + let currentLocation = j - 1; + let charMatch = patternAlphabet[text.charAt(currentLocation)]; + if (computeMatches) { + // Speed up: quick bool to int conversion (i.e, `charMatch ? 1 : 0`) + matchMask[currentLocation] = +!!charMatch; + } + + // First pass: exact match + bitArr[j] = (bitArr[j + 1] << 1 | 1) & charMatch; + + // Subsequent passes: fuzzy match + if (i) { + bitArr[j] |= (lastBitArr[j + 1] | lastBitArr[j]) << 1 | 1 | lastBitArr[j + 1]; + } + if (bitArr[j] & mask) { + finalScore = computeScore$1(pattern, { + errors: i, + currentLocation, + expectedLocation, + distance, + ignoreLocation + }); + + // This match will almost certainly be better than any existing match. + // But check anyway. + if (finalScore <= currentThreshold) { + // Indeed it is + currentThreshold = finalScore; + bestLocation = currentLocation; + + // Already passed `loc`, downhill from here on in. + if (bestLocation <= expectedLocation) { + break; + } + + // When passing `bestLocation`, don't exceed our current distance from `expectedLocation`. + start = Math.max(1, 2 * expectedLocation - bestLocation); + } + } + } + + // No hope for a (better) match at greater error levels. + const score = computeScore$1(pattern, { + errors: i + 1, + currentLocation: expectedLocation, + expectedLocation, + distance, + ignoreLocation + }); + if (score > currentThreshold) { + break; + } + lastBitArr = bitArr; + } + const result = { + isMatch: bestLocation >= 0, + // Count exact matches (those with a score of 0) to be "almost" exact + score: Math.max(0.001, finalScore) + }; + if (computeMatches) { + const indices = convertMaskToIndices(matchMask, minMatchCharLength); + if (!indices.length) { + result.isMatch = false; + } else if (includeMatches) { + result.indices = indices; + } + } + return result; +} +function createPatternAlphabet(pattern) { + let mask = {}; + for (let i = 0, len = pattern.length; i < len; i += 1) { + const char = pattern.charAt(i); + mask[char] = (mask[char] || 0) | 1 << len - i - 1; + } + return mask; +} +class BitapSearch { + constructor(pattern, { + location = Config.location, + threshold = Config.threshold, + distance = Config.distance, + includeMatches = Config.includeMatches, + findAllMatches = Config.findAllMatches, + minMatchCharLength = Config.minMatchCharLength, + isCaseSensitive = Config.isCaseSensitive, + ignoreLocation = Config.ignoreLocation + } = {}) { + this.options = { + location, + threshold, + distance, + includeMatches, + findAllMatches, + minMatchCharLength, + isCaseSensitive, + ignoreLocation + }; + this.pattern = isCaseSensitive ? pattern : pattern.toLowerCase(); + this.chunks = []; + if (!this.pattern.length) { + return; + } + const addChunk = (pattern, startIndex) => { + this.chunks.push({ + pattern, + alphabet: createPatternAlphabet(pattern), + startIndex + }); + }; + const len = this.pattern.length; + if (len > MAX_BITS) { + let i = 0; + const remainder = len % MAX_BITS; + const end = len - remainder; + while (i < end) { + addChunk(this.pattern.substr(i, MAX_BITS), i); + i += MAX_BITS; + } + if (remainder) { + const startIndex = len - MAX_BITS; + addChunk(this.pattern.substr(startIndex), startIndex); + } + } else { + addChunk(this.pattern, 0); + } + } + searchIn(text) { + const { + isCaseSensitive, + includeMatches + } = this.options; + if (!isCaseSensitive) { + text = text.toLowerCase(); + } + + // Exact match + if (this.pattern === text) { + let result = { + isMatch: true, + score: 0 + }; + if (includeMatches) { + result.indices = [[0, text.length - 1]]; + } + return result; + } + + // Otherwise, use Bitap algorithm + const { + location, + distance, + threshold, + findAllMatches, + minMatchCharLength, + ignoreLocation + } = this.options; + let allIndices = []; + let totalScore = 0; + let hasMatches = false; + this.chunks.forEach(({ + pattern, + alphabet, + startIndex + }) => { + const { + isMatch, + score, + indices + } = search(text, pattern, alphabet, { + location: location + startIndex, + distance, + threshold, + findAllMatches, + minMatchCharLength, + includeMatches, + ignoreLocation + }); + if (isMatch) { + hasMatches = true; + } + totalScore += score; + if (isMatch && indices) { + allIndices = [...allIndices, ...indices]; + } + }); + let result = { + isMatch: hasMatches, + score: hasMatches ? totalScore / this.chunks.length : 1 + }; + if (hasMatches && includeMatches) { + result.indices = allIndices; + } + return result; + } +} +const registeredSearchers = []; +function createSearcher(pattern, options) { + for (let i = 0, len = registeredSearchers.length; i < len; i += 1) { + let searcherClass = registeredSearchers[i]; + if (searcherClass.condition(pattern, options)) { + return new searcherClass(pattern, options); + } + } + return new BitapSearch(pattern, options); +} +const LogicalOperator = { + AND: '$and', + OR: '$or' +}; +const KeyType = { + PATH: '$path', + PATTERN: '$val' +}; +const isExpression = query => !!(query[LogicalOperator.AND] || query[LogicalOperator.OR]); +const isPath = query => !!query[KeyType.PATH]; +const isLeaf = query => !isArray(query) && isObject(query) && !isExpression(query); +const convertToExplicit = query => ({ + [LogicalOperator.AND]: Object.keys(query).map(key => ({ + [key]: query[key] + })) +}); + +// When `auto` is `true`, the parse function will infer and initialize and add +// the appropriate `Searcher` instance +function parse(query, options, { + auto = true +} = {}) { + const next = query => { + let keys = Object.keys(query); + const isQueryPath = isPath(query); + if (!isQueryPath && keys.length > 1 && !isExpression(query)) { + return next(convertToExplicit(query)); + } + if (isLeaf(query)) { + const key = isQueryPath ? query[KeyType.PATH] : keys[0]; + const pattern = isQueryPath ? query[KeyType.PATTERN] : query[key]; + if (!isString(pattern)) { + throw new Error(LOGICAL_SEARCH_INVALID_QUERY_FOR_KEY(key)); + } + const obj = { + keyId: createKeyId(key), + pattern + }; + if (auto) { + obj.searcher = createSearcher(pattern, options); + } + return obj; + } + let node = { + children: [], + operator: keys[0] + }; + keys.forEach(key => { + const value = query[key]; + if (isArray(value)) { + value.forEach(item => { + node.children.push(next(item)); + }); + } + }); + return node; + }; + if (!isExpression(query)) { + query = convertToExplicit(query); + } + return next(query); +} + +// Practical scoring function +function computeScore(results, { + ignoreFieldNorm = Config.ignoreFieldNorm +}) { + results.forEach(result => { + let totalScore = 1; + result.matches.forEach(({ + key, + norm, + score + }) => { + const weight = key ? key.weight : null; + totalScore *= Math.pow(score === 0 && weight ? Number.EPSILON : score, (weight || 1) * (ignoreFieldNorm ? 1 : norm)); + }); + result.score = totalScore; + }); +} +function transformMatches(result, data) { + const matches = result.matches; + data.matches = []; + if (!isDefined(matches)) { + return; + } + matches.forEach(match => { + if (!isDefined(match.indices) || !match.indices.length) { + return; + } + const { + indices, + value + } = match; + let obj = { + indices, + value + }; + if (match.key) { + obj.key = match.key.src; + } + if (match.idx > -1) { + obj.refIndex = match.idx; + } + data.matches.push(obj); + }); +} +function transformScore(result, data) { + data.score = result.score; +} +function format(results, docs, { + includeMatches = Config.includeMatches, + includeScore = Config.includeScore +} = {}) { + const transformers = []; + if (includeMatches) transformers.push(transformMatches); + if (includeScore) transformers.push(transformScore); + return results.map(result => { + const { + idx + } = result; + const data = { + item: docs[idx], + refIndex: idx + }; + if (transformers.length) { + transformers.forEach(transformer => { + transformer(result, data); + }); + } + return data; + }); +} +class Fuse { + constructor(docs, options = {}, index) { + this.options = _objectSpread2(_objectSpread2({}, Config), options); + if (this.options.useExtendedSearch && !false) { + throw new Error(EXTENDED_SEARCH_UNAVAILABLE); + } + this._keyStore = new KeyStore(this.options.keys); + this.setCollection(docs, index); + } + setCollection(docs, index) { + this._docs = docs; + if (index && !(index instanceof FuseIndex)) { + throw new Error(INCORRECT_INDEX_TYPE); + } + this._myIndex = index || createIndex(this.options.keys, this._docs, { + getFn: this.options.getFn, + fieldNormWeight: this.options.fieldNormWeight + }); + } + add(doc) { + if (!isDefined(doc)) { + return; + } + this._docs.push(doc); + this._myIndex.add(doc); + } + remove(predicate = ( /* doc, idx */) => false) { + const results = []; + for (let i = 0, len = this._docs.length; i < len; i += 1) { + const doc = this._docs[i]; + if (predicate(doc, i)) { + this.removeAt(i); + i -= 1; + len -= 1; + results.push(doc); + } + } + return results; + } + removeAt(idx) { + this._docs.splice(idx, 1); + this._myIndex.removeAt(idx); + } + getIndex() { + return this._myIndex; + } + search(query, { + limit = -1 + } = {}) { + const { + includeMatches, + includeScore, + shouldSort, + sortFn, + ignoreFieldNorm + } = this.options; + let results = isString(query) ? isString(this._docs[0]) ? this._searchStringList(query) : this._searchObjectList(query) : this._searchLogical(query); + computeScore(results, { + ignoreFieldNorm + }); + if (shouldSort) { + results.sort(sortFn); + } + if (isNumber(limit) && limit > -1) { + results = results.slice(0, limit); + } + return format(results, this._docs, { + includeMatches, + includeScore + }); + } + _searchStringList(query) { + const searcher = createSearcher(query, this.options); + const { + records + } = this._myIndex; + const results = []; + + // Iterate over every string in the index + records.forEach(({ + v: text, + i: idx, + n: norm + }) => { + if (!isDefined(text)) { + return; + } + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + results.push({ + item: text, + idx, + matches: [{ + score, + value: text, + norm, + indices + }] + }); + } + }); + return results; + } + _searchLogical(query) { + { + throw new Error(LOGICAL_SEARCH_UNAVAILABLE); + } + } + _searchObjectList(query) { + const searcher = createSearcher(query, this.options); + const { + keys, + records + } = this._myIndex; + const results = []; + + // List is Array + records.forEach(({ + $: item, + i: idx + }) => { + if (!isDefined(item)) { + return; + } + let matches = []; + + // Iterate over every key (i.e, path), and fetch the value at that key + keys.forEach((key, keyIndex) => { + matches.push(...this._findMatches({ + key, + value: item[keyIndex], + searcher + })); + }); + if (matches.length) { + results.push({ + idx, + item, + matches + }); + } + }); + return results; + } + _findMatches({ + key, + value, + searcher + }) { + if (!isDefined(value)) { + return []; + } + let matches = []; + if (isArray(value)) { + value.forEach(({ + v: text, + i: idx, + n: norm + }) => { + if (!isDefined(text)) { + return; + } + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + matches.push({ + score, + key, + value: text, + idx, + norm, + indices + }); + } + }); + } else { + const { + v: text, + n: norm + } = value; + const { + isMatch, + score, + indices + } = searcher.searchIn(text); + if (isMatch) { + matches.push({ + score, + key, + value: text, + norm, + indices + }); + } + } + return matches; + } +} +Fuse.version = '7.0.0'; +Fuse.createIndex = createIndex; +Fuse.parseIndex = parseIndex; +Fuse.config = Config; +{ + Fuse.parseQuery = parse; +} + +var SearchByFuse = /** @class */ (function () { + function SearchByFuse(config) { + this._haystack = []; + this._fuseOptions = __assign(__assign({}, config.fuseOptions), { keys: __spreadArray([], config.searchFields, true), includeMatches: true }); + } + SearchByFuse.prototype.index = function (data) { + this._haystack = data; + if (this._fuse) { + this._fuse.setCollection(data); + } + }; + SearchByFuse.prototype.reset = function () { + this._haystack = []; + this._fuse = undefined; + }; + SearchByFuse.prototype.isEmptyIndex = function () { + return !this._haystack.length; + }; + SearchByFuse.prototype.search = function (needle) { + if (!this._fuse) { + { + this._fuse = new Fuse(this._haystack, this._fuseOptions); + } + } + var results = this._fuse.search(needle); + return results.map(function (value, i) { + return { + item: value.item, + score: value.score || 0, + rank: i + 1, // If value.score is used for sorting, this can create non-stable sorts! + }; + }); + }; + return SearchByFuse; +}()); + +function getSearcher(config) { + { + return new SearchByFuse(config); + } +} + +/** + * Helpers to create HTML elements used by Choices + * Can be overridden by providing `callbackOnCreateTemplates` option. + * `Choices.defaults.templates` allows access to the default template methods from `callbackOnCreateTemplates` + */ +var isEmptyObject = function (obj) { + // eslint-disable-next-line no-restricted-syntax + for (var prop in obj) { + if (Object.prototype.hasOwnProperty.call(obj, prop)) { + return false; + } + } + return true; +}; +var assignCustomProperties = function (el, customProperties) { + if (!customProperties) { + return; + } + var dataset = el.dataset; + if (typeof customProperties === 'string') { + dataset.customProperties = customProperties; + } + else if (typeof customProperties === 'object' && !isEmptyObject(customProperties)) { + dataset.customProperties = JSON.stringify(customProperties); + } +}; +var addAriaLabel = function (docRoot, id, element) { + var label = id && docRoot.querySelector("label[for='".concat(id, "']")); + var text = label && label.innerText; + if (text) { + element.setAttribute('aria-label', text); + } +}; +var templates = { + containerOuter: function (_a, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType, labelId) { + var containerOuter = _a.classNames.containerOuter; + var div = document.createElement('div'); + div.className = getClassNames(containerOuter).join(' '); + div.dataset.type = passedElementType; + if (dir) { + div.dir = dir; + } + if (isSelectOneElement) { + div.tabIndex = 0; + } + if (isSelectElement) { + div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox'); + if (searchEnabled) { + div.setAttribute('aria-autocomplete', 'list'); + } + else if (!labelId) { + addAriaLabel(this._docRoot, this.passedElement.element.id, div); + } + div.setAttribute('aria-haspopup', 'true'); + div.setAttribute('aria-expanded', 'false'); + } + if (labelId) { + div.setAttribute('aria-labelledby', labelId); + } + return div; + }, + containerInner: function (_a) { + var containerInner = _a.classNames.containerInner; + var div = document.createElement('div'); + div.className = getClassNames(containerInner).join(' '); + return div; + }, + itemList: function (_a, isSelectOneElement) { + var searchEnabled = _a.searchEnabled, _b = _a.classNames, list = _b.list, listSingle = _b.listSingle, listItems = _b.listItems; + var div = document.createElement('div'); + div.className = "".concat(getClassNames(list).join(' '), " ").concat(isSelectOneElement ? getClassNames(listSingle).join(' ') : getClassNames(listItems).join(' ')); + if (this._isSelectElement && searchEnabled) { + div.setAttribute('role', 'listbox'); + } + return div; + }, + placeholder: function (_a, value) { + var allowHTML = _a.allowHTML, placeholder = _a.classNames.placeholder; + var div = document.createElement('div'); + div.className = getClassNames(placeholder).join(' '); + div.innerHTML = escapeForTemplate(allowHTML, value); + return div; + }, + item: function (_a, _b, removeItemButton) { + var _c, _d, _e; + var allowHTML = _a.allowHTML, removeItemButtonAlignLeft = _a.removeItemButtonAlignLeft, removeItemIconText = _a.removeItemIconText, removeItemLabelText = _a.removeItemLabelText, _f = _a.classNames, item = _f.item, button = _f.button, highlightedState = _f.highlightedState, itemSelectable = _f.itemSelectable, placeholder = _f.placeholder; + var id = _b.id, value = _b.value, label = _b.label, labelClass = _b.labelClass, labelDescription = _b.labelDescription, customProperties = _b.customProperties, disabled = _b.disabled, highlighted = _b.highlighted, isPlaceholder = _b.placeholder; + var rawValue = unwrapStringForRaw(value); + var div = document.createElement('div'); + div.className = getClassNames(item).join(' '); + if (labelClass) { + var spanLabel = document.createElement('span'); + spanLabel.innerHTML = escapeForTemplate(allowHTML, label); + spanLabel.className = getClassNames(labelClass).join(' '); + div.appendChild(spanLabel); + } + else { + div.innerHTML = escapeForTemplate(allowHTML, label); + } + var dataset = div.dataset; + dataset.item = ''; + dataset.id = id; + dataset.value = rawValue; + if (labelClass) { + dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + dataset.labelDescription = labelDescription; + } + assignCustomProperties(div, customProperties); + if (disabled || this.containerOuter.isDisabled) { + div.setAttribute('aria-disabled', 'true'); + } + if (this._isSelectElement) { + div.setAttribute('aria-selected', 'true'); + div.setAttribute('role', 'option'); + } + if (isPlaceholder) { + (_c = div.classList).add.apply(_c, getClassNames(placeholder)); + dataset.placeholder = ''; + } + (_d = div.classList).add.apply(_d, (highlighted ? getClassNames(highlightedState) : getClassNames(itemSelectable))); + if (removeItemButton) { + if (disabled) { + (_e = div.classList).remove.apply(_e, getClassNames(itemSelectable)); + } + dataset.deletable = ''; + var removeButton = document.createElement('button'); + removeButton.type = 'button'; + removeButton.className = getClassNames(button).join(' '); + removeButton.innerHTML = resolveNoticeFunction(removeItemIconText, value); + var REMOVE_ITEM_LABEL = resolveNoticeFunction(removeItemLabelText, value); + if (REMOVE_ITEM_LABEL) { + removeButton.setAttribute('aria-label', REMOVE_ITEM_LABEL); + } + removeButton.dataset.button = ''; + if (removeItemButtonAlignLeft) { + div.insertAdjacentElement('afterbegin', removeButton); + } + else { + div.appendChild(removeButton); + } + } + return div; + }, + choiceList: function (_a, isSelectOneElement) { + var list = _a.classNames.list; + var div = document.createElement('div'); + div.className = getClassNames(list).join(' '); + if (!isSelectOneElement) { + div.setAttribute('aria-multiselectable', 'true'); + } + div.setAttribute('role', 'listbox'); + return div; + }, + choiceGroup: function (_a, _b) { + var allowHTML = _a.allowHTML, _c = _a.classNames, group = _c.group, groupHeading = _c.groupHeading, itemDisabled = _c.itemDisabled; + var id = _b.id, label = _b.label, disabled = _b.disabled; + var rawLabel = unwrapStringForRaw(label); + var div = document.createElement('div'); + div.className = "".concat(getClassNames(group).join(' '), " ").concat(disabled ? getClassNames(itemDisabled).join(' ') : ''); + div.setAttribute('role', 'group'); + var dataset = div.dataset; + dataset.group = ''; + dataset.id = id; + dataset.value = rawLabel; + if (disabled) { + div.setAttribute('aria-disabled', 'true'); + } + var heading = document.createElement('div'); + heading.className = getClassNames(groupHeading).join(' '); + heading.innerHTML = escapeForTemplate(allowHTML, label); + div.appendChild(heading); + return div; + }, + choice: function (_a, _b, selectText) { + var _c, _d, _e, _f, _g; + var allowHTML = _a.allowHTML, _h = _a.classNames, item = _h.item, itemChoice = _h.itemChoice, itemSelectable = _h.itemSelectable, selectedState = _h.selectedState, itemDisabled = _h.itemDisabled, description = _h.description, placeholder = _h.placeholder; + var id = _b.id, value = _b.value, label = _b.label, groupId = _b.groupId, elementId = _b.elementId, labelClass = _b.labelClass, labelDescription = _b.labelDescription, isDisabled = _b.disabled, isSelected = _b.selected, isPlaceholder = _b.placeholder; + var rawValue = unwrapStringForRaw(value); + var div = document.createElement('div'); + div.id = elementId; + div.className = "".concat(getClassNames(item).join(' '), " ").concat(getClassNames(itemChoice).join(' ')); + var describedBy = div; + if (labelClass) { + var spanLabel = document.createElement('span'); + spanLabel.innerHTML = escapeForTemplate(allowHTML, label); + spanLabel.className = getClassNames(labelClass).join(' '); + describedBy = spanLabel; + div.appendChild(spanLabel); + } + else { + div.innerHTML = escapeForTemplate(allowHTML, label); + } + if (labelDescription) { + var descId = "".concat(elementId, "-description"); + describedBy.setAttribute('aria-describedby', descId); + var spanDesc = document.createElement('span'); + spanDesc.innerHTML = escapeForTemplate(allowHTML, labelDescription); + spanDesc.id = descId; + (_c = spanDesc.classList).add.apply(_c, getClassNames(description)); + div.appendChild(spanDesc); + } + if (isSelected) { + (_d = div.classList).add.apply(_d, getClassNames(selectedState)); + } + if (isPlaceholder) { + (_e = div.classList).add.apply(_e, getClassNames(placeholder)); + } + var dataset = div.dataset; + var showGroupId = groupId && groupId > 0; + div.setAttribute('role', showGroupId ? 'treeitem' : 'option'); + if (showGroupId) { + dataset.groupId = "".concat(groupId); + } + dataset.choice = ''; + dataset.id = id; + dataset.value = rawValue; + dataset.selectText = selectText; + if (labelClass) { + dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + dataset.labelDescription = labelDescription; + } + if (isDisabled) { + (_f = div.classList).add.apply(_f, getClassNames(itemDisabled)); + dataset.choiceDisabled = ''; + div.setAttribute('aria-disabled', 'true'); + } + else { + (_g = div.classList).add.apply(_g, getClassNames(itemSelectable)); + dataset.choiceSelectable = ''; + } + return div; + }, + input: function (_a, placeholderValue) { + var _b = _a.classNames, input = _b.input, inputCloned = _b.inputCloned, labelId = _a.labelId; + var inp = document.createElement('input'); + inp.type = 'search'; + inp.className = "".concat(getClassNames(input).join(' '), " ").concat(getClassNames(inputCloned).join(' ')); + inp.autocomplete = 'off'; + inp.autocapitalize = 'off'; + inp.spellcheck = false; + inp.setAttribute('role', 'textbox'); + inp.setAttribute('aria-autocomplete', 'list'); + if (placeholderValue) { + inp.setAttribute('aria-label', placeholderValue); + } + else if (!labelId) { + addAriaLabel(this._docRoot, this.passedElement.element.id, inp); + } + return inp; + }, + dropdown: function (_a) { + var _b, _c; + var _d = _a.classNames, list = _d.list, listDropdown = _d.listDropdown; + var div = document.createElement('div'); + (_b = div.classList).add.apply(_b, getClassNames(list)); + (_c = div.classList).add.apply(_c, getClassNames(listDropdown)); + div.setAttribute('aria-expanded', 'false'); + return div; + }, + notice: function (_a, innerHTML, type) { + var _b = _a.classNames, item = _b.item, itemChoice = _b.itemChoice, addChoice = _b.addChoice, noResults = _b.noResults, noChoices = _b.noChoices, noticeItem = _b.notice; + if (type === void 0) { type = NoticeTypes.generic; } + var classes = __spreadArray(__spreadArray(__spreadArray([], getClassNames(item), true), getClassNames(itemChoice), true), getClassNames(noticeItem), true); + // eslint-disable-next-line default-case + switch (type) { + case NoticeTypes.addChoice: + classes.push.apply(classes, getClassNames(addChoice)); + break; + case NoticeTypes.noResults: + classes.push.apply(classes, getClassNames(noResults)); + break; + case NoticeTypes.noChoices: + classes.push.apply(classes, getClassNames(noChoices)); + break; + } + var notice = document.createElement('div'); + notice.innerHTML = innerHTML; + notice.className = classes.join(' '); + if (type === NoticeTypes.addChoice) { + notice.dataset.choiceSelectable = ''; + notice.dataset.choice = ''; + } + return notice; + }, + option: function (choice) { + // HtmlOptionElement's label value does not support HTML, so the avoid double escaping unwrap the untrusted string. + var labelValue = unwrapStringForRaw(choice.label); + var opt = new Option(labelValue, choice.value, false, choice.selected); + var labelClass = choice.labelClass, labelDescription = choice.labelDescription; + if (labelClass) { + opt.dataset.labelClass = getClassNames(labelClass).join(' '); + } + if (labelDescription) { + opt.dataset.labelDescription = labelDescription; + } + assignCustomProperties(opt, choice.customProperties); + opt.disabled = choice.disabled; + if (choice.selected) { + opt.setAttribute('selected', ''); + } + return opt; + }, +}; + +/** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ +var IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && + '-ms-ime-align' in document.documentElement.style; +var USER_DEFAULTS = {}; +var parseDataSetId = function (element) { + if (!element) { + return undefined; + } + var id = element.dataset.id; + return id ? parseInt(id, 10) : undefined; +}; +/** + * Choices + * @author Josh Johnson + */ +var Choices = /** @class */ (function () { + function Choices(element, userConfig) { + if (element === void 0) { element = '[data-choice]'; } + if (userConfig === void 0) { userConfig = {}; } + var _this = this; + this.initialisedOK = undefined; + this._hasNonChoicePlaceholder = false; + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + var defaults = Choices.defaults; + this.config = __assign(__assign(__assign({}, defaults.allOptions), defaults.options), userConfig); + ObjectsInConfig.forEach(function (key) { + _this.config[key] = __assign(__assign(__assign({}, defaults.allOptions[key]), defaults.options[key]), userConfig[key]); + }); + var config = this.config; + if (!config.silent) { + this._validateConfig(); + } + var docRoot = config.shadowRoot || document.documentElement; + this._docRoot = docRoot; + var passedElement = typeof element === 'string' ? docRoot.querySelector(element) : element; + if (!passedElement || + typeof passedElement !== 'object' || + !(isHtmlInputElement(passedElement) || isHtmlSelectElement(passedElement))) { + if (!passedElement && typeof element === 'string') { + throw TypeError("Selector ".concat(element, " failed to find an element")); + } + throw TypeError("Expected one of the following types text|select-one|select-multiple"); + } + this._elementType = passedElement.type; + this._isTextElement = this._elementType === TEXT_TYPE; + if (this._isTextElement || config.maxItemCount !== 1) { + config.singleModeForMultiSelect = false; + } + if (config.singleModeForMultiSelect) { + this._elementType = SELECT_MULTIPLE_TYPE; + } + this._isSelectOneElement = this._elementType === SELECT_ONE_TYPE; + this._isSelectMultipleElement = this._elementType === SELECT_MULTIPLE_TYPE; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; + this._canAddUserChoices = (this._isTextElement && config.addItems) || (this._isSelectElement && config.addChoices); + if (!['auto', 'always'].includes("".concat(config.renderSelectedChoices))) { + config.renderSelectedChoices = 'auto'; + } + if (config.closeDropdownOnSelect === 'auto') { + config.closeDropdownOnSelect = this._isTextElement || this._isSelectOneElement || config.singleModeForMultiSelect; + } + else { + config.closeDropdownOnSelect = coerceBool(config.closeDropdownOnSelect); + } + if (config.placeholder) { + if (config.placeholderValue) { + this._hasNonChoicePlaceholder = true; + } + else if (passedElement.dataset.placeholder) { + this._hasNonChoicePlaceholder = true; + config.placeholderValue = passedElement.dataset.placeholder; + } + } + if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { + var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); + config.addItemFilter = re.test.bind(re); + } + if (this._isTextElement) { + this.passedElement = new WrappedInput({ + element: passedElement, + classNames: config.classNames, + }); + } + else { + var selectEl = passedElement; + this.passedElement = new WrappedSelect({ + element: selectEl, + classNames: config.classNames, + template: function (data) { return _this._templates.option(data); }, + extractPlaceholder: config.placeholder && !this._hasNonChoicePlaceholder, + }); + } + this.initialised = false; + this._store = new Store(); + this._currentValue = ''; + config.searchEnabled = (!this._isTextElement && config.searchEnabled) || this._elementType === SELECT_MULTIPLE_TYPE; + this._canSearch = config.searchEnabled; + this._isScrollingOnIe = false; + this._highlightPosition = 0; + this._wasTap = true; + this._placeholderValue = this._generatePlaceholderValue(); + this._baseId = generateId(passedElement, 'choices-'); + /** + * setting direction in cases where it's explicitly set on passedElement + * or when calculated direction is different from the document + */ + this._direction = this.passedElement.dir; + if (!this._direction) { + var elementDirection = window.getComputedStyle(this.passedElement.element).direction; + var documentDirection = window.getComputedStyle(document.documentElement).direction; + if (elementDirection !== documentDirection) { + this._direction = elementDirection; + } + } + this._idNames = { + itemChoice: 'item-choice', + }; + this._templates = defaults.templates; + this._render = this._render.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + this._onKeyUp = this._onKeyUp.bind(this); + this._onKeyDown = this._onKeyDown.bind(this); + this._onInput = this._onInput.bind(this); + this._onClick = this._onClick.bind(this); + this._onTouchMove = this._onTouchMove.bind(this); + this._onTouchEnd = this._onTouchEnd.bind(this); + this._onMouseDown = this._onMouseDown.bind(this); + this._onMouseOver = this._onMouseOver.bind(this); + this._onFormReset = this._onFormReset.bind(this); + this._onSelectKey = this._onSelectKey.bind(this); + this._onEnterKey = this._onEnterKey.bind(this); + this._onEscapeKey = this._onEscapeKey.bind(this); + this._onDirectionKey = this._onDirectionKey.bind(this); + this._onDeleteKey = this._onDeleteKey.bind(this); + // If element has already been initialised with Choices, fail silently + if (this.passedElement.isActive) { + if (!config.silent) { + console.warn('Trying to initialise Choices on element already initialised', { element: element }); + } + this.initialised = true; + this.initialisedOK = false; + return; + } + // Let's go + this.init(); + // preserve the selected item list after setup for form reset + this._initialItems = this._store.items.map(function (choice) { return choice.value; }); + } + Object.defineProperty(Choices, "defaults", { + get: function () { + return Object.preventExtensions({ + get options() { + return USER_DEFAULTS; + }, + get allOptions() { + return DEFAULT_CONFIG; + }, + get templates() { + return templates; + }, + }); + }, + enumerable: false, + configurable: true + }); + Choices.prototype.init = function () { + if (this.initialised || this.initialisedOK !== undefined) { + return; + } + this._searcher = getSearcher(this.config); + this._loadChoices(); + this._createTemplates(); + this._createElements(); + this._createStructure(); + if ((this._isTextElement && !this.config.addItems) || + this.passedElement.element.hasAttribute('disabled') || + !!this.passedElement.element.closest('fieldset:disabled')) { + this.disable(); + } + else { + this.enable(); + this._addEventListeners(); + } + // should be triggered **after** disabled state to avoid additional re-draws + this._initStore(); + this.initialised = true; + this.initialisedOK = true; + var callbackOnInit = this.config.callbackOnInit; + // Run callback if it is a function + if (callbackOnInit && typeof callbackOnInit === 'function') { + callbackOnInit.call(this); + } + }; + Choices.prototype.destroy = function () { + if (!this.initialised) { + return; + } + this._removeEventListeners(); + this.passedElement.reveal(); + this.containerOuter.unwrap(this.passedElement.element); + this._store._listeners = []; // prevents select/input value being wiped + this.clearStore(); + this._stopSearch(); + this._templates = Choices.defaults.templates; + this.initialised = false; + this.initialisedOK = undefined; + }; + Choices.prototype.enable = function () { + var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter; + if (passedElement.isDisabled) { + passedElement.enable(); + } + if (containerOuter.isDisabled) { + this._addEventListeners(); + this.input.enable(); + containerOuter.enable(); + this._render(); + } + return this; + }; + Choices.prototype.disable = function () { + var _a = this, passedElement = _a.passedElement, containerOuter = _a.containerOuter; + if (!passedElement.isDisabled) { + passedElement.disable(); + } + if (!containerOuter.isDisabled) { + this._removeEventListeners(); + this.input.disable(); + containerOuter.disable(); + this._render(); + } + return this; + }; + Choices.prototype.highlightItem = function (item, runEvent) { + if (runEvent === void 0) { runEvent = true; } + if (!item || !item.id) { + return this; + } + var choice = this._store.choices.find(function (c) { return c.id === item.id; }); + if (!choice || choice.highlighted) { + return this; + } + this._store.dispatch(highlightItem(choice, true)); + if (runEvent) { + this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.unhighlightItem = function (item, runEvent) { + if (runEvent === void 0) { runEvent = true; } + if (!item || !item.id) { + return this; + } + var choice = this._store.choices.find(function (c) { return c.id === item.id; }); + if (!choice || !choice.highlighted) { + return this; + } + this._store.dispatch(highlightItem(choice, false)); + if (runEvent) { + this.passedElement.triggerEvent("highlightItem" /* EventType.highlightItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.highlightAll = function () { + var _this = this; + this._store.withTxn(function () { + _this._store.items.forEach(function (item) { return _this.highlightItem(item); }); + }); + return this; + }; + Choices.prototype.unhighlightAll = function () { + var _this = this; + this._store.withTxn(function () { + _this._store.items.forEach(function (item) { return _this.unhighlightItem(item); }); + }); + return this; + }; + Choices.prototype.removeActiveItemsByValue = function (value) { + var _this = this; + this._store.withTxn(function () { + _this._store.items.filter(function (item) { return item.value === value; }).forEach(function (item) { return _this._removeItem(item); }); + }); + return this; + }; + Choices.prototype.removeActiveItems = function (excludedId) { + var _this = this; + this._store.withTxn(function () { + _this._store.items.filter(function (_a) { + var id = _a.id; + return id !== excludedId; + }).forEach(function (item) { return _this._removeItem(item); }); + }); + return this; + }; + Choices.prototype.removeHighlightedItems = function (runEvent) { + var _this = this; + if (runEvent === void 0) { runEvent = false; } + this._store.withTxn(function () { + _this._store.highlightedActiveItems.forEach(function (item) { + _this._removeItem(item); + // If this action was performed by the user + // trigger the event + if (runEvent) { + _this._triggerChange(item.value); + } + }); + }); + return this; + }; + Choices.prototype.showDropdown = function (preventInputFocus) { + var _this = this; + if (this.dropdown.isActive) { + return this; + } + requestAnimationFrame(function () { + _this.dropdown.show(); + _this.containerOuter.open(_this.dropdown.distanceFromTopWindow); + if (!preventInputFocus && _this._canSearch) { + _this.input.focus(); + } + _this.passedElement.triggerEvent("showDropdown" /* EventType.showDropdown */); + }); + return this; + }; + Choices.prototype.hideDropdown = function (preventInputBlur) { + var _this = this; + if (!this.dropdown.isActive) { + return this; + } + requestAnimationFrame(function () { + _this.dropdown.hide(); + _this.containerOuter.close(); + if (!preventInputBlur && _this._canSearch) { + _this.input.removeActiveDescendant(); + _this.input.blur(); + } + _this.passedElement.triggerEvent("hideDropdown" /* EventType.hideDropdown */); + }); + return this; + }; + Choices.prototype.getValue = function (valueOnly) { + var _this = this; + if (valueOnly === void 0) { valueOnly = false; } + var values = this._store.items.reduce(function (selectedItems, item) { + var itemValue = valueOnly ? item.value : _this._getChoiceForOutput(item); + selectedItems.push(itemValue); + return selectedItems; + }, []); + return this._isSelectOneElement || this.config.singleModeForMultiSelect ? values[0] : values; + }; + Choices.prototype.setValue = function (items) { + var _this = this; + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setValue'); + return this; + } + this._store.withTxn(function () { + items.forEach(function (value) { + if (value) { + _this._addChoice(mapInputToChoice(value, false)); + } + }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.setChoiceByValue = function (value) { + var _this = this; + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoiceByValue'); + return this; + } + if (this._isTextElement) { + return this; + } + this._store.withTxn(function () { + // If only one value has been passed, convert to array + var choiceValue = Array.isArray(value) ? value : [value]; + // Loop through each value and + choiceValue.forEach(function (val) { return _this._findAndSelectChoiceByValue(val); }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + /** + * Set choices of select input via an array of objects (or function that returns array of object or promise of it), + * a value field name and a label field name. + * This behaves the same as passing items via the choices option but can be called after initialising Choices. + * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. + * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). + * + * **Input types affected:** select-one, select-multiple + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([ + * {value: 'One', label: 'Label One', disabled: true}, + * {value: 'Two', label: 'Label Two', selected: true}, + * {value: 'Three', label: 'Label Three'}, + * ], 'value', 'label', false); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices(async () => { + * try { + * const items = await fetch('/items'); + * return items.json() + * } catch(err) { + * console.error(err) + * } + * }); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([{ + * label: 'Group one', + * id: 1, + * disabled: false, + * choices: [ + * {value: 'Child One', label: 'Child One', selected: true}, + * {value: 'Child Two', label: 'Child Two', disabled: true}, + * {value: 'Child Three', label: 'Child Three'}, + * ] + * }, + * { + * label: 'Group two', + * id: 2, + * disabled: false, + * choices: [ + * {value: 'Child Four', label: 'Child Four', disabled: true}, + * {value: 'Child Five', label: 'Child Five'}, + * {value: 'Child Six', label: 'Child Six', customProperties: { + * description: 'Custom description about child six', + * random: 'Another random custom property' + * }}, + * ] + * }], 'value', 'label', false); + * ``` + */ + Choices.prototype.setChoices = function (choicesArrayOrFetcher, value, label, replaceChoices) { + var _this = this; + if (choicesArrayOrFetcher === void 0) { choicesArrayOrFetcher = []; } + if (value === void 0) { value = 'value'; } + if (label === void 0) { label = 'label'; } + if (replaceChoices === void 0) { replaceChoices = false; } + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoices'); + return this; + } + if (!this._isSelectElement) { + throw new TypeError("setChoices can't be used with INPUT based Choices"); + } + if (typeof value !== 'string' || !value) { + throw new TypeError("value parameter must be a name of 'value' field in passed objects"); + } + // Clear choices if needed + if (replaceChoices) { + this.clearChoices(); + } + if (typeof choicesArrayOrFetcher === 'function') { + // it's a choices fetcher function + var fetcher_1 = choicesArrayOrFetcher(this); + if (typeof Promise === 'function' && fetcher_1 instanceof Promise) { + // that's a promise + // eslint-disable-next-line no-promise-executor-return + return new Promise(function (resolve) { return requestAnimationFrame(resolve); }) + .then(function () { return _this._handleLoadingState(true); }) + .then(function () { return fetcher_1; }) + .then(function (data) { return _this.setChoices(data, value, label, replaceChoices); }) + .catch(function (err) { + if (!_this.config.silent) { + console.error(err); + } + }) + .then(function () { return _this._handleLoadingState(false); }) + .then(function () { return _this; }); + } + // function returned something else than promise, let's check if it's an array of choices + if (!Array.isArray(fetcher_1)) { + throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof fetcher_1)); + } + // recursion with results, it's sync and choices were cleared already + return this.setChoices(fetcher_1, value, label, false); + } + if (!Array.isArray(choicesArrayOrFetcher)) { + throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices"); + } + this.containerOuter.removeLoadingState(); + this._store.withTxn(function () { + var isDefaultValue = value === 'value'; + var isDefaultLabel = label === 'label'; + choicesArrayOrFetcher.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + var group = groupOrChoice; + if (!isDefaultLabel) { + group = __assign(__assign({}, group), { label: group[label] }); + } + _this._addGroup(mapInputToChoice(group, true)); + } + else { + var choice = groupOrChoice; + if (!isDefaultLabel || !isDefaultValue) { + choice = __assign(__assign({}, choice), { value: choice[value], label: choice[label] }); + } + _this._addChoice(mapInputToChoice(choice, false)); + } + }); + }); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.refresh = function (withEvents, selectFirstOption, deselectAll) { + var _this = this; + if (withEvents === void 0) { withEvents = false; } + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (deselectAll === void 0) { deselectAll = false; } + if (!this._isSelectElement) { + if (!this.config.silent) { + console.warn('refresh method can only be used on choices backed by a element'); + } + return this; + } + this._store.withTxn(function () { + var choicesFromOptions = _this.passedElement.optionsAsChoices(); + var items = _this._store.items; + // Build the list of items which require preserving + var existingItems = {}; + if (!deselectAll) { + items.forEach(function (choice) { + if (choice.id && choice.active && choice.selected && !choice.disabled) { + existingItems[choice.value] = true; + } + }); + } + choicesFromOptions.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + return; + } + var choice = groupOrChoice; + if (deselectAll) { + choice.selected = false; + } + else if (existingItems[choice.value]) { + choice.selected = true; + } + }); + _this.clearStore(); + /* @todo only generate add events for the added options instead of all + if (withEvents) { + items.forEach((choice) => { + if (existingItems[choice.value]) { + this.passedElement.triggerEvent( + EventType.removeItem, + this._getChoiceForEvent(choice), + ); + } + }); + } + */ + // load new choices & items + _this._addPredefinedChoices(choicesFromOptions, selectFirstOption, withEvents); + // re-do search if required + if (_this._isSearching) { + _this._searchChoices(_this.input.value); + } + }); + return this; + }; + Choices.prototype.removeChoice = function (value) { + var choice = this._store.choices.find(function (c) { return c.value === value; }); + if (!choice) { + return this; + } + this._store.dispatch(removeChoice(choice)); + // @todo integrate with Store + this._searcher.reset(); + if (choice.selected) { + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.clearChoices = function () { + this.passedElement.element.innerHTML = ''; + this._store.dispatch(clearChoices()); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearStore = function () { + this._store.reset(); + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearInput = function () { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); + this._clearNotice(); + if (this._isSearching) { + this._stopSearch(); + } + return this; + }; + Choices.prototype._validateConfig = function () { + var config = this.config; + var invalidConfigOptions = diff(config, DEFAULT_CONFIG); + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + if (config.allowHTML && config.allowHtmlUserInput) { + if (config.addItems) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addItems all being true is strongly not recommended and may lead to XSS attacks'); + } + if (config.addChoices) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addChoices all being true is strongly not recommended and may lead to XSS attacks'); + } + } + }; + Choices.prototype._render = function (changes) { + if (changes === void 0) { changes = { choices: true, groups: true, items: true }; } + if (this._store.inTxn()) { + return; + } + if (this._isSelectElement) { + if (changes.choices || changes.groups) { + this._renderChoices(); + } + } + if (changes.items) { + this._renderItems(); + } + }; + Choices.prototype._renderChoices = function () { + var _this = this; + this.choiceList.clear(); + if (!this._canAddItems()) { + return; // block rendering choices if the input limit is reached. + } + var config = this.config; + var _a = this._store, activeGroups = _a.activeGroups, activeChoices = _a.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + var noChoices = true; + if (activeChoices.length) { + if (config.resetScrollPosition) { + requestAnimationFrame(function () { return _this.choiceList.scrollToTop(); }); + } + // If we have grouped options + if (activeGroups.length && !this._isSearching) { + if (!this._hasNonChoicePlaceholder) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { return activeChoice.placeholder && activeChoice.groupId === -1; }); + if (activePlaceholders.length) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + } + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } + else { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } + noChoices = !choiceListFragment.childNodes.length; + } + var notice = this._notice; + if (noChoices) { + if (!notice) { + this._notice = { + text: resolveStringFunction(config.noChoicesText), + type: NoticeTypes.noChoices, + }; + } + } + else if (notice && notice.type === NoticeTypes.noChoices) { + this._notice = undefined; + } + this._renderNotice(); + if (!noChoices) { + this.choiceList.element.append(choiceListFragment); + this._highlightChoice(); + } + }; + Choices.prototype._renderItems = function () { + var items = this._store.items || []; + this.itemList.clear(); + // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) + var itemListFragment = this._createItemsFragment(items); + // If we have items to add, append them + if (itemListFragment.childNodes.length) { + this.itemList.element.append(itemListFragment); + } + }; + Choices.prototype._createGroupsFragment = function (groups, choices, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + var config = this.config; + var getGroupChoices = function (group) { + return choices.filter(function (choice) { + if (_this._isSelectOneElement) { + return choice.groupId === group.id; + } + return choice.groupId === group.id && (config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; + // If sorting is enabled, filter groups + if (config.shouldSort) { + groups.sort(config.sorter); + } + // Add Choices without group first, regardless of sort, otherwise they won't be distinguishable + // from the last group + var choicesWithoutGroup = choices.filter(function (c) { return !c.groupId; }); + if (choicesWithoutGroup.length) { + this._createChoicesFragment(choicesWithoutGroup, fragment, false); + } + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + if (groupChoices.length) { + var dropdownGroup = _this._templates.choiceGroup(_this.config, group); + fragment.appendChild(dropdownGroup); + _this._createChoicesFragment(groupChoices, fragment, true); + } + }); + return fragment; + }; + Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + if (withinGroup === void 0) { withinGroup = false; } + // Create a fragment to store our list items (so we don't have to update the DOM for each item) + var _a = this, config = _a.config, isSearching = _a._isSearching, isSelectOneElement = _a._isSelectOneElement; + var searchResultLimit = config.searchResultLimit, renderChoiceLimit = config.renderChoiceLimit; + var groupLookup = []; + var appendGroupInSearch = config.appendGroupInSearch && isSearching; + if (appendGroupInSearch) { + this._store.groups.forEach(function (group) { + groupLookup[group.id] = group.label; + }); + } + if (this._isSelectElement) { + var backingOptions = choices.filter(function (choice) { return !choice.element; }); + if (backingOptions.length) { + this.passedElement.addOptions(backingOptions); + } + } + var skipSelected = config.renderSelectedChoices === 'auto' && !isSelectOneElement; + var placeholderChoices = []; + var normalChoices = []; + choices.forEach(function (choice) { + if ((isSearching && !choice.rank) || (skipSelected && choice.selected)) { + return; + } + if (_this._hasNonChoicePlaceholder || !choice.placeholder) { + normalChoices.push(choice); + } + else { + placeholderChoices.push(choice); + } + }); + if (isSearching) { + // sortByRank is used to ensure stable sorting, as scores are non-unique + // this additionally ensures fuseOptions.sortFn is not ignored + normalChoices.sort(sortByRank); + } + else if (config.shouldSort) { + normalChoices.sort(config.sorter); + } + var sortedChoices = isSelectOneElement && placeholderChoices.length ? __spreadArray(__spreadArray([], placeholderChoices, true), normalChoices, true) : normalChoices; + var choiceLimit = sortedChoices.length; + var limit = choiceLimit; + if (isSearching && searchResultLimit > 0) { + limit = searchResultLimit; + } + else if (renderChoiceLimit > 0 && !withinGroup) { + limit = renderChoiceLimit; + } + if (limit < choiceLimit) { + choiceLimit = limit; + } + choiceLimit--; + // Add each choice to dropdown within range + sortedChoices.every(function (choice, index) { + var dropdownItem = _this._templates.choice(config, choice, config.itemSelectText); + if (appendGroupInSearch && choice.groupId > 0) { + var groupName = groupLookup[choice.groupId]; + if (groupName) { + dropdownItem.innerHTML += " (".concat(groupName, ")"); + } + } + fragment.appendChild(dropdownItem); + return index < choiceLimit; + }); + return fragment; + }; + Choices.prototype._createItemsFragment = function (items, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + // Create fragment to add elements to + var config = this.config; + var shouldSortItems = config.shouldSortItems, sorter = config.sorter, removeItemButton = config.removeItemButton, delimiter = config.delimiter; + // If sorting is enabled, filter items + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sorter); + } + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items.map(function (_a) { + var value = _a.value; + return value; + }).join(delimiter); + } + var addItemToFragment = function (item) { + // Create new list element + var listItem = _this._templates.item(config, item, removeItemButton); + // Append it to list + fragment.appendChild(listItem); + }; + // Add each list item to list + items.forEach(addItemToFragment); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder && !items.length) { + addItemToFragment(mapInputToChoice({ + selected: true, + value: '', + label: this.config.placeholderValue || '', + active: true, + placeholder: true, + }, false)); + } + return fragment; + }; + Choices.prototype._displayNotice = function (text, type, openDropdown) { + if (openDropdown === void 0) { openDropdown = true; } + var oldNotice = this._notice; + if (oldNotice && + ((oldNotice.type === type && oldNotice.text === text) || + (oldNotice.type === NoticeTypes.addChoice && + (type === NoticeTypes.noResults || type === NoticeTypes.noChoices)))) { + if (openDropdown) { + this.showDropdown(true); + } + return; + } + this._clearNotice(); + this._notice = text + ? { + text: text, + type: type, + } + : undefined; + this._renderNotice(); + if (openDropdown && text) { + this.showDropdown(true); + } + }; + Choices.prototype._clearNotice = function () { + if (!this._notice) { + return; + } + var noticeElement = this.choiceList.element.querySelector(getClassNamesSelector(this.config.classNames.notice)); + if (noticeElement) { + noticeElement.remove(); + } + this._notice = undefined; + }; + Choices.prototype._renderNotice = function () { + var noticeConf = this._notice; + if (noticeConf) { + var notice = this._templates.notice(this.config, noticeConf.text, noticeConf.type); + this.choiceList.prepend(notice); + } + }; + Choices.prototype._getChoiceForOutput = function (choice, keyCode) { + if (!choice) { + return undefined; + } + var group = choice.groupId > 0 ? this._store.getGroupById(choice.groupId) : null; + return { + id: choice.id, + highlighted: choice.highlighted, + labelClass: choice.labelClass, + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + disabled: choice.disabled, + active: choice.active, + label: choice.label, + placeholder: choice.placeholder, + value: choice.value, + groupValue: group && group.label ? group.label : undefined, + element: choice.element, + keyCode: keyCode, + }; + }; + Choices.prototype._triggerChange = function (value) { + if (value === undefined || value === null) { + return; + } + this.passedElement.triggerEvent("change" /* EventType.change */, { + value: value, + }); + }; + Choices.prototype._handleButtonAction = function (element) { + var items = this._store.items; + if (!items.length || !this.config.removeItems || !this.config.removeItemButton) { + return; + } + var id = element && parseDataSetId(element.parentNode); + var itemToRemove = id && items.find(function (item) { return item.id === id; }); + if (!itemToRemove) { + return; + } + // Remove item associated with button + this._removeItem(itemToRemove); + this._triggerChange(itemToRemove.value); + if (this._isSelectOneElement && !this._hasNonChoicePlaceholder) { + var placeholderChoice = this._store.choices.reverse().find(function (choice) { return !choice.disabled && choice.placeholder; }); + if (placeholderChoice) { + this._addItem(placeholderChoice); + if (placeholderChoice.value) { + this._triggerChange(placeholderChoice.value); + } + } + } + }; + Choices.prototype._handleItemAction = function (element, hasShiftKey) { + var _this = this; + if (hasShiftKey === void 0) { hasShiftKey = false; } + var items = this._store.items; + if (!items.length || !this.config.removeItems || this._isSelectOneElement) { + return; + } + var id = parseDataSetId(element); + if (!id) { + return; + } + // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + items.forEach(function (item) { + if (item.id === id && !item.highlighted) { + _this.highlightItem(item); + } + else if (!hasShiftKey && item.highlighted) { + _this.unhighlightItem(item); + } + }); + // Focus input as without focus, a user cannot do anything with a + // highlighted item + this.input.focus(); + }; + Choices.prototype._handleChoiceAction = function (element) { + var _this = this; + // If we are clicking on an option + var id = parseDataSetId(element); + var choice = id && this._store.getChoiceById(id); + if (!choice || choice.disabled) { + return false; + } + var hasActiveDropdown = this.dropdown.isActive; + if (!choice.selected) { + if (!this._canAddItems()) { + return true; // causes _onEnterKey to early out + } + this._store.withTxn(function () { + _this._addItem(choice, true, true); + _this.clearInput(); + _this.unhighlightAll(); + }); + this._triggerChange(choice.value); + } + // We want to close the dropdown if we are dealing with a single select box + if (hasActiveDropdown && this.config.closeDropdownOnSelect) { + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + return true; + }; + Choices.prototype._handleBackspace = function (items) { + var config = this.config; + if (!config.removeItems || !items.length) { + return; + } + var lastItem = items[items.length - 1]; + var hasHighlightedItems = items.some(function (item) { return item.highlighted; }); + // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items + if (config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + this._removeItem(lastItem); + this._triggerChange(lastItem.value); + } + else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } + this.removeHighlightedItems(true); + } + }; + Choices.prototype._loadChoices = function () { + var _a; + var config = this.config; + if (this._isTextElement) { + // Assign preset items from passed object first + this._presetChoices = config.items.map(function (e) { return mapInputToChoice(e, false); }); + // Add any values passed from attribute + var value = this.passedElement.value; + if (value) { + var elementItems = value + .split(config.delimiter) + .map(function (e) { return mapInputToChoice(e, false); }); + this._presetChoices = this._presetChoices.concat(elementItems); + } + this._presetChoices.forEach(function (choice) { + choice.selected = true; + }); + } + else if (this._isSelectElement) { + // Assign preset choices from passed object + this._presetChoices = config.choices.map(function (e) { return mapInputToChoice(e, true); }); + // Create array of choices from option elements + var choicesFromOptions = this.passedElement.optionsAsChoices(); + if (choicesFromOptions) { + (_a = this._presetChoices).push.apply(_a, choicesFromOptions); + } + } + }; + Choices.prototype._handleLoadingState = function (setLoading) { + if (setLoading === void 0) { setLoading = true; } + var config = this.config; + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); + if (this._isSelectOneElement) { + this.itemList.clear(); + this.itemList.element.append(this._templates.placeholder(config, config.loadingText)); + } + else { + this.input.placeholder = config.loadingText; + } + } + else { + this.enable(); + this.containerOuter.removeLoadingState(); + if (!this._isSelectOneElement) { + this.input.placeholder = this._placeholderValue || ''; + } + } + }; + Choices.prototype._handleSearch = function (value) { + if (!this.input.isFocussed) { + return; + } + var choices = this._store.choices; + var _a = this.config, searchFloor = _a.searchFloor, searchChoices = _a.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { return !option.active; }); + // Check that we have a value to search and the input was an alphanumeric character + if (value !== null && typeof value !== 'undefined' && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; + if (resultCount !== null) { + // Trigger search event + this.passedElement.triggerEvent("search" /* EventType.search */, { + value: value, + resultCount: resultCount, + }); + } + } + else if (hasUnactiveChoices) { + this._stopSearch(); + } + }; + Choices.prototype._canAddItems = function () { + var config = this.config; + var maxItemCount = config.maxItemCount, maxItemText = config.maxItemText; + if (!config.singleModeForMultiSelect && maxItemCount > 0 && maxItemCount <= this._store.items.length) { + this._displayNotice(typeof maxItemText === 'function' ? maxItemText(maxItemCount) : maxItemText, NoticeTypes.addChoice); + return false; + } + return true; + }; + Choices.prototype._canCreateItem = function (value) { + var config = this.config; + var canAddItem = true; + var notice = ''; + if (canAddItem && typeof config.addItemFilter === 'function' && !config.addItemFilter(value)) { + canAddItem = false; + notice = resolveNoticeFunction(config.customAddItemText, value); + } + if (canAddItem) { + var foundChoice = this._store.choices.find(function (choice) { return config.valueComparer(choice.value, value); }); + if (this._isSelectElement) { + // for exact matches, do not prompt to add it as a custom choice + if (foundChoice) { + this._displayNotice('', NoticeTypes.addChoice); + return false; + } + } + else if (this._isTextElement && !config.duplicateItemsAllowed) { + if (foundChoice) { + canAddItem = false; + notice = resolveNoticeFunction(config.uniqueItemText, value); + } + } + } + if (canAddItem) { + notice = resolveNoticeFunction(config.addItemText, value); + } + if (notice) { + this._displayNotice(notice, NoticeTypes.addChoice); + } + return canAddItem; + }; + Choices.prototype._searchChoices = function (value) { + var newValue = value.trim().replace(/\s{2,}/, ' '); + // signal input didn't change search + if (!newValue.length || newValue === this._currentValue) { + return null; + } + var searcher = this._searcher; + if (searcher.isEmptyIndex()) { + searcher.index(this._store.searchableChoices); + } + // If new value matches the desired length and is not the same as the current value with a space + var results = searcher.search(newValue); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + var notice = this._notice; + var noticeType = notice && notice.type; + if (noticeType !== NoticeTypes.addChoice) { + if (!results.length) { + this._displayNotice(resolveStringFunction(this.config.noResultsText), NoticeTypes.noResults); + } + else if (noticeType === NoticeTypes.noResults) { + this._clearNotice(); + } + } + this._store.dispatch(filterChoices(results)); + return results.length; + }; + Choices.prototype._stopSearch = function () { + var wasSearching = this._isSearching; + this._currentValue = ''; + this._isSearching = false; + if (wasSearching) { + this._store.dispatch(activateChoices(true)); + } + }; + Choices.prototype._addEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + // capture events - can cancel event processing or propagation + documentElement.addEventListener('touchend', this._onTouchEnd, true); + outerElement.addEventListener('keydown', this._onKeyDown, true); + outerElement.addEventListener('mousedown', this._onMouseDown, true); + // passive events - doesn't call `preventDefault` or `stopPropagation` + documentElement.addEventListener('click', this._onClick, { passive: true }); + documentElement.addEventListener('touchmove', this._onTouchMove, { + passive: true, + }); + this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { + passive: true, + }); + if (this._isSelectOneElement) { + outerElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + outerElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + } + inputElement.addEventListener('keyup', this._onKeyUp, { + passive: true, + }); + inputElement.addEventListener('input', this._onInput, { + passive: true, + }); + inputElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + inputElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + if (inputElement.form) { + inputElement.form.addEventListener('reset', this._onFormReset, { + passive: true, + }); + } + this.input.addEventListeners(); + }; + Choices.prototype._removeEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + documentElement.removeEventListener('touchend', this._onTouchEnd, true); + outerElement.removeEventListener('keydown', this._onKeyDown, true); + outerElement.removeEventListener('mousedown', this._onMouseDown, true); + documentElement.removeEventListener('click', this._onClick); + documentElement.removeEventListener('touchmove', this._onTouchMove); + this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); + if (this._isSelectOneElement) { + outerElement.removeEventListener('focus', this._onFocus); + outerElement.removeEventListener('blur', this._onBlur); + } + inputElement.removeEventListener('keyup', this._onKeyUp); + inputElement.removeEventListener('input', this._onInput); + inputElement.removeEventListener('focus', this._onFocus); + inputElement.removeEventListener('blur', this._onBlur); + if (inputElement.form) { + inputElement.form.removeEventListener('reset', this._onFormReset); + } + this.input.removeEventListeners(); + }; + Choices.prototype._onKeyDown = function (event) { + var keyCode = event.keyCode; + var items = this._store.items; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.element.hasChildNodes(); + /* + See: + https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key + https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values + https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs + https://stackoverflow.com/a/70866532 - "Unidentified" for mobile + http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7) + + Logic: when a key event is sent, `event.key` represents its printable value _or_ one + of a large list of special values indicating meta keys/functionality. In addition, + key events for compose functionality contain a value of `Dead` when mid-composition. + + I can't quite verify it, but non-English IMEs may also be able to generate key codes + for code points in the surrogate-pair range, which could potentially be seen as having + key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that + alone. + + Here, key.length === 1 means we know for sure the input was printable and not a special + `key` value. When the length is greater than 1, it could be either a printable surrogate + pair or a special `key` value. We can tell the difference by checking if the _character + code_ value (not code point!) is in the "surrogate pair" range or not. + + We don't use .codePointAt because an invalid code point would return 65535, which wouldn't + pass the >= 0x10000 check we would otherwise use. + + > ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points + > of each plane are noncharacters: U+FFFE and U+FFFF on the BMP... + */ + var wasPrintableChar = event.key.length === 1 || + (event.key.length === 2 && event.key.charCodeAt(0) >= 0xd800) || + event.key === 'Unidentified'; + if (!this._isTextElement && !hasActiveDropdown) { + this.showDropdown(); + if (!this.input.isFocussed && wasPrintableChar) { + /* + We update the input value with the pressed key as + the input was not focussed at the time of key press + therefore does not have the value of the key. + */ + this.input.value += event.key; + } + } + switch (keyCode) { + case 65 /* KeyCodeMap.A_KEY */: + return this._onSelectKey(event, hasItems); + case 13 /* KeyCodeMap.ENTER_KEY */: + return this._onEnterKey(event, hasActiveDropdown); + case 27 /* KeyCodeMap.ESC_KEY */: + return this._onEscapeKey(event, hasActiveDropdown); + case 38 /* KeyCodeMap.UP_KEY */: + case 33 /* KeyCodeMap.PAGE_UP_KEY */: + case 40 /* KeyCodeMap.DOWN_KEY */: + case 34 /* KeyCodeMap.PAGE_DOWN_KEY */: + return this._onDirectionKey(event, hasActiveDropdown); + case 8 /* KeyCodeMap.DELETE_KEY */: + case 46 /* KeyCodeMap.BACK_KEY */: + return this._onDeleteKey(event, items, hasFocusedInput); + } + }; + Choices.prototype._onKeyUp = function ( /* event: KeyboardEvent */) { + this._canSearch = this.config.searchEnabled; + }; + Choices.prototype._onInput = function ( /* event: InputEvent */) { + var value = this.input.value; + if (!value) { + if (this._isTextElement) { + this.hideDropdown(true); + } + else { + this._stopSearch(); + } + this._clearNotice(); + return; + } + if (!this._canAddItems()) { + return; + } + if (this._canSearch) { + // do the search even if the entered text can not be added + this._handleSearch(value); + } + if (!this._canAddUserChoices) { + return; + } + // determine if a notice needs to be displayed for why a search result can't be added + this._canCreateItem(value); + if (this._isSelectElement) { + this._highlightPosition = 0; // reset to select the notice and/or exact match + this._highlightChoice(); + } + }; + Choices.prototype._onSelectKey = function (event, hasItems) { + var ctrlKey = event.ctrlKey, metaKey = event.metaKey; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; + // If CTRL + A or CMD + A have been pressed and there are items to select + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + if (shouldHightlightAll) { + this.highlightAll(); + } + } + }; + Choices.prototype._onEnterKey = function (event, hasActiveDropdown) { + var _this = this; + var config = this.config; + var value = this.input.value; + var target = event.target; + var targetWasRemoveButton = target && target.hasAttribute('data-button'); + event.preventDefault(); + if (targetWasRemoveButton) { + this._handleButtonAction(target); + return; + } + if (!hasActiveDropdown) { + if (this._isSelectElement || this._notice) { + this.showDropdown(); + } + return; + } + var highlightedChoice = this.dropdown.element.querySelector(getClassNamesSelector(config.classNames.highlightedState)); + if (highlightedChoice && this._handleChoiceAction(highlightedChoice)) { + return; + } + if (!target || !value) { + this.hideDropdown(true); + return; + } + if (!this._canAddItems()) { + return; + } + var addedItem = false; + this._store.withTxn(function () { + addedItem = _this._findAndSelectChoiceByValue(value, true); + if (!addedItem) { + if (!_this._canAddUserChoices) { + return; + } + if (!_this._canCreateItem(value)) { + return; + } + var sanitisedValue = sanitise(value); + var userValue = config.allowHtmlUserInput || sanitisedValue === value ? value : { escaped: sanitisedValue, raw: value }; + _this._addChoice(mapInputToChoice({ + value: userValue, + label: userValue, + selected: true, + }, false), true, true); + addedItem = true; + } + _this.clearInput(); + _this.unhighlightAll(); + }); + if (!addedItem) { + return; + } + this._triggerChange(value); + if (config.closeDropdownOnSelect) { + this.hideDropdown(true); + } + }; + Choices.prototype._onEscapeKey = function (event, hasActiveDropdown) { + if (hasActiveDropdown) { + event.stopPropagation(); + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + }; + Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) { + var keyCode = event.keyCode, metaKey = event.metaKey; + // If up or down key is pressed, traverse through options + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === 40 /* KeyCodeMap.DOWN_KEY */ || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ ? 1 : -1; + var skipKey = metaKey || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ || keyCode === 33 /* KeyCodeMap.PAGE_UP_KEY */; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl = void 0; + if (skipKey) { + if (directionInt > 0) { + nextEl = this.dropdown.element.querySelector("".concat(selectableChoiceIdentifier, ":last-of-type")); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + else { + var currentEl = this.dropdown.element.querySelector(getClassNamesSelector(this.config.classNames.highlightedState)); + if (currentEl) { + nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChildElement(nextEl, directionInt); + } + this._highlightChoice(nextEl); + } + // Prevent default to maintain cursor position whilst + // traversing dropdown options + event.preventDefault(); + } + }; + Choices.prototype._onDeleteKey = function (event, items, hasFocusedInput) { + var target = event.target; + // If backspace or delete key is pressed and the input has no value + if (!this._isSelectOneElement && !target.value && hasFocusedInput) { + this._handleBackspace(items); + event.preventDefault(); + } + }; + Choices.prototype._onTouchMove = function () { + if (this._wasTap) { + this._wasTap = false; + } + }; + Choices.prototype._onTouchEnd = function (event) { + var target = (event || event.touches[0]).target; + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } + else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } + // Prevents focus event firing + event.stopPropagation(); + } + this._wasTap = true; + }; + /** + * Handles mousedown event in capture mode for containetOuter.element + */ + Choices.prototype._onMouseDown = function (event) { + var target = event.target; + if (!(target instanceof HTMLElement)) { + return; + } + // If we have our mouse down on the scrollbar and are on IE11... + if (IS_IE11 && this.choiceList.element.contains(target)) { + // check if click was on a scrollbar area + var firstChoice = this.choiceList.element.firstElementChild; + this._isScrollingOnIe = + this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; + } + if (target === this.input.element) { + return; + } + var item = target.closest('[data-button],[data-item],[data-choice]'); + if (item instanceof HTMLElement) { + var hasShiftKey = event.shiftKey; + var dataset = item.dataset; + if ('button' in dataset) { + this._handleButtonAction(item); + } + else if ('item' in dataset) { + this._handleItemAction(item, hasShiftKey); + } + else if ('choice' in dataset) { + this._handleChoiceAction(item); + } + } + event.preventDefault(); + }; + /** + * Handles mouseover event over this.dropdown + * @param {MouseEvent} event + */ + Choices.prototype._onMouseOver = function (_a) { + var target = _a.target; + if (target instanceof HTMLElement && 'choice' in target.dataset) { + this._highlightChoice(target); + } + }; + Choices.prototype._onClick = function (_a) { + var target = _a.target; + var containerOuter = this.containerOuter; + var clickWasWithinContainer = containerOuter.element.contains(target); + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } + else { + this.showDropdown(); + containerOuter.element.focus(); + } + } + else if (this._isSelectOneElement && + target !== this.input.element && + !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } + else { + var hasHighlightedItems = !!this._store.highlightedActiveItems.length; + if (hasHighlightedItems) { + this.unhighlightAll(); + } + containerOuter.removeFocusState(); + this.hideDropdown(true); + } + }; + Choices.prototype._onFocus = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var focusWasWithinContainer = target && containerOuter.element.contains(target); + if (!focusWasWithinContainer) { + return; + } + var targetIsInput = target === this.input.element; + var focusActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput) { + containerOuter.addFocusState(); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.addFocusState(); + if (targetIsInput) { + _this.showDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput) { + _this.showDropdown(true); + // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown + containerOuter.addFocusState(); + } + }, + _b); + focusActions[this._elementType](); + }; + Choices.prototype._onBlur = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var blurWasWithinContainer = target && containerOuter.element.contains(target); + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeChoices = this._store.activeChoices; + var hasHighlightedItems_1 = activeChoices.some(function (item) { return item.highlighted; }); + var targetIsInput_1 = target === this.input.element; + var blurActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + _this.hideDropdown(true); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.removeFocusState(); + if (targetIsInput_1 || (target === containerOuter.element && !_this._canSearch)) { + _this.hideDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + _this.hideDropdown(true); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + } + }, + _b); + blurActions[this._elementType](); + } + else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + }; + Choices.prototype._onFormReset = function () { + var _this = this; + this._store.withTxn(function () { + _this.clearInput(); + _this.hideDropdown(); + _this.refresh(false, false, true); + if (_this._initialItems.length) { + _this.setChoiceByValue(_this._initialItems); + } + }); + }; + Choices.prototype._highlightChoice = function (el) { + var _a; + if (el === void 0) { el = null; } + var highlightedState = this.config.classNames.highlightedState; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); + if (!choices.length) { + return; + } + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(getClassNamesSelector(highlightedState))); + // Remove any highlighted choices + highlightedChoices.forEach(function (choice) { + var _a; + (_a = choice.classList).remove.apply(_a, getClassNames(highlightedState)); + choice.setAttribute('aria-selected', 'false'); + }); + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } + else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } + else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + if (!passedEl) { + passedEl = choices[0]; + } + } + (_a = passedEl.classList).add.apply(_a, getClassNames(highlightedState)); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent("highlightChoice" /* EventType.highlightChoice */, { + el: passedEl, + }); + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + }; + Choices.prototype._addItem = function (item, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + var id = item.id; + if (!id) { + throw new TypeError('item.id must be set before _addItem is called for a choice/item'); + } + if (this.config.singleModeForMultiSelect || this._isSelectOneElement) { + this.removeActiveItems(id); + } + this._store.dispatch(addItem(item)); + if (withEvents) { + this.passedElement.triggerEvent("addItem" /* EventType.addItem */, this._getChoiceForOutput(item)); + if (userTriggered) { + this.passedElement.triggerEvent("choice" /* EventType.choice */, this._getChoiceForOutput(item)); + } + } + }; + Choices.prototype._removeItem = function (item) { + var id = item.id; + if (!id) { + return; + } + this._store.dispatch(removeItem(item)); + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(item)); + }; + Choices.prototype._addChoice = function (choice, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + if (choice.id) { + throw new TypeError('Can not re-add a choice which has already been added'); + } + // Generate unique id, in-place update is required so chaining _addItem works as expected + this._lastAddedChoiceId++; + choice.id = this._lastAddedChoiceId; + choice.elementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choice.id); + var _a = this.config, prependValue = _a.prependValue, appendValue = _a.appendValue; + if (prependValue) { + choice.value = prependValue + choice.value; + } + if (appendValue) { + choice.value += appendValue.toString(); + } + if ((prependValue || appendValue) && choice.element) { + choice.element.value = choice.value; + } + this._store.dispatch(addChoice(choice)); + if (choice.selected) { + this._addItem(choice, withEvents, userTriggered); + } + }; + Choices.prototype._addGroup = function (group, withEvents) { + var _this = this; + if (withEvents === void 0) { withEvents = true; } + if (group.id) { + throw new TypeError('Can not re-add a group which has already been added'); + } + this._store.dispatch(addGroup(group)); + if (!group.choices) { + return; + } + // add unique id for the group(s), and do not store the full list of choices in this group + var g = group; + this._lastAddedGroupId++; + g.id = this._lastAddedGroupId; + var id = group.id, choices = group.choices; + g.choices = []; + choices.forEach(function (item) { + item.groupId = id; + if (group.disabled) { + item.disabled = true; + } + _this._addChoice(item, withEvents); + }); + }; + Choices.prototype._createTemplates = function () { + var _this = this; + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; + if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { + userTemplates = callbackOnCreateTemplates.call(this, strToEl, escapeForTemplate); + } + var templating = {}; + Object.keys(this._templates).forEach(function (name) { + if (name in userTemplates) { + templating[name] = userTemplates[name].bind(_this); + } + else { + templating[name] = _this._templates[name].bind(_this); + } + }); + this._templates = templating; + }; + Choices.prototype._createElements = function () { + var templating = this._templates; + var config = this.config; + var position = config.position, classNames = config.classNames; + var elementType = this._elementType; + this.containerOuter = new Container({ + element: templating.containerOuter(config, this._direction, this._isSelectElement, this._isSelectOneElement, config.searchEnabled, elementType, config.labelId), + classNames: classNames, + type: elementType, + position: position, + }); + this.containerInner = new Container({ + element: templating.containerInner(config), + classNames: classNames, + type: elementType, + position: position, + }); + this.input = new Input({ + element: templating.input(config, this._placeholderValue), + classNames: classNames, + type: elementType, + preventPaste: !config.paste, + }); + this.choiceList = new List({ + element: templating.choiceList(config, this._isSelectOneElement), + }); + this.itemList = new List({ + element: templating.itemList(config, this._isSelectOneElement), + }); + this.dropdown = new Dropdown({ + element: templating.dropdown(config), + classNames: classNames, + type: elementType, + }); + }; + Choices.prototype._createStructure = function () { + var _a = this, containerInner = _a.containerInner, containerOuter = _a.containerOuter, passedElement = _a.passedElement, dropdown = _a.dropdown, input = _a.input; + // Hide original element + passedElement.conceal(); + // Wrap input in container preserving DOM ordering + containerInner.wrap(passedElement.element); + // Wrapper inner container with outer container + containerOuter.wrap(containerInner.element); + if (this._isSelectOneElement) { + input.placeholder = this.config.searchPlaceholderValue || ''; + } + else { + if (this._placeholderValue) { + input.placeholder = this._placeholderValue; + } + input.setWidth(); + } + containerOuter.element.appendChild(containerInner.element); + containerOuter.element.appendChild(dropdown.element); + containerInner.element.appendChild(this.itemList.element); + dropdown.element.appendChild(this.choiceList.element); + if (!this._isSelectOneElement) { + containerInner.element.appendChild(input.element); + } + else if (this.config.searchEnabled) { + dropdown.element.insertBefore(input.element, dropdown.element.firstChild); + } + this._highlightPosition = 0; + this._isSearching = false; + }; + Choices.prototype._initStore = function () { + var _this = this; + this._store.subscribe(this._render); + this._store.withTxn(function () { + _this._addPredefinedChoices(_this._presetChoices, _this._isSelectOneElement && !_this._hasNonChoicePlaceholder, false); + }); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder) { + this._render({ choices: false, groups: false, items: true }); + } + }; + Choices.prototype._addPredefinedChoices = function (choices, selectFirstOption, withEvents) { + var _this = this; + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (withEvents === void 0) { withEvents = true; } + if (selectFirstOption) { + /** + * If there is a selected choice already or the choice is not the first in + * the array, add each choice normally. + * + * Otherwise we pre-select the first enabled choice in the array ("select-one" only) + */ + var noSelectedChoices = choices.findIndex(function (choice) { return choice.selected; }) === -1; + if (noSelectedChoices) { + choices.some(function (choice) { + if (choice.disabled || 'choices' in choice) { + return false; + } + choice.selected = true; + return true; + }); + } + } + choices.forEach(function (item) { + if ('choices' in item) { + if (_this._isSelectElement) { + _this._addGroup(item, withEvents); + } + } + else { + _this._addChoice(item, withEvents); + } + }); + }; + Choices.prototype._findAndSelectChoiceByValue = function (value, userTriggered) { + var _this = this; + if (userTriggered === void 0) { userTriggered = false; } + var choices = this._store.choices; + // Check 'value' property exists and the choice isn't already selected + var foundChoice = choices.find(function (choice) { return _this.config.valueComparer(choice.value, value); }); + if (foundChoice && !foundChoice.disabled && !foundChoice.selected) { + this._addItem(foundChoice, true, userTriggered); + return true; + } + return false; + }; + Choices.prototype._generatePlaceholderValue = function () { + var config = this.config; + if (!config.placeholder) { + return null; + } + if (this._hasNonChoicePlaceholder) { + return config.placeholderValue; + } + if (this._isSelectElement) { + var placeholderOption = this.passedElement.placeholderOption; + return placeholderOption ? placeholderOption.text : null; + } + return null; + }; + Choices.prototype._warnChoicesInitFailed = function (caller) { + if (this.config.silent) { + return; + } + if (!this.initialised) { + throw new TypeError("".concat(caller, " called on a non-initialised instance of Choices")); + } + else if (!this.initialisedOK) { + throw new TypeError("".concat(caller, " called for an element which has multiple instances of Choices initialised on it")); + } + }; + Choices.version = '11.0.0-rc7'; + return Choices; + }()); + + return Choices; + +})); diff --git a/public/assets/scripts/choices.search-prefix.min.js b/public/assets/scripts/choices.search-prefix.min.js new file mode 100644 index 000000000..eaca36f82 --- /dev/null +++ b/public/assets/scripts/choices.search-prefix.min.js @@ -0,0 +1,2 @@ +/*! choices.js v11.0.0-rc7 | © 2024 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Choices=t()}(this,(function(){"use strict";var e=function(t,i){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i])},e(t,i)};function t(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function n(){this.constructor=t}e(t,i),t.prototype=null===i?Object.create(i):(n.prototype=i.prototype,new n)}var i=function(){return i=Object.assign||function(e){for(var t,i=1,n=arguments.length;i/g,">").replace(/0?this.element.scrollTop+(e.offsetTop+e.offsetHeight)-(this.element.scrollTop+this.element.offsetHeight):e.offsetTop;requestAnimationFrame((function(){i._animateScroll(n,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t;this.element.scrollTop=e+(n>1?n:1)},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t;this.element.scrollTop=e-(n>1?n:1)},e.prototype._animateScroll=function(e,t){var i=this,n=this.element.scrollTop,s=!1;t>0?(this._scrollDown(n,4,e),ne&&(s=!0)),s&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}(),P=function(){function e(e){var t=e.classNames;this.element=e.element,this.classNames=t,this.isDisabled=!1}return Object.defineProperty(e.prototype,"isActive",{get:function(){return"active"===this.element.dataset.choice},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"dir",{get:function(){return this.element.dir},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!1,configurable:!0}),e.prototype.conceal=function(){var e,t=this.element;(e=t.classList).add.apply(e,w(this.classNames.input)),t.hidden=!0,t.tabIndex=-1;var i=t.getAttribute("style");i&&t.setAttribute("data-choice-orig-style",i),t.setAttribute("data-choice","active")},e.prototype.reveal=function(){var e,t=this.element;(e=t.classList).remove.apply(e,w(this.classNames.input)),t.hidden=!1,t.removeAttribute("tabindex");var i=t.getAttribute("data-choice-orig-style");i?(t.removeAttribute("data-choice-orig-style"),t.setAttribute("style",i)):t.removeAttribute("style"),t.removeAttribute("data-choice")},e.prototype.enable=function(){var e=this.element;e.removeAttribute("disabled"),e.disabled=!1,this.isDisabled=!1},e.prototype.disable=function(){var e=this.element;e.setAttribute("disabled",""),e.disabled=!0,this.isDisabled=!0},e.prototype.triggerEvent=function(e,t){var i;void 0===(i=t||{})&&(i=null),this.element.dispatchEvent(new CustomEvent(e,{detail:i,bubbles:!0,cancelable:!0}))},e}(),M=function(e){function i(){return null!==e&&e.apply(this,arguments)||this}return t(i,e),i}(P),j=function(e,t){return void 0===t&&(t=!0),void 0===e?t:!!e},k=function(e){if("string"==typeof e&&(e=e.split(" ").filter((function(e){return e.length}))),Array.isArray(e)&&e.length)return e},H=function(e,t){if("string"==typeof e)return H({value:e,label:e},!1);var i=e;if("choices"in i){if(!t)throw new TypeError("optGroup is not allowed");var n=i,s=n.choices.map((function(e){return H(e,!1)}));return{id:0,label:E(n.label)||n.value,active:!!s.length,disabled:!!n.disabled,choices:s}}var o=i;return{id:0,groupId:0,score:0,rank:0,value:o.value,label:o.label||o.value,active:j(o.active),selected:j(o.selected,!1),disabled:j(o.disabled,!1),placeholder:j(o.placeholder,!1),highlighted:!1,labelClass:k(o.labelClass),labelDescription:o.labelDescription,customProperties:o.customProperties}},K=function(e){return"SELECT"===e.tagName},B=function(e){function i(t){var i=t.template,n=t.extractPlaceholder,s=e.call(this,{element:t.element,classNames:t.classNames})||this;return s.template=i,s.extractPlaceholder=n,s}return t(i,e),Object.defineProperty(i.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!1,configurable:!0}),i.prototype.addOptions=function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){var n=e;if(!n.element){var s=t.template(n);i.appendChild(s),n.element=s}})),this.element.appendChild(i)},i.prototype.optionsAsChoices=function(){var e=this,t=[];return this.element.querySelectorAll(":scope > option, :scope > optgroup").forEach((function(i){!function(e){return"OPTION"===e.tagName}(i)?function(e){return"OPTGROUP"===e.tagName}(i)&&t.push(e._optgroupToChoice(i)):t.push(e._optionToChoice(i))})),t},i.prototype._optionToChoice=function(e){!e.hasAttribute("value")&&e.hasAttribute("placeholder")&&(e.setAttribute("value",""),e.value="");var t=e.dataset;return{id:0,groupId:0,score:0,rank:0,value:e.value,label:e.innerHTML,element:e,active:!0,selected:this.extractPlaceholder?e.selected:e.hasAttribute("selected"),disabled:e.disabled,highlighted:!1,placeholder:this.extractPlaceholder&&(!e.value||e.hasAttribute("placeholder")),labelClass:void 0!==t.labelClass?k(t.labelClass):void 0,labelDescription:void 0!==t.labelDescription?t.labelDescription:void 0,customProperties:A(t.customProperties)}},i.prototype._optgroupToChoice=function(e){var t=this,i=e.querySelectorAll("option"),n=Array.from(i).map((function(e){return t._optionToChoice(e)}));return{id:0,label:e.label||"",element:e,active:!!n.length,disabled:e.disabled,choices:n}},i}(P),V={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,closeDropdownOnSelect:"auto",singleModeForMultiSelect:!1,addChoices:!1,addItems:!0,addItemFilter:function(e){return!!e&&""!==e},removeItems:!0,removeItemButton:!1,removeItemButtonAlignLeft:!1,editItems:!1,allowHTML:!1,allowHtmlUserInput:!1,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:function(e,t){var i=e.label,n=t.label,s=void 0===n?t.value:n;return E(void 0===i?e.value:i).localeCompare(E(s),[],{sensitivity:"base",ignorePunctuation:!0,numeric:!0})},shadowRoot:null,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'.concat(e,'"')},removeItemIconText:function(){return"Remove item"},removeItemLabelText:function(e){return"Remove item: ".concat(e)},maxItemText:function(e){return"Only ".concat(e," values can be added")},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},labelId:"",callbackOnInit:null,callbackOnCreateTemplates:null,classNames:{containerOuter:["choices"],containerInner:["choices__inner"],input:["choices__input"],inputCloned:["choices__input--cloned"],list:["choices__list"],listItems:["choices__list--multiple"],listSingle:["choices__list--single"],listDropdown:["choices__list--dropdown"],item:["choices__item"],itemSelectable:["choices__item--selectable"],itemDisabled:["choices__item--disabled"],itemChoice:["choices__item--choice"],description:["choices__description"],placeholder:["choices__placeholder"],group:["choices__group"],groupHeading:["choices__heading"],button:["choices__button"],activeState:["is-active"],focusState:["is-focused"],openState:["is-open"],disabledState:["is-disabled"],highlightedState:["is-highlighted"],selectedState:["is-selected"],flippedState:["is-flipped"],loadingState:["is-loading"],notice:["choices__notice"],addChoice:["choices__item--selectable","add-choice"],noResults:["has-no-results"],noChoices:["has-no-choices"]},appendGroupInSearch:!1},R={groups:function(e,t){var i=e,n=!0;switch(t.type){case h:i.push(t.group);break;case l:i=[];break;default:n=!1}return{state:i,update:n}},items:function(e,t){var i=e,n=!0;switch(t.type){case u:var s=t.item;s.selected=!0,(o=s.element)&&(o.selected=!0,o.setAttribute("selected","")),i.push(s),i.forEach((function(e){e.highlighted=!1}));break;case d:var o,a=t.item;if(a.selected=!1,o=a.element){o.selected=!1,o.removeAttribute("selected");var c=o.parentElement;c&&K(c)&&c.type===L&&(c.value="")}i=i.filter((function(e){return e.id!==a.id}));break;case r:i=i.filter((function(e){return e.id!==t.choice.id}));break;case p:var l=t;i.forEach((function(e){e.id===l.item.id&&(e.highlighted=l.highlighted)}));break;default:n=!1}return{state:i,update:n}},choices:function(e,t){var i=e,n=!0;switch(t.type){case o:i.push(t.choice);break;case r:i=i.filter((function(e){return e.id!==t.choice.id}));break;case u:case d:break;case a:var s=[];t.results.forEach((function(e){s[e.item.id]=e})),i.forEach((function(e){var t=s[e.id];void 0!==t?(e.score=t.score,e.rank=t.rank,e.active=!0):(e.score=0,e.rank=0,e.active=!1)}));break;case c:i.forEach((function(e){e.active=t.active}));break;case l:i=[];break;default:n=!1}return{state:i,update:n}}},q=function(){function e(){this._state=this.defaultState,this._listeners=[],this._txn=0}return Object.defineProperty(e.prototype,"defaultState",{get:function(){return{groups:[],items:[],choices:[]}},enumerable:!1,configurable:!0}),e.prototype.changeSet=function(e){return{groups:e,items:e,choices:e}},e.prototype.reset=function(){this._state=this.defaultState;var e=this.changeSet(!0);this._txn?this._changeSet=e:this._listeners.forEach((function(t){return t(e)}))},e.prototype.subscribe=function(e){this._listeners.push(e)},e.prototype.dispatch=function(e){var t=this._state,i=!1,n=this._changeSet||this.changeSet(!1);Object.keys(R).forEach((function(s){var o=R[s](t[s],e);o.update&&(i=!0,n[s]=!0,t[s]=o.state)})),i&&(this._txn?this._changeSet=n:this._listeners.forEach((function(e){return e(n)})))},e.prototype.withTxn=function(e){this._txn++;try{e()}finally{if(this._txn=Math.max(0,this._txn-1),!this._txn){var t=this._changeSet;t&&(this._changeSet=void 0,this._listeners.forEach((function(e){return e(t)})))}}},Object.defineProperty(e.prototype,"state",{get:function(){return this._state},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"items",{get:function(){return this.state.items},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"highlightedActiveItems",{get:function(){return this.items.filter((function(e){return!e.disabled&&e.active&&e.highlighted}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"choices",{get:function(){return this.state.choices},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeChoices",{get:function(){return this.choices.filter((function(e){return e.active}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"searchableChoices",{get:function(){return this.choices.filter((function(e){return!e.disabled&&!e.placeholder}))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"groups",{get:function(){return this.state.groups},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"activeGroups",{get:function(){var e=this;return this.state.groups.filter((function(t){var i=t.active&&!t.disabled,n=e.state.choices.some((function(e){return e.active&&!e.disabled}));return i&&n}),[])},enumerable:!1,configurable:!0}),e.prototype.inTxn=function(){return this._txn>0},e.prototype.getChoiceById=function(e){return this.activeChoices.find((function(t){return t.id===e}))},e.prototype.getGroupById=function(e){return this.groups.find((function(t){return t.id===e}))},e}(),G="no-choices",U="no-results",W="add-choice",X=function(){function e(e){this._haystack=[],this._fields=e.searchFields}return e.prototype.index=function(e){this._haystack=e},e.prototype.reset=function(){this._haystack=[]},e.prototype.isEmptyIndex=function(){return!this._haystack.length},e.prototype.search=function(e){var t=this._fields;if(!t||!t.length||!e)return[];var i=e.toLowerCase();return this._haystack.filter((function(e){return t.some((function(t){return t in e&&e[t].toLowerCase().startsWith(i)}))})).map((function(e,t){return{item:e,score:t,rank:t+1}}))},e}(),J=function(e,t){if(t){var i=e.dataset;"string"==typeof t?i.customProperties=t:"object"!=typeof t||function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))return!1;return!0}(t)||(i.customProperties=JSON.stringify(t))}},z=function(e,t,i){var n=t&&e.querySelector("label[for='".concat(t,"']")),s=n&&n.innerText;s&&i.setAttribute("aria-label",s)},Q={containerOuter:function(e,t,i,n,s,o,r){var a=e.classNames.containerOuter,c=document.createElement("div");return c.className=w(a).join(" "),c.dataset.type=o,t&&(c.dir=t),n&&(c.tabIndex=0),i&&(c.setAttribute("role",s?"combobox":"listbox"),s?c.setAttribute("aria-autocomplete","list"):r||z(this._docRoot,this.passedElement.element.id,c),c.setAttribute("aria-haspopup","true"),c.setAttribute("aria-expanded","false")),r&&c.setAttribute("aria-labelledby",r),c},containerInner:function(e){var t=e.classNames.containerInner,i=document.createElement("div");return i.className=w(t).join(" "),i},itemList:function(e,t){var i=e.searchEnabled,n=e.classNames,s=n.list,o=n.listSingle,r=n.listItems,a=document.createElement("div");return a.className="".concat(w(s).join(" ")," ").concat(t?w(o).join(" "):w(r).join(" ")),this._isSelectElement&&i&&a.setAttribute("role","listbox"),a},placeholder:function(e,t){var i=e.allowHTML,n=e.classNames.placeholder,s=document.createElement("div");return s.className=w(n).join(" "),s.innerHTML=C(i,t),s},item:function(e,t,i){var n,s,o,r=e.allowHTML,a=e.removeItemButtonAlignLeft,c=e.removeItemIconText,l=e.removeItemLabelText,h=e.classNames,u=h.item,d=h.button,p=h.highlightedState,m=h.itemSelectable,f=h.placeholder,v=t.id,_=t.value,g=t.label,b=t.labelClass,S=t.labelDescription,I=t.customProperties,A=t.disabled,T=t.highlighted,O=t.placeholder,L=E(_),x=document.createElement("div");if(x.className=w(u).join(" "),b){var D=document.createElement("span");D.innerHTML=C(r,g),D.className=w(b).join(" "),x.appendChild(D)}else x.innerHTML=C(r,g);var N=x.dataset;if(N.item="",N.id=v,N.value=L,b&&(N.labelClass=w(b).join(" ")),S&&(N.labelDescription=S),J(x,I),(A||this.containerOuter.isDisabled)&&x.setAttribute("aria-disabled","true"),this._isSelectElement&&(x.setAttribute("aria-selected","true"),x.setAttribute("role","option")),O&&((n=x.classList).add.apply(n,w(f)),N.placeholder=""),(s=x.classList).add.apply(s,w(T?p:m)),i){A&&(o=x.classList).remove.apply(o,w(m)),N.deletable="";var F=document.createElement("button");F.type="button",F.className=w(d).join(" "),F.innerHTML=y(c,_);var P=y(l,_);P&&F.setAttribute("aria-label",P),F.dataset.button="",a?x.insertAdjacentElement("afterbegin",F):x.appendChild(F)}return x},choiceList:function(e,t){var i=e.classNames.list,n=document.createElement("div");return n.className=w(i).join(" "),t||n.setAttribute("aria-multiselectable","true"),n.setAttribute("role","listbox"),n},choiceGroup:function(e,t){var i=e.allowHTML,n=e.classNames,s=n.group,o=n.groupHeading,r=n.itemDisabled,a=t.id,c=t.label,l=t.disabled,h=E(c),u=document.createElement("div");u.className="".concat(w(s).join(" ")," ").concat(l?w(r).join(" "):""),u.setAttribute("role","group");var d=u.dataset;d.group="",d.id=a,d.value=h,l&&u.setAttribute("aria-disabled","true");var p=document.createElement("div");return p.className=w(o).join(" "),p.innerHTML=C(i,c),u.appendChild(p),u},choice:function(e,t,i){var n,s,o,r,a,c=e.allowHTML,l=e.classNames,h=l.item,u=l.itemChoice,d=l.itemSelectable,p=l.selectedState,m=l.itemDisabled,f=l.description,v=l.placeholder,_=t.id,g=t.label,y=t.groupId,b=t.elementId,S=t.labelClass,I=t.labelDescription,A=t.disabled,T=t.selected,O=t.placeholder,L=E(t.value),x=document.createElement("div");x.id=b,x.className="".concat(w(h).join(" ")," ").concat(w(u).join(" "));var D=x;if(S){var N=document.createElement("span");N.innerHTML=C(c,g),N.className=w(S).join(" "),D=N,x.appendChild(N)}else x.innerHTML=C(c,g);if(I){var F="".concat(b,"-description");D.setAttribute("aria-describedby",F);var P=document.createElement("span");P.innerHTML=C(c,I),P.id=F,(n=P.classList).add.apply(n,w(f)),x.appendChild(P)}T&&(s=x.classList).add.apply(s,w(p)),O&&(o=x.classList).add.apply(o,w(v));var M=x.dataset,j=y&&y>0;return x.setAttribute("role",j?"treeitem":"option"),j&&(M.groupId="".concat(y)),M.choice="",M.id=_,M.value=L,M.selectText=i,S&&(M.labelClass=w(S).join(" ")),I&&(M.labelDescription=I),A?((r=x.classList).add.apply(r,w(m)),M.choiceDisabled="",x.setAttribute("aria-disabled","true")):((a=x.classList).add.apply(a,w(d)),M.choiceSelectable=""),x},input:function(e,t){var i=e.classNames,n=i.input,s=i.inputCloned,o=e.labelId,r=document.createElement("input");return r.type="search",r.className="".concat(w(n).join(" ")," ").concat(w(s).join(" ")),r.autocomplete="off",r.autocapitalize="off",r.spellcheck=!1,r.setAttribute("role","textbox"),r.setAttribute("aria-autocomplete","list"),t?r.setAttribute("aria-label",t):o||z(this._docRoot,this.passedElement.element.id,r),r},dropdown:function(e){var t,i,n=e.classNames,s=n.list,o=n.listDropdown,r=document.createElement("div");return(t=r.classList).add.apply(t,w(s)),(i=r.classList).add.apply(i,w(o)),r.setAttribute("aria-expanded","false"),r},notice:function(e,t,i){var s=e.classNames,o=s.itemChoice,r=s.addChoice,a=s.noResults,c=s.noChoices,l=s.notice;void 0===i&&(i="");var h=n(n(n([],w(s.item),!0),w(o),!0),w(l),!0);switch(i){case W:h.push.apply(h,w(r));break;case U:h.push.apply(h,w(a));break;case G:h.push.apply(h,w(c))}var u=document.createElement("div");return u.innerHTML=t,u.className=h.join(" "),i===W&&(u.dataset.choiceSelectable="",u.dataset.choice=""),u},option:function(e){var t=E(e.label),i=new Option(t,e.value,!1,e.selected),n=e.labelClass,s=e.labelDescription;return n&&(i.dataset.labelClass=w(n).join(" ")),s&&(i.dataset.labelDescription=s),J(i,e.customProperties),i.disabled=e.disabled,e.selected&&i.setAttribute("selected",""),i}},Y="-ms-scroll-limit"in document.documentElement.style&&"-ms-ime-align"in document.documentElement.style,Z={},$=function(e){if(e){var t=e.dataset.id;return t?parseInt(t,10):void 0}};return function(){function e(t,n){void 0===t&&(t="[data-choice]"),void 0===n&&(n={});var s=this;this.initialisedOK=void 0,this._hasNonChoicePlaceholder=!1,this._lastAddedChoiceId=0,this._lastAddedGroupId=0;var o=e.defaults;this.config=i(i(i({},o.allOptions),o.options),n),m.forEach((function(e){s.config[e]=i(i(i({},o.allOptions[e]),o.options[e]),n[e])}));var r=this.config;r.silent||this._validateConfig();var a=r.shadowRoot||document.documentElement;this._docRoot=a;var c="string"==typeof t?a.querySelector(t):t;if(!c||"object"!=typeof c||"INPUT"!==c.tagName&&!K(c)){if(!c&&"string"==typeof t)throw TypeError("Selector ".concat(t," failed to find an element"));throw TypeError("Expected one of the following types text|select-one|select-multiple")}if(this._elementType=c.type,this._isTextElement=this._elementType===O,(this._isTextElement||1!==r.maxItemCount)&&(r.singleModeForMultiSelect=!1),r.singleModeForMultiSelect&&(this._elementType=x),this._isSelectOneElement=this._elementType===L,this._isSelectMultipleElement=this._elementType===x,this._isSelectElement=this._isSelectOneElement||this._isSelectMultipleElement,this._canAddUserChoices=this._isTextElement&&r.addItems||this._isSelectElement&&r.addChoices,["auto","always"].includes("".concat(r.renderSelectedChoices))||(r.renderSelectedChoices="auto"),r.closeDropdownOnSelect="auto"===r.closeDropdownOnSelect?this._isTextElement||this._isSelectOneElement||r.singleModeForMultiSelect:j(r.closeDropdownOnSelect),r.placeholder&&(r.placeholderValue?this._hasNonChoicePlaceholder=!0:c.dataset.placeholder&&(this._hasNonChoicePlaceholder=!0,r.placeholderValue=c.dataset.placeholder)),n.addItemFilter&&"function"!=typeof n.addItemFilter){var l=n.addItemFilter instanceof RegExp?n.addItemFilter:new RegExp(n.addItemFilter);r.addItemFilter=l.test.bind(l)}if(this.passedElement=this._isTextElement?new M({element:c,classNames:r.classNames}):new B({element:c,classNames:r.classNames,template:function(e){return s._templates.option(e)},extractPlaceholder:r.placeholder&&!this._hasNonChoicePlaceholder}),this.initialised=!1,this._store=new q,this._currentValue="",r.searchEnabled=!this._isTextElement&&r.searchEnabled||this._elementType===x,this._canSearch=r.searchEnabled,this._isScrollingOnIe=!1,this._highlightPosition=0,this._wasTap=!0,this._placeholderValue=this._generatePlaceholderValue(),this._baseId=function(e){var t=e.id||e.name&&"".concat(e.name,"-").concat(v(2))||v(4);return t=t.replace(/(:|\.|\[|\]|,)/g,""),"".concat("choices-","-").concat(t)}(c),this._direction=this.passedElement.dir,!this._direction){var h=window.getComputedStyle(this.passedElement.element).direction;h!==window.getComputedStyle(document.documentElement).direction&&(this._direction=h)}if(this._idNames={itemChoice:"item-choice"},this._templates=o.templates,this._render=this._render.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this),this._onKeyUp=this._onKeyUp.bind(this),this._onKeyDown=this._onKeyDown.bind(this),this._onInput=this._onInput.bind(this),this._onClick=this._onClick.bind(this),this._onTouchMove=this._onTouchMove.bind(this),this._onTouchEnd=this._onTouchEnd.bind(this),this._onMouseDown=this._onMouseDown.bind(this),this._onMouseOver=this._onMouseOver.bind(this),this._onFormReset=this._onFormReset.bind(this),this._onSelectKey=this._onSelectKey.bind(this),this._onEnterKey=this._onEnterKey.bind(this),this._onEscapeKey=this._onEscapeKey.bind(this),this._onDirectionKey=this._onDirectionKey.bind(this),this._onDeleteKey=this._onDeleteKey.bind(this),this.passedElement.isActive)return r.silent||console.warn("Trying to initialise Choices on element already initialised",{element:t}),this.initialised=!0,void(this.initialisedOK=!1);this.init(),this._initialItems=this._store.items.map((function(e){return e.value}))}return Object.defineProperty(e,"defaults",{get:function(){return Object.preventExtensions({get options(){return Z},get allOptions(){return V},get templates(){return Q}})},enumerable:!1,configurable:!0}),e.prototype.init=function(){if(!this.initialised&&void 0===this.initialisedOK){this._searcher=new X(this.config),this._loadChoices(),this._createTemplates(),this._createElements(),this._createStructure(),this._isTextElement&&!this.config.addItems||this.passedElement.element.hasAttribute("disabled")||this.passedElement.element.closest("fieldset:disabled")?this.disable():(this.enable(),this._addEventListeners()),this._initStore(),this.initialised=!0,this.initialisedOK=!0;var e=this.config.callbackOnInit;e&&"function"==typeof e&&e.call(this)}},e.prototype.destroy=function(){this.initialised&&(this._removeEventListeners(),this.passedElement.reveal(),this.containerOuter.unwrap(this.passedElement.element),this._store._listeners=[],this.clearStore(),this._stopSearch(),this._templates=e.defaults.templates,this.initialised=!1,this.initialisedOK=void 0)},e.prototype.enable=function(){var e=this.passedElement,t=this.containerOuter;return e.isDisabled&&e.enable(),t.isDisabled&&(this._addEventListeners(),this.input.enable(),t.enable(),this._render()),this},e.prototype.disable=function(){var e=this.passedElement,t=this.containerOuter;return e.isDisabled||e.disable(),t.isDisabled||(this._removeEventListeners(),this.input.disable(),t.disable(),this._render()),this},e.prototype.highlightItem=function(e,t){if(void 0===t&&(t=!0),!e||!e.id)return this;var i=this._store.choices.find((function(t){return t.id===e.id}));return!i||i.highlighted||(this._store.dispatch(f(i,!0)),t&&this.passedElement.triggerEvent("highlightItem",this._getChoiceForOutput(i))),this},e.prototype.unhighlightItem=function(e,t){if(void 0===t&&(t=!0),!e||!e.id)return this;var i=this._store.choices.find((function(t){return t.id===e.id}));return i&&i.highlighted?(this._store.dispatch(f(i,!1)),t&&this.passedElement.triggerEvent("highlightItem",this._getChoiceForOutput(i)),this):this},e.prototype.highlightAll=function(){var e=this;return this._store.withTxn((function(){e._store.items.forEach((function(t){return e.highlightItem(t)}))})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.withTxn((function(){e._store.items.forEach((function(t){return e.unhighlightItem(t)}))})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.withTxn((function(){t._store.items.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)}))})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.withTxn((function(){t._store.items.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)}))})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.withTxn((function(){t._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)}))})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive||requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent("showDropdown")})),this},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent("hideDropdown")})),this):this},e.prototype.getValue=function(e){var t=this;void 0===e&&(e=!1);var i=this._store.items.reduce((function(i,n){var s=e?n.value:t._getChoiceForOutput(n);return i.push(s),i}),[]);return this._isSelectOneElement||this.config.singleModeForMultiSelect?i[0]:i},e.prototype.setValue=function(e){var t=this;return this.initialisedOK?(this._store.withTxn((function(){e.forEach((function(e){e&&t._addChoice(H(e,!1))}))})),this._searcher.reset(),this):(this._warnChoicesInitFailed("setValue"),this)},e.prototype.setChoiceByValue=function(e){var t=this;return this.initialisedOK?(this._isTextElement||(this._store.withTxn((function(){(Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)}))})),this._searcher.reset()),this):(this._warnChoicesInitFailed("setChoiceByValue"),this)},e.prototype.setChoices=function(e,t,n,s){var o=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===n&&(n="label"),void 0===s&&(s=!1),!this.initialisedOK)return this._warnChoicesInitFailed("setChoices"),this;if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(s&&this.clearChoices(),"function"==typeof e){var r=e(this);if("function"==typeof Promise&&r instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return o._handleLoadingState(!0)})).then((function(){return r})).then((function(e){return o.setChoices(e,t,n,s)})).catch((function(e){o.config.silent||console.error(e)})).then((function(){return o._handleLoadingState(!1)})).then((function(){return o}));if(!Array.isArray(r))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: ".concat(typeof r));return this.setChoices(r,t,n,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._store.withTxn((function(){var s="value"===t,r="label"===n;e.forEach((function(e){if("choices"in e){var a=e;r||(a=i(i({},a),{label:a[n]})),o._addGroup(H(a,!0))}else{var c=e;r&&s||(c=i(i({},c),{value:c[t],label:c[n]})),o._addChoice(H(c,!1))}}))})),this._searcher.reset(),this},e.prototype.refresh=function(e,t,i){var n=this;return void 0===e&&(e=!1),void 0===t&&(t=!1),void 0===i&&(i=!1),this._isSelectElement?(this._store.withTxn((function(){var s=n.passedElement.optionsAsChoices(),o={};i||n._store.items.forEach((function(e){e.id&&e.active&&e.selected&&!e.disabled&&(o[e.value]=!0)})),s.forEach((function(e){if(!("choices"in e)){var t=e;i?t.selected=!1:o[t.value]&&(t.selected=!0)}})),n.clearStore(),n._addPredefinedChoices(s,t,e),n._isSearching&&n._searchChoices(n.input.value)})),this):(this.config.silent||console.warn("refresh method can only be used on choices backed by a element'); + } + return this; + } + this._store.withTxn(function () { + var choicesFromOptions = _this.passedElement.optionsAsChoices(); + var items = _this._store.items; + // Build the list of items which require preserving + var existingItems = {}; + if (!deselectAll) { + items.forEach(function (choice) { + if (choice.id && choice.active && choice.selected && !choice.disabled) { + existingItems[choice.value] = true; + } + }); + } + choicesFromOptions.forEach(function (groupOrChoice) { + if ('choices' in groupOrChoice) { + return; + } + var choice = groupOrChoice; + if (deselectAll) { + choice.selected = false; + } + else if (existingItems[choice.value]) { + choice.selected = true; + } + }); + _this.clearStore(); + /* @todo only generate add events for the added options instead of all + if (withEvents) { + items.forEach((choice) => { + if (existingItems[choice.value]) { + this.passedElement.triggerEvent( + EventType.removeItem, + this._getChoiceForEvent(choice), + ); + } + }); + } + */ + // load new choices & items + _this._addPredefinedChoices(choicesFromOptions, selectFirstOption, withEvents); + // re-do search if required + if (_this._isSearching) { + _this._searchChoices(_this.input.value); + } + }); + return this; + }; + Choices.prototype.removeChoice = function (value) { + var choice = this._store.choices.find(function (c) { return c.value === value; }); + if (!choice) { + return this; + } + this._store.dispatch(removeChoice(choice)); + // @todo integrate with Store + this._searcher.reset(); + if (choice.selected) { + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(choice)); + } + return this; + }; + Choices.prototype.clearChoices = function () { + this.passedElement.element.innerHTML = ''; + this._store.dispatch(clearChoices()); + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearStore = function () { + this._store.reset(); + this._lastAddedChoiceId = 0; + this._lastAddedGroupId = 0; + // @todo integrate with Store + this._searcher.reset(); + return this; + }; + Choices.prototype.clearInput = function () { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); + this._clearNotice(); + if (this._isSearching) { + this._stopSearch(); + } + return this; + }; + Choices.prototype._validateConfig = function () { + var config = this.config; + var invalidConfigOptions = diff(config, DEFAULT_CONFIG); + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + if (config.allowHTML && config.allowHtmlUserInput) { + if (config.addItems) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addItems all being true is strongly not recommended and may lead to XSS attacks'); + } + if (config.addChoices) { + console.warn('Warning: allowHTML/allowHtmlUserInput/addChoices all being true is strongly not recommended and may lead to XSS attacks'); + } + } + }; + Choices.prototype._render = function (changes) { + if (changes === void 0) { changes = { choices: true, groups: true, items: true }; } + if (this._store.inTxn()) { + return; + } + if (this._isSelectElement) { + if (changes.choices || changes.groups) { + this._renderChoices(); + } + } + if (changes.items) { + this._renderItems(); + } + }; + Choices.prototype._renderChoices = function () { + var _this = this; + this.choiceList.clear(); + if (!this._canAddItems()) { + return; // block rendering choices if the input limit is reached. + } + var config = this.config; + var _a = this._store, activeGroups = _a.activeGroups, activeChoices = _a.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + var noChoices = true; + if (activeChoices.length) { + if (config.resetScrollPosition) { + requestAnimationFrame(function () { return _this.choiceList.scrollToTop(); }); + } + // If we have grouped options + if (activeGroups.length && !this._isSearching) { + if (!this._hasNonChoicePlaceholder) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { return activeChoice.placeholder && activeChoice.groupId === -1; }); + if (activePlaceholders.length) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + } + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } + else { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } + noChoices = !choiceListFragment.childNodes.length; + } + var notice = this._notice; + if (noChoices) { + if (!notice) { + this._notice = { + text: resolveStringFunction(config.noChoicesText), + type: NoticeTypes.noChoices, + }; + } + } + else if (notice && notice.type === NoticeTypes.noChoices) { + this._notice = undefined; + } + this._renderNotice(); + if (!noChoices) { + this.choiceList.element.append(choiceListFragment); + this._highlightChoice(); + } + }; + Choices.prototype._renderItems = function () { + var items = this._store.items || []; + this.itemList.clear(); + // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) + var itemListFragment = this._createItemsFragment(items); + // If we have items to add, append them + if (itemListFragment.childNodes.length) { + this.itemList.element.append(itemListFragment); + } + }; + Choices.prototype._createGroupsFragment = function (groups, choices, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + var config = this.config; + var getGroupChoices = function (group) { + return choices.filter(function (choice) { + if (_this._isSelectOneElement) { + return choice.groupId === group.id; + } + return choice.groupId === group.id && (config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; + // If sorting is enabled, filter groups + if (config.shouldSort) { + groups.sort(config.sorter); + } + // Add Choices without group first, regardless of sort, otherwise they won't be distinguishable + // from the last group + var choicesWithoutGroup = choices.filter(function (c) { return !c.groupId; }); + if (choicesWithoutGroup.length) { + this._createChoicesFragment(choicesWithoutGroup, fragment, false); + } + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + if (groupChoices.length) { + var dropdownGroup = _this._templates.choiceGroup(_this.config, group); + fragment.appendChild(dropdownGroup); + _this._createChoicesFragment(groupChoices, fragment, true); + } + }); + return fragment; + }; + Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + if (withinGroup === void 0) { withinGroup = false; } + // Create a fragment to store our list items (so we don't have to update the DOM for each item) + var _a = this, config = _a.config, isSearching = _a._isSearching, isSelectOneElement = _a._isSelectOneElement; + var searchResultLimit = config.searchResultLimit, renderChoiceLimit = config.renderChoiceLimit; + var groupLookup = []; + var appendGroupInSearch = config.appendGroupInSearch && isSearching; + if (appendGroupInSearch) { + this._store.groups.forEach(function (group) { + groupLookup[group.id] = group.label; + }); + } + if (this._isSelectElement) { + var backingOptions = choices.filter(function (choice) { return !choice.element; }); + if (backingOptions.length) { + this.passedElement.addOptions(backingOptions); + } + } + var skipSelected = config.renderSelectedChoices === 'auto' && !isSelectOneElement; + var placeholderChoices = []; + var normalChoices = []; + choices.forEach(function (choice) { + if ((isSearching && !choice.rank) || (skipSelected && choice.selected)) { + return; + } + if (_this._hasNonChoicePlaceholder || !choice.placeholder) { + normalChoices.push(choice); + } + else { + placeholderChoices.push(choice); + } + }); + if (isSearching) { + // sortByRank is used to ensure stable sorting, as scores are non-unique + // this additionally ensures fuseOptions.sortFn is not ignored + normalChoices.sort(sortByRank); + } + else if (config.shouldSort) { + normalChoices.sort(config.sorter); + } + var sortedChoices = isSelectOneElement && placeholderChoices.length ? __spreadArray(__spreadArray([], placeholderChoices, true), normalChoices, true) : normalChoices; + var choiceLimit = sortedChoices.length; + var limit = choiceLimit; + if (isSearching && searchResultLimit > 0) { + limit = searchResultLimit; + } + else if (renderChoiceLimit > 0 && !withinGroup) { + limit = renderChoiceLimit; + } + if (limit < choiceLimit) { + choiceLimit = limit; + } + choiceLimit--; + // Add each choice to dropdown within range + sortedChoices.every(function (choice, index) { + var dropdownItem = _this._templates.choice(config, choice, config.itemSelectText); + if (appendGroupInSearch && choice.groupId > 0) { + var groupName = groupLookup[choice.groupId]; + if (groupName) { + dropdownItem.innerHTML += " (".concat(groupName, ")"); + } + } + fragment.appendChild(dropdownItem); + return index < choiceLimit; + }); + return fragment; + }; + Choices.prototype._createItemsFragment = function (items, fragment) { + var _this = this; + if (fragment === void 0) { fragment = document.createDocumentFragment(); } + // Create fragment to add elements to + var config = this.config; + var shouldSortItems = config.shouldSortItems, sorter = config.sorter, removeItemButton = config.removeItemButton, delimiter = config.delimiter; + // If sorting is enabled, filter items + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sorter); + } + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items.map(function (_a) { + var value = _a.value; + return value; + }).join(delimiter); + } + var addItemToFragment = function (item) { + // Create new list element + var listItem = _this._templates.item(config, item, removeItemButton); + // Append it to list + fragment.appendChild(listItem); + }; + // Add each list item to list + items.forEach(addItemToFragment); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder && !items.length) { + addItemToFragment(mapInputToChoice({ + selected: true, + value: '', + label: this.config.placeholderValue || '', + active: true, + placeholder: true, + }, false)); + } + return fragment; + }; + Choices.prototype._displayNotice = function (text, type, openDropdown) { + if (openDropdown === void 0) { openDropdown = true; } + var oldNotice = this._notice; + if (oldNotice && + ((oldNotice.type === type && oldNotice.text === text) || + (oldNotice.type === NoticeTypes.addChoice && + (type === NoticeTypes.noResults || type === NoticeTypes.noChoices)))) { + if (openDropdown) { + this.showDropdown(true); + } + return; + } + this._clearNotice(); + this._notice = text + ? { + text: text, + type: type, + } + : undefined; + this._renderNotice(); + if (openDropdown && text) { + this.showDropdown(true); + } + }; + Choices.prototype._clearNotice = function () { + if (!this._notice) { + return; + } + var noticeElement = this.choiceList.element.querySelector(getClassNamesSelector(this.config.classNames.notice)); + if (noticeElement) { + noticeElement.remove(); + } + this._notice = undefined; + }; + Choices.prototype._renderNotice = function () { + var noticeConf = this._notice; + if (noticeConf) { + var notice = this._templates.notice(this.config, noticeConf.text, noticeConf.type); + this.choiceList.prepend(notice); + } + }; + Choices.prototype._getChoiceForOutput = function (choice, keyCode) { + if (!choice) { + return undefined; + } + var group = choice.groupId > 0 ? this._store.getGroupById(choice.groupId) : null; + return { + id: choice.id, + highlighted: choice.highlighted, + labelClass: choice.labelClass, + labelDescription: choice.labelDescription, + customProperties: choice.customProperties, + disabled: choice.disabled, + active: choice.active, + label: choice.label, + placeholder: choice.placeholder, + value: choice.value, + groupValue: group && group.label ? group.label : undefined, + element: choice.element, + keyCode: keyCode, + }; + }; + Choices.prototype._triggerChange = function (value) { + if (value === undefined || value === null) { + return; + } + this.passedElement.triggerEvent("change" /* EventType.change */, { + value: value, + }); + }; + Choices.prototype._handleButtonAction = function (element) { + var items = this._store.items; + if (!items.length || !this.config.removeItems || !this.config.removeItemButton) { + return; + } + var id = element && parseDataSetId(element.parentNode); + var itemToRemove = id && items.find(function (item) { return item.id === id; }); + if (!itemToRemove) { + return; + } + // Remove item associated with button + this._removeItem(itemToRemove); + this._triggerChange(itemToRemove.value); + if (this._isSelectOneElement && !this._hasNonChoicePlaceholder) { + var placeholderChoice = this._store.choices.reverse().find(function (choice) { return !choice.disabled && choice.placeholder; }); + if (placeholderChoice) { + this._addItem(placeholderChoice); + if (placeholderChoice.value) { + this._triggerChange(placeholderChoice.value); + } + } + } + }; + Choices.prototype._handleItemAction = function (element, hasShiftKey) { + var _this = this; + if (hasShiftKey === void 0) { hasShiftKey = false; } + var items = this._store.items; + if (!items.length || !this.config.removeItems || this._isSelectOneElement) { + return; + } + var id = parseDataSetId(element); + if (!id) { + return; + } + // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + items.forEach(function (item) { + if (item.id === id && !item.highlighted) { + _this.highlightItem(item); + } + else if (!hasShiftKey && item.highlighted) { + _this.unhighlightItem(item); + } + }); + // Focus input as without focus, a user cannot do anything with a + // highlighted item + this.input.focus(); + }; + Choices.prototype._handleChoiceAction = function (element) { + var _this = this; + // If we are clicking on an option + var id = parseDataSetId(element); + var choice = id && this._store.getChoiceById(id); + if (!choice || choice.disabled) { + return false; + } + var hasActiveDropdown = this.dropdown.isActive; + if (!choice.selected) { + if (!this._canAddItems()) { + return true; // causes _onEnterKey to early out + } + this._store.withTxn(function () { + _this._addItem(choice, true, true); + _this.clearInput(); + _this.unhighlightAll(); + }); + this._triggerChange(choice.value); + } + // We want to close the dropdown if we are dealing with a single select box + if (hasActiveDropdown && this.config.closeDropdownOnSelect) { + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + return true; + }; + Choices.prototype._handleBackspace = function (items) { + var config = this.config; + if (!config.removeItems || !items.length) { + return; + } + var lastItem = items[items.length - 1]; + var hasHighlightedItems = items.some(function (item) { return item.highlighted; }); + // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items + if (config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + this._removeItem(lastItem); + this._triggerChange(lastItem.value); + } + else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } + this.removeHighlightedItems(true); + } + }; + Choices.prototype._loadChoices = function () { + var _a; + var config = this.config; + if (this._isTextElement) { + // Assign preset items from passed object first + this._presetChoices = config.items.map(function (e) { return mapInputToChoice(e, false); }); + // Add any values passed from attribute + var value = this.passedElement.value; + if (value) { + var elementItems = value + .split(config.delimiter) + .map(function (e) { return mapInputToChoice(e, false); }); + this._presetChoices = this._presetChoices.concat(elementItems); + } + this._presetChoices.forEach(function (choice) { + choice.selected = true; + }); + } + else if (this._isSelectElement) { + // Assign preset choices from passed object + this._presetChoices = config.choices.map(function (e) { return mapInputToChoice(e, true); }); + // Create array of choices from option elements + var choicesFromOptions = this.passedElement.optionsAsChoices(); + if (choicesFromOptions) { + (_a = this._presetChoices).push.apply(_a, choicesFromOptions); + } + } + }; + Choices.prototype._handleLoadingState = function (setLoading) { + if (setLoading === void 0) { setLoading = true; } + var config = this.config; + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); + if (this._isSelectOneElement) { + this.itemList.clear(); + this.itemList.element.append(this._templates.placeholder(config, config.loadingText)); + } + else { + this.input.placeholder = config.loadingText; + } + } + else { + this.enable(); + this.containerOuter.removeLoadingState(); + if (!this._isSelectOneElement) { + this.input.placeholder = this._placeholderValue || ''; + } + } + }; + Choices.prototype._handleSearch = function (value) { + if (!this.input.isFocussed) { + return; + } + var choices = this._store.choices; + var _a = this.config, searchFloor = _a.searchFloor, searchChoices = _a.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { return !option.active; }); + // Check that we have a value to search and the input was an alphanumeric character + if (value !== null && typeof value !== 'undefined' && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; + if (resultCount !== null) { + // Trigger search event + this.passedElement.triggerEvent("search" /* EventType.search */, { + value: value, + resultCount: resultCount, + }); + } + } + else if (hasUnactiveChoices) { + this._stopSearch(); + } + }; + Choices.prototype._canAddItems = function () { + var config = this.config; + var maxItemCount = config.maxItemCount, maxItemText = config.maxItemText; + if (!config.singleModeForMultiSelect && maxItemCount > 0 && maxItemCount <= this._store.items.length) { + this._displayNotice(typeof maxItemText === 'function' ? maxItemText(maxItemCount) : maxItemText, NoticeTypes.addChoice); + return false; + } + return true; + }; + Choices.prototype._canCreateItem = function (value) { + var config = this.config; + var canAddItem = true; + var notice = ''; + if (canAddItem && typeof config.addItemFilter === 'function' && !config.addItemFilter(value)) { + canAddItem = false; + notice = resolveNoticeFunction(config.customAddItemText, value); + } + if (canAddItem) { + var foundChoice = this._store.choices.find(function (choice) { return config.valueComparer(choice.value, value); }); + if (this._isSelectElement) { + // for exact matches, do not prompt to add it as a custom choice + if (foundChoice) { + this._displayNotice('', NoticeTypes.addChoice); + return false; + } + } + else if (this._isTextElement && !config.duplicateItemsAllowed) { + if (foundChoice) { + canAddItem = false; + notice = resolveNoticeFunction(config.uniqueItemText, value); + } + } + } + if (canAddItem) { + notice = resolveNoticeFunction(config.addItemText, value); + } + if (notice) { + this._displayNotice(notice, NoticeTypes.addChoice); + } + return canAddItem; + }; + Choices.prototype._searchChoices = function (value) { + var newValue = value.trim().replace(/\s{2,}/, ' '); + // signal input didn't change search + if (!newValue.length || newValue === this._currentValue) { + return null; + } + var searcher = this._searcher; + if (searcher.isEmptyIndex()) { + searcher.index(this._store.searchableChoices); + } + // If new value matches the desired length and is not the same as the current value with a space + var results = searcher.search(newValue); + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; + var notice = this._notice; + var noticeType = notice && notice.type; + if (noticeType !== NoticeTypes.addChoice) { + if (!results.length) { + this._displayNotice(resolveStringFunction(this.config.noResultsText), NoticeTypes.noResults); + } + else if (noticeType === NoticeTypes.noResults) { + this._clearNotice(); + } + } + this._store.dispatch(filterChoices(results)); + return results.length; + }; + Choices.prototype._stopSearch = function () { + var wasSearching = this._isSearching; + this._currentValue = ''; + this._isSearching = false; + if (wasSearching) { + this._store.dispatch(activateChoices(true)); + } + }; + Choices.prototype._addEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + // capture events - can cancel event processing or propagation + documentElement.addEventListener('touchend', this._onTouchEnd, true); + outerElement.addEventListener('keydown', this._onKeyDown, true); + outerElement.addEventListener('mousedown', this._onMouseDown, true); + // passive events - doesn't call `preventDefault` or `stopPropagation` + documentElement.addEventListener('click', this._onClick, { passive: true }); + documentElement.addEventListener('touchmove', this._onTouchMove, { + passive: true, + }); + this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { + passive: true, + }); + if (this._isSelectOneElement) { + outerElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + outerElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + } + inputElement.addEventListener('keyup', this._onKeyUp, { + passive: true, + }); + inputElement.addEventListener('input', this._onInput, { + passive: true, + }); + inputElement.addEventListener('focus', this._onFocus, { + passive: true, + }); + inputElement.addEventListener('blur', this._onBlur, { + passive: true, + }); + if (inputElement.form) { + inputElement.form.addEventListener('reset', this._onFormReset, { + passive: true, + }); + } + this.input.addEventListeners(); + }; + Choices.prototype._removeEventListeners = function () { + var documentElement = this._docRoot; + var outerElement = this.containerOuter.element; + var inputElement = this.input.element; + documentElement.removeEventListener('touchend', this._onTouchEnd, true); + outerElement.removeEventListener('keydown', this._onKeyDown, true); + outerElement.removeEventListener('mousedown', this._onMouseDown, true); + documentElement.removeEventListener('click', this._onClick); + documentElement.removeEventListener('touchmove', this._onTouchMove); + this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); + if (this._isSelectOneElement) { + outerElement.removeEventListener('focus', this._onFocus); + outerElement.removeEventListener('blur', this._onBlur); + } + inputElement.removeEventListener('keyup', this._onKeyUp); + inputElement.removeEventListener('input', this._onInput); + inputElement.removeEventListener('focus', this._onFocus); + inputElement.removeEventListener('blur', this._onBlur); + if (inputElement.form) { + inputElement.form.removeEventListener('reset', this._onFormReset); + } + this.input.removeEventListeners(); + }; + Choices.prototype._onKeyDown = function (event) { + var keyCode = event.keyCode; + var items = this._store.items; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.element.hasChildNodes(); + /* + See: + https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key + https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values + https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - UTF-16 surrogate pairs + https://stackoverflow.com/a/70866532 - "Unidentified" for mobile + http://www.unicode.org/versions/Unicode5.2.0/ch16.pdf#G19635 - U+FFFF is reserved (Section 16.7) + + Logic: when a key event is sent, `event.key` represents its printable value _or_ one + of a large list of special values indicating meta keys/functionality. In addition, + key events for compose functionality contain a value of `Dead` when mid-composition. + + I can't quite verify it, but non-English IMEs may also be able to generate key codes + for code points in the surrogate-pair range, which could potentially be seen as having + key.length > 1. Since `Fn` is one of the special keys, we can't distinguish by that + alone. + + Here, key.length === 1 means we know for sure the input was printable and not a special + `key` value. When the length is greater than 1, it could be either a printable surrogate + pair or a special `key` value. We can tell the difference by checking if the _character + code_ value (not code point!) is in the "surrogate pair" range or not. + + We don't use .codePointAt because an invalid code point would return 65535, which wouldn't + pass the >= 0x10000 check we would otherwise use. + + > ...The Unicode Standard sets aside 66 noncharacter code points. The last two code points + > of each plane are noncharacters: U+FFFE and U+FFFF on the BMP... + */ + var wasPrintableChar = event.key.length === 1 || + (event.key.length === 2 && event.key.charCodeAt(0) >= 0xd800) || + event.key === 'Unidentified'; + if (!this._isTextElement && !hasActiveDropdown) { + this.showDropdown(); + if (!this.input.isFocussed && wasPrintableChar) { + /* + We update the input value with the pressed key as + the input was not focussed at the time of key press + therefore does not have the value of the key. + */ + this.input.value += event.key; + } + } + switch (keyCode) { + case 65 /* KeyCodeMap.A_KEY */: + return this._onSelectKey(event, hasItems); + case 13 /* KeyCodeMap.ENTER_KEY */: + return this._onEnterKey(event, hasActiveDropdown); + case 27 /* KeyCodeMap.ESC_KEY */: + return this._onEscapeKey(event, hasActiveDropdown); + case 38 /* KeyCodeMap.UP_KEY */: + case 33 /* KeyCodeMap.PAGE_UP_KEY */: + case 40 /* KeyCodeMap.DOWN_KEY */: + case 34 /* KeyCodeMap.PAGE_DOWN_KEY */: + return this._onDirectionKey(event, hasActiveDropdown); + case 8 /* KeyCodeMap.DELETE_KEY */: + case 46 /* KeyCodeMap.BACK_KEY */: + return this._onDeleteKey(event, items, hasFocusedInput); + } + }; + Choices.prototype._onKeyUp = function ( /* event: KeyboardEvent */) { + this._canSearch = this.config.searchEnabled; + }; + Choices.prototype._onInput = function ( /* event: InputEvent */) { + var value = this.input.value; + if (!value) { + if (this._isTextElement) { + this.hideDropdown(true); + } + else { + this._stopSearch(); + } + this._clearNotice(); + return; + } + if (!this._canAddItems()) { + return; + } + if (this._canSearch) { + // do the search even if the entered text can not be added + this._handleSearch(value); + } + if (!this._canAddUserChoices) { + return; + } + // determine if a notice needs to be displayed for why a search result can't be added + this._canCreateItem(value); + if (this._isSelectElement) { + this._highlightPosition = 0; // reset to select the notice and/or exact match + this._highlightChoice(); + } + }; + Choices.prototype._onSelectKey = function (event, hasItems) { + var ctrlKey = event.ctrlKey, metaKey = event.metaKey; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; + // If CTRL + A or CMD + A have been pressed and there are items to select + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + if (shouldHightlightAll) { + this.highlightAll(); + } + } + }; + Choices.prototype._onEnterKey = function (event, hasActiveDropdown) { + var _this = this; + var config = this.config; + var value = this.input.value; + var target = event.target; + var targetWasRemoveButton = target && target.hasAttribute('data-button'); + event.preventDefault(); + if (targetWasRemoveButton) { + this._handleButtonAction(target); + return; + } + if (!hasActiveDropdown) { + if (this._isSelectElement || this._notice) { + this.showDropdown(); + } + return; + } + var highlightedChoice = this.dropdown.element.querySelector(getClassNamesSelector(config.classNames.highlightedState)); + if (highlightedChoice && this._handleChoiceAction(highlightedChoice)) { + return; + } + if (!target || !value) { + this.hideDropdown(true); + return; + } + if (!this._canAddItems()) { + return; + } + var addedItem = false; + this._store.withTxn(function () { + addedItem = _this._findAndSelectChoiceByValue(value, true); + if (!addedItem) { + if (!_this._canAddUserChoices) { + return; + } + if (!_this._canCreateItem(value)) { + return; + } + var sanitisedValue = sanitise(value); + var userValue = config.allowHtmlUserInput || sanitisedValue === value ? value : { escaped: sanitisedValue, raw: value }; + _this._addChoice(mapInputToChoice({ + value: userValue, + label: userValue, + selected: true, + }, false), true, true); + addedItem = true; + } + _this.clearInput(); + _this.unhighlightAll(); + }); + if (!addedItem) { + return; + } + this._triggerChange(value); + if (config.closeDropdownOnSelect) { + this.hideDropdown(true); + } + }; + Choices.prototype._onEscapeKey = function (event, hasActiveDropdown) { + if (hasActiveDropdown) { + event.stopPropagation(); + this.hideDropdown(true); + this.containerOuter.element.focus(); + } + }; + Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) { + var keyCode = event.keyCode, metaKey = event.metaKey; + // If up or down key is pressed, traverse through options + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === 40 /* KeyCodeMap.DOWN_KEY */ || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ ? 1 : -1; + var skipKey = metaKey || keyCode === 34 /* KeyCodeMap.PAGE_DOWN_KEY */ || keyCode === 33 /* KeyCodeMap.PAGE_UP_KEY */; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl = void 0; + if (skipKey) { + if (directionInt > 0) { + nextEl = this.dropdown.element.querySelector("".concat(selectableChoiceIdentifier, ":last-of-type")); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + else { + var currentEl = this.dropdown.element.querySelector(getClassNamesSelector(this.config.classNames.highlightedState)); + if (currentEl) { + nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); + } + else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChildElement(nextEl, directionInt); + } + this._highlightChoice(nextEl); + } + // Prevent default to maintain cursor position whilst + // traversing dropdown options + event.preventDefault(); + } + }; + Choices.prototype._onDeleteKey = function (event, items, hasFocusedInput) { + var target = event.target; + // If backspace or delete key is pressed and the input has no value + if (!this._isSelectOneElement && !target.value && hasFocusedInput) { + this._handleBackspace(items); + event.preventDefault(); + } + }; + Choices.prototype._onTouchMove = function () { + if (this._wasTap) { + this._wasTap = false; + } + }; + Choices.prototype._onTouchEnd = function (event) { + var target = (event || event.touches[0]).target; + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } + else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } + // Prevents focus event firing + event.stopPropagation(); + } + this._wasTap = true; + }; + /** + * Handles mousedown event in capture mode for containetOuter.element + */ + Choices.prototype._onMouseDown = function (event) { + var target = event.target; + if (!(target instanceof HTMLElement)) { + return; + } + // If we have our mouse down on the scrollbar and are on IE11... + if (IS_IE11 && this.choiceList.element.contains(target)) { + // check if click was on a scrollbar area + var firstChoice = this.choiceList.element.firstElementChild; + this._isScrollingOnIe = + this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; + } + if (target === this.input.element) { + return; + } + var item = target.closest('[data-button],[data-item],[data-choice]'); + if (item instanceof HTMLElement) { + var hasShiftKey = event.shiftKey; + var dataset = item.dataset; + if ('button' in dataset) { + this._handleButtonAction(item); + } + else if ('item' in dataset) { + this._handleItemAction(item, hasShiftKey); + } + else if ('choice' in dataset) { + this._handleChoiceAction(item); + } + } + event.preventDefault(); + }; + /** + * Handles mouseover event over this.dropdown + * @param {MouseEvent} event + */ + Choices.prototype._onMouseOver = function (_a) { + var target = _a.target; + if (target instanceof HTMLElement && 'choice' in target.dataset) { + this._highlightChoice(target); + } + }; + Choices.prototype._onClick = function (_a) { + var target = _a.target; + var containerOuter = this.containerOuter; + var clickWasWithinContainer = containerOuter.element.contains(target); + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } + else { + this.showDropdown(); + containerOuter.element.focus(); + } + } + else if (this._isSelectOneElement && + target !== this.input.element && + !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } + else { + var hasHighlightedItems = !!this._store.highlightedActiveItems.length; + if (hasHighlightedItems) { + this.unhighlightAll(); + } + containerOuter.removeFocusState(); + this.hideDropdown(true); + } + }; + Choices.prototype._onFocus = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var focusWasWithinContainer = target && containerOuter.element.contains(target); + if (!focusWasWithinContainer) { + return; + } + var targetIsInput = target === this.input.element; + var focusActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput) { + containerOuter.addFocusState(); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.addFocusState(); + if (targetIsInput) { + _this.showDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput) { + _this.showDropdown(true); + // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown + containerOuter.addFocusState(); + } + }, + _b); + focusActions[this._elementType](); + }; + Choices.prototype._onBlur = function (_a) { + var _b; + var _this = this; + var target = _a.target; + var containerOuter = this.containerOuter; + var blurWasWithinContainer = target && containerOuter.element.contains(target); + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeChoices = this._store.activeChoices; + var hasHighlightedItems_1 = activeChoices.some(function (item) { return item.highlighted; }); + var targetIsInput_1 = target === this.input.element; + var blurActions = (_b = {}, + _b[TEXT_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + _this.hideDropdown(true); + } + }, + _b[SELECT_ONE_TYPE] = function () { + containerOuter.removeFocusState(); + if (targetIsInput_1 || (target === containerOuter.element && !_this._canSearch)) { + _this.hideDropdown(true); + } + }, + _b[SELECT_MULTIPLE_TYPE] = function () { + if (targetIsInput_1) { + containerOuter.removeFocusState(); + _this.hideDropdown(true); + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + } + }, + _b); + blurActions[this._elementType](); + } + else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + }; + Choices.prototype._onFormReset = function () { + var _this = this; + this._store.withTxn(function () { + _this.clearInput(); + _this.hideDropdown(); + _this.refresh(false, false, true); + if (_this._initialItems.length) { + _this.setChoiceByValue(_this._initialItems); + } + }); + }; + Choices.prototype._highlightChoice = function (el) { + var _a; + if (el === void 0) { el = null; } + var highlightedState = this.config.classNames.highlightedState; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); + if (!choices.length) { + return; + } + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll(getClassNamesSelector(highlightedState))); + // Remove any highlighted choices + highlightedChoices.forEach(function (choice) { + var _a; + (_a = choice.classList).remove.apply(_a, getClassNames(highlightedState)); + choice.setAttribute('aria-selected', 'false'); + }); + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } + else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } + else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + if (!passedEl) { + passedEl = choices[0]; + } + } + (_a = passedEl.classList).add.apply(_a, getClassNames(highlightedState)); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent("highlightChoice" /* EventType.highlightChoice */, { + el: passedEl, + }); + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + }; + Choices.prototype._addItem = function (item, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + var id = item.id; + if (!id) { + throw new TypeError('item.id must be set before _addItem is called for a choice/item'); + } + if (this.config.singleModeForMultiSelect || this._isSelectOneElement) { + this.removeActiveItems(id); + } + this._store.dispatch(addItem(item)); + if (withEvents) { + this.passedElement.triggerEvent("addItem" /* EventType.addItem */, this._getChoiceForOutput(item)); + if (userTriggered) { + this.passedElement.triggerEvent("choice" /* EventType.choice */, this._getChoiceForOutput(item)); + } + } + }; + Choices.prototype._removeItem = function (item) { + var id = item.id; + if (!id) { + return; + } + this._store.dispatch(removeItem(item)); + this.passedElement.triggerEvent("removeItem" /* EventType.removeItem */, this._getChoiceForOutput(item)); + }; + Choices.prototype._addChoice = function (choice, withEvents, userTriggered) { + if (withEvents === void 0) { withEvents = true; } + if (userTriggered === void 0) { userTriggered = false; } + if (choice.id) { + throw new TypeError('Can not re-add a choice which has already been added'); + } + // Generate unique id, in-place update is required so chaining _addItem works as expected + this._lastAddedChoiceId++; + choice.id = this._lastAddedChoiceId; + choice.elementId = "".concat(this._baseId, "-").concat(this._idNames.itemChoice, "-").concat(choice.id); + var _a = this.config, prependValue = _a.prependValue, appendValue = _a.appendValue; + if (prependValue) { + choice.value = prependValue + choice.value; + } + if (appendValue) { + choice.value += appendValue.toString(); + } + if ((prependValue || appendValue) && choice.element) { + choice.element.value = choice.value; + } + this._store.dispatch(addChoice(choice)); + if (choice.selected) { + this._addItem(choice, withEvents, userTriggered); + } + }; + Choices.prototype._addGroup = function (group, withEvents) { + var _this = this; + if (withEvents === void 0) { withEvents = true; } + if (group.id) { + throw new TypeError('Can not re-add a group which has already been added'); + } + this._store.dispatch(addGroup(group)); + if (!group.choices) { + return; + } + // add unique id for the group(s), and do not store the full list of choices in this group + var g = group; + this._lastAddedGroupId++; + g.id = this._lastAddedGroupId; + var id = group.id, choices = group.choices; + g.choices = []; + choices.forEach(function (item) { + item.groupId = id; + if (group.disabled) { + item.disabled = true; + } + _this._addChoice(item, withEvents); + }); + }; + Choices.prototype._createTemplates = function () { + var _this = this; + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; + if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { + userTemplates = callbackOnCreateTemplates.call(this, strToEl, escapeForTemplate); + } + var templating = {}; + Object.keys(this._templates).forEach(function (name) { + if (name in userTemplates) { + templating[name] = userTemplates[name].bind(_this); + } + else { + templating[name] = _this._templates[name].bind(_this); + } + }); + this._templates = templating; + }; + Choices.prototype._createElements = function () { + var templating = this._templates; + var config = this.config; + var position = config.position, classNames = config.classNames; + var elementType = this._elementType; + this.containerOuter = new Container({ + element: templating.containerOuter(config, this._direction, this._isSelectElement, this._isSelectOneElement, config.searchEnabled, elementType, config.labelId), + classNames: classNames, + type: elementType, + position: position, + }); + this.containerInner = new Container({ + element: templating.containerInner(config), + classNames: classNames, + type: elementType, + position: position, + }); + this.input = new Input({ + element: templating.input(config, this._placeholderValue), + classNames: classNames, + type: elementType, + preventPaste: !config.paste, + }); + this.choiceList = new List({ + element: templating.choiceList(config, this._isSelectOneElement), + }); + this.itemList = new List({ + element: templating.itemList(config, this._isSelectOneElement), + }); + this.dropdown = new Dropdown({ + element: templating.dropdown(config), + classNames: classNames, + type: elementType, + }); + }; + Choices.prototype._createStructure = function () { + var _a = this, containerInner = _a.containerInner, containerOuter = _a.containerOuter, passedElement = _a.passedElement, dropdown = _a.dropdown, input = _a.input; + // Hide original element + passedElement.conceal(); + // Wrap input in container preserving DOM ordering + containerInner.wrap(passedElement.element); + // Wrapper inner container with outer container + containerOuter.wrap(containerInner.element); + if (this._isSelectOneElement) { + input.placeholder = this.config.searchPlaceholderValue || ''; + } + else { + if (this._placeholderValue) { + input.placeholder = this._placeholderValue; + } + input.setWidth(); + } + containerOuter.element.appendChild(containerInner.element); + containerOuter.element.appendChild(dropdown.element); + containerInner.element.appendChild(this.itemList.element); + dropdown.element.appendChild(this.choiceList.element); + if (!this._isSelectOneElement) { + containerInner.element.appendChild(input.element); + } + else if (this.config.searchEnabled) { + dropdown.element.insertBefore(input.element, dropdown.element.firstChild); + } + this._highlightPosition = 0; + this._isSearching = false; + }; + Choices.prototype._initStore = function () { + var _this = this; + this._store.subscribe(this._render); + this._store.withTxn(function () { + _this._addPredefinedChoices(_this._presetChoices, _this._isSelectOneElement && !_this._hasNonChoicePlaceholder, false); + }); + if (this._isSelectOneElement && this._hasNonChoicePlaceholder) { + this._render({ choices: false, groups: false, items: true }); + } + }; + Choices.prototype._addPredefinedChoices = function (choices, selectFirstOption, withEvents) { + var _this = this; + if (selectFirstOption === void 0) { selectFirstOption = false; } + if (withEvents === void 0) { withEvents = true; } + if (selectFirstOption) { + /** + * If there is a selected choice already or the choice is not the first in + * the array, add each choice normally. + * + * Otherwise we pre-select the first enabled choice in the array ("select-one" only) + */ + var noSelectedChoices = choices.findIndex(function (choice) { return choice.selected; }) === -1; + if (noSelectedChoices) { + choices.some(function (choice) { + if (choice.disabled || 'choices' in choice) { + return false; + } + choice.selected = true; + return true; + }); + } + } + choices.forEach(function (item) { + if ('choices' in item) { + if (_this._isSelectElement) { + _this._addGroup(item, withEvents); + } + } + else { + _this._addChoice(item, withEvents); + } + }); + }; + Choices.prototype._findAndSelectChoiceByValue = function (value, userTriggered) { + var _this = this; + if (userTriggered === void 0) { userTriggered = false; } + var choices = this._store.choices; + // Check 'value' property exists and the choice isn't already selected + var foundChoice = choices.find(function (choice) { return _this.config.valueComparer(choice.value, value); }); + if (foundChoice && !foundChoice.disabled && !foundChoice.selected) { + this._addItem(foundChoice, true, userTriggered); + return true; + } + return false; + }; + Choices.prototype._generatePlaceholderValue = function () { + var config = this.config; + if (!config.placeholder) { + return null; + } + if (this._hasNonChoicePlaceholder) { + return config.placeholderValue; + } + if (this._isSelectElement) { + var placeholderOption = this.passedElement.placeholderOption; + return placeholderOption ? placeholderOption.text : null; + } + return null; + }; + Choices.prototype._warnChoicesInitFailed = function (caller) { + if (this.config.silent) { + return; + } + if (!this.initialised) { + throw new TypeError("".concat(caller, " called on a non-initialised instance of Choices")); + } + else if (!this.initialisedOK) { + throw new TypeError("".concat(caller, " called for an element which has multiple instances of Choices initialised on it")); + } + }; + Choices.version = '11.0.0-rc7'; + return Choices; +}()); + +export { Choices as default }; diff --git a/public/assets/styles/base.css b/public/assets/styles/base.css index b64f6b953..4672d7ec7 100644 --- a/public/assets/styles/base.css +++ b/public/assets/styles/base.css @@ -78,8 +78,7 @@ a:focus { border: 1px solid #ddd; border-radius: 2.5px; font-size: 14px; - -webkit-appearance: none; - appearance: none; + appearance: none; margin-bottom: 24px; } @@ -137,7 +136,7 @@ label + p { .section a, .section a:visited, .section a:focus { - color: #00bcd4; + color: #005F75; } .logo { diff --git a/public/assets/styles/base.min.css b/public/assets/styles/base.min.css index 53c15adb5..ebba2030c 100644 --- a/public/assets/styles/base.min.css +++ b/public/assets/styles/base.min.css @@ -1 +1 @@ -*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::after,::before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label,p{margin-bottom:8px}label{font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:30px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;-webkit-appearance:none;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}label+p{margin-top:-4px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#00bcd4}.logo{display:block;margin-bottom:12px}.logo-img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.push-bottom{margin-bottom:24px}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}.text-center{text-align:center}[data-test-hook]{margin-bottom:24px} \ No newline at end of file +*{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}*,::after,::before{box-sizing:border-box}body,html{position:relative;margin:0;width:100%;height:100%}body{font-family:"Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;font-size:16px;line-height:1.4;color:#fff;background-color:#333;overflow-x:hidden}hr,label{display:block}label,p{margin-bottom:8px}label{font-size:14px;font-weight:500;cursor:pointer}p{margin-top:0}hr{margin:30px 0;border:0;border-bottom:1px solid #eaeaea;height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:12px;font-weight:400;line-height:1.2}a,a:focus,a:visited{color:#fff;text-decoration:none;font-weight:600}.form-control{display:block;width:100%;background-color:#f9f9f9;padding:12px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;appearance:none;margin-bottom:24px}.h1,h1{font-size:32px}.h2,h2{font-size:24px}.h3,h3{font-size:20px}.h4,h4{font-size:18px}.h5,h5{font-size:16px}.h6,h6{font-size:14px}label+p{margin-top:-4px}.container{display:block;margin:auto;max-width:40em;padding:48px}@media (max-width:620px){.container{padding:0}}.section{background-color:#fff;padding:24px;color:#333}.section a,.section a:focus,.section a:visited{color:#005f75}.logo{display:block;margin-bottom:12px}.logo-img{width:100%;height:auto;display:inline-block;max-width:100%;vertical-align:top;padding:6px 0}.visible-ie{display:none}.push-bottom{margin-bottom:24px}.zero-bottom{margin-bottom:0}.zero-top{margin-top:0}.text-center{text-align:center}[data-test-hook]{margin-bottom:24px} \ No newline at end of file diff --git a/public/assets/styles/choices.css b/public/assets/styles/choices.css index ed8ff340e..8c0346f80 100644 --- a/public/assets/styles/choices.css +++ b/public/assets/styles/choices.css @@ -62,9 +62,9 @@ opacity: 1; } .choices[data-type*=select-one] .choices__button:focus { - box-shadow: 0 0 0 2px #00bcd4; + box-shadow: 0 0 0 2px #005F75; } -.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button { +.choices[data-type*=select-one] .choices__item[data-placeholder] .choices__button { display: none; } .choices[data-type*=select-one]::after { @@ -81,7 +81,7 @@ pointer-events: none; } .choices[data-type*=select-one].is-open::after { - border-color: transparent transparent #333 transparent; + border-color: transparent transparent #333; margin-top: -7.5px; } .choices[data-type*=select-one][dir=rtl]::after { @@ -108,7 +108,7 @@ margin-bottom: 0; margin-left: 8px; padding-left: 16px; - border-left: 1px solid #008fa1; + border-left: 1px solid #003642; background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg=="); background-size: 8px; width: 8px; @@ -174,8 +174,8 @@ font-weight: 500; margin-right: 3.75px; margin-bottom: 3.75px; - background-color: #00bcd4; - border: 1px solid #00a5bb; + background-color: #005F75; + border: 1px solid #004a5c; color: #fff; word-break: break-all; box-sizing: border-box; @@ -188,8 +188,8 @@ margin-left: 3.75px; } .choices__list--multiple .choices__item.is-highlighted { - background-color: #00a5bb; - border: 1px solid #008fa1; + background-color: #004a5c; + border: 1px solid #003642; } .is-disabled .choices__list--multiple .choices__item { background-color: #aaaaaa; @@ -294,8 +294,7 @@ .choices__button { text-indent: -9999px; - -webkit-appearance: none; - appearance: none; + appearance: none; border: 0; background-color: transparent; background-repeat: no-repeat; diff --git a/public/assets/styles/choices.min.css b/public/assets/styles/choices.min.css index 9260536aa..b893b3cf5 100644 --- a/public/assets/styles/choices.min.css +++ b/public/assets/styles/choices.min.css @@ -1 +1 @@ -.choices{position:relative;overflow:hidden;margin-bottom:24px;font-size:16px}.choices:focus{outline:0}.choices:last-child{margin-bottom:0}.choices.is-open{overflow:visible}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices [hidden]{display:none!important}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.25}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #00bcd4}.choices[data-type*=select-one] .choices__item[data-value=""] .choices__button{display:none}.choices[data-type*=select-one]::after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open::after{border-color:transparent transparent #333;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]::after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0-4px 0 8px;padding-left:16px;border-left:1px solid #008fa1;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#00bcd4;border:1px solid #00a5bb;color:#fff;word-break:break-all;box-sizing:border-box}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#00a5bb;border:1px solid #008fa1}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown,.choices__list[aria-expanded]{visibility:hidden;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all;will-change:visibility}.is-active.choices__list--dropdown,.is-active.choices__list[aria-expanded]{visibility:visible}.is-open .choices__list--dropdown,.is-open .choices__list[aria-expanded]{border-color:#b7b7b7}.is-flipped .choices__list--dropdown,.is-flipped .choices__list[aria-expanded]{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list,.choices__list[aria-expanded] .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item,.choices__list[aria-expanded] .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item,[dir=rtl] .choices__list[aria-expanded] .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable,.choices__list[aria-expanded] .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable::after,.choices__list[aria-expanded] .choices__item--selectable::after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable::after,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted::after,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;-webkit-appearance:none;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus,.choices__input:focus{outline:0}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input::-webkit-search-cancel-button,.choices__input::-webkit-search-decoration,.choices__input::-webkit-search-results-button,.choices__input::-webkit-search-results-decoration{display:none}.choices__input::-ms-clear,.choices__input::-ms-reveal{display:none;width:0;height:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5} \ No newline at end of file +.choices{position:relative;overflow:hidden;margin-bottom:24px;font-size:16px}.choices:focus{outline:0}.choices:last-child{margin-bottom:0}.choices.is-open{overflow:visible}.choices.is-disabled .choices__inner,.choices.is-disabled .choices__input{background-color:#eaeaea;cursor:not-allowed;-webkit-user-select:none;user-select:none}.choices.is-disabled .choices__item{cursor:not-allowed}.choices [hidden]{display:none!important}.choices[data-type*=select-one]{cursor:pointer}.choices[data-type*=select-one] .choices__inner{padding-bottom:7.5px}.choices[data-type*=select-one] .choices__input{display:block;width:100%;padding:10px;border-bottom:1px solid #ddd;background-color:#fff;margin:0}.choices[data-type*=select-one] .choices__button{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);padding:0;background-size:8px;position:absolute;top:50%;right:0;margin-top:-10px;margin-right:25px;height:20px;width:20px;border-radius:10em;opacity:.25}.choices[data-type*=select-one] .choices__button:focus,.choices[data-type*=select-one] .choices__button:hover{opacity:1}.choices[data-type*=select-one] .choices__button:focus{box-shadow:0 0 0 2px #005f75}.choices[data-type*=select-one] .choices__item[data-placeholder] .choices__button{display:none}.choices[data-type*=select-one]::after{content:"";height:0;width:0;border-style:solid;border-color:#333 transparent transparent;border-width:5px;position:absolute;right:11.5px;top:50%;margin-top:-2.5px;pointer-events:none}.choices[data-type*=select-one].is-open::after{border-color:transparent transparent #333;margin-top:-7.5px}.choices[data-type*=select-one][dir=rtl]::after{left:11.5px;right:auto}.choices[data-type*=select-one][dir=rtl] .choices__button{right:auto;left:0;margin-left:25px;margin-right:0}.choices[data-type*=select-multiple] .choices__inner,.choices[data-type*=text] .choices__inner{cursor:text}.choices[data-type*=select-multiple] .choices__button,.choices[data-type*=text] .choices__button{position:relative;display:inline-block;margin:0-4px 0 8px;padding-left:16px;border-left:1px solid #003642;background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjEiIHZpZXdCb3g9IjAgMCAyMSAyMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjRkZGIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik0yLjU5Mi4wNDRsMTguMzY0IDE4LjM2NC0yLjU0OCAyLjU0OEwuMDQ0IDIuNTkyeiIvPjxwYXRoIGQ9Ik0wIDE4LjM2NEwxOC4zNjQgMGwyLjU0OCAyLjU0OEwyLjU0OCAyMC45MTJ6Ii8+PC9nPjwvc3ZnPg==);background-size:8px;width:8px;line-height:1;opacity:.75;border-radius:0}.choices[data-type*=select-multiple] .choices__button:focus,.choices[data-type*=select-multiple] .choices__button:hover,.choices[data-type*=text] .choices__button:focus,.choices[data-type*=text] .choices__button:hover{opacity:1}.choices__inner{display:inline-block;vertical-align:top;width:100%;background-color:#f9f9f9;padding:7.5px 7.5px 3.75px;border:1px solid #ddd;border-radius:2.5px;font-size:14px;min-height:44px;overflow:hidden}.is-focused .choices__inner,.is-open .choices__inner{border-color:#b7b7b7}.is-open .choices__inner{border-radius:2.5px 2.5px 0 0}.is-flipped.is-open .choices__inner{border-radius:0 0 2.5px 2.5px}.choices__list{margin:0;padding-left:0;list-style:none}.choices__list--single{display:inline-block;padding:4px 16px 4px 4px;width:100%}[dir=rtl] .choices__list--single{padding-right:4px;padding-left:16px}.choices__list--single .choices__item{width:100%}.choices__list--multiple{display:inline}.choices__list--multiple .choices__item{display:inline-block;vertical-align:middle;border-radius:20px;padding:4px 10px;font-size:12px;font-weight:500;margin-right:3.75px;margin-bottom:3.75px;background-color:#005f75;border:1px solid #004a5c;color:#fff;word-break:break-all;box-sizing:border-box}.choices__list--multiple .choices__item[data-deletable]{padding-right:5px}[dir=rtl] .choices__list--multiple .choices__item{margin-right:0;margin-left:3.75px}.choices__list--multiple .choices__item.is-highlighted{background-color:#004a5c;border:1px solid #003642}.is-disabled .choices__list--multiple .choices__item{background-color:#aaa;border:1px solid #919191}.choices__list--dropdown,.choices__list[aria-expanded]{visibility:hidden;z-index:1;position:absolute;width:100%;background-color:#fff;border:1px solid #ddd;top:100%;margin-top:-1px;border-bottom-left-radius:2.5px;border-bottom-right-radius:2.5px;overflow:hidden;word-break:break-all;will-change:visibility}.is-active.choices__list--dropdown,.is-active.choices__list[aria-expanded]{visibility:visible}.is-open .choices__list--dropdown,.is-open .choices__list[aria-expanded]{border-color:#b7b7b7}.is-flipped .choices__list--dropdown,.is-flipped .choices__list[aria-expanded]{top:auto;bottom:100%;margin-top:0;margin-bottom:-1px;border-radius:.25rem .25rem 0 0}.choices__list--dropdown .choices__list,.choices__list[aria-expanded] .choices__list{position:relative;max-height:300px;overflow:auto;-webkit-overflow-scrolling:touch;will-change:scroll-position}.choices__list--dropdown .choices__item,.choices__list[aria-expanded] .choices__item{position:relative;padding:10px;font-size:14px}[dir=rtl] .choices__list--dropdown .choices__item,[dir=rtl] .choices__list[aria-expanded] .choices__item{text-align:right}@media (min-width:640px){.choices__list--dropdown .choices__item--selectable,.choices__list[aria-expanded] .choices__item--selectable{padding-right:100px}.choices__list--dropdown .choices__item--selectable::after,.choices__list[aria-expanded] .choices__item--selectable::after{content:attr(data-select-text);font-size:12px;opacity:0;position:absolute;right:10px;top:50%;transform:translateY(-50%)}[dir=rtl] .choices__list--dropdown .choices__item--selectable,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable{text-align:right;padding-left:100px;padding-right:10px}[dir=rtl] .choices__list--dropdown .choices__item--selectable::after,[dir=rtl] .choices__list[aria-expanded] .choices__item--selectable::after{right:auto;left:10px}}.choices__list--dropdown .choices__item--selectable.is-highlighted,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted{background-color:#f2f2f2}.choices__list--dropdown .choices__item--selectable.is-highlighted::after,.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after{opacity:.5}.choices__item{cursor:default}.choices__item--selectable{cursor:pointer}.choices__item--disabled{cursor:not-allowed;-webkit-user-select:none;user-select:none;opacity:.5}.choices__heading{font-weight:600;font-size:12px;padding:10px;border-bottom:1px solid #f7f7f7;color:gray}.choices__button{text-indent:-9999px;appearance:none;border:0;background-color:transparent;background-repeat:no-repeat;background-position:center;cursor:pointer}.choices__button:focus,.choices__input:focus{outline:0}.choices__input{display:inline-block;vertical-align:baseline;background-color:#f9f9f9;font-size:14px;margin-bottom:5px;border:0;border-radius:0;max-width:100%;padding:4px 0 4px 2px}.choices__input::-webkit-search-cancel-button,.choices__input::-webkit-search-decoration,.choices__input::-webkit-search-results-button,.choices__input::-webkit-search-results-decoration{display:none}.choices__input::-ms-clear,.choices__input::-ms-reveal{display:none;width:0;height:0}[dir=rtl] .choices__input{padding-right:2px;padding-left:0}.choices__placeholder{opacity:.5} \ No newline at end of file diff --git a/public/index.html b/public/index.html index 9ebf19fed..b6c7cb092 100644 --- a/public/index.html +++ b/public/index.html @@ -4,7 +4,7 @@ Choices Single select input +
+

Below is an example of how you could have two select inputs depend on @@ -520,6 +522,7 @@

Single select input

Form interaction

Change the values and press reset to restore to initial state.

+
+
+
@@ -81,11 +99,20 @@

Select multiple inputs

id="choices-remove-button" multiple > - + + +
@@ -101,20 +128,76 @@

Select multiple inputs

+ +
+ +
+ + +
-
- +
+
+ +
+
@@ -130,6 +213,13 @@

Select multiple inputs

+
@@ -147,6 +237,14 @@

Select multiple inputs

+
@@ -161,6 +259,15 @@

Select multiple inputs

+
@@ -175,6 +282,14 @@

Select multiple inputs

+
@@ -189,6 +304,14 @@

Select multiple inputs

+
@@ -206,6 +329,13 @@

Select multiple inputs

+
@@ -223,6 +353,38 @@

Select multiple inputs

+ +
+ +
+ + +
@@ -235,6 +397,22 @@

Select multiple inputs

> +
@@ -261,6 +439,14 @@

Select multiple inputs

+
@@ -282,6 +468,38 @@

Select multiple inputs

+ +
+ +
+ + +
@@ -292,6 +510,40 @@

Select multiple inputs

id="choices-custom-properties" multiple > +
@@ -314,6 +566,14 @@

Select multiple inputs

>Label Four +
@@ -323,6 +583,37 @@

Select multiple inputs

name="choices-non-string-values" id="choices-non-string-values" > +
@@ -337,6 +628,14 @@

Select multiple inputs

+ +
@@ -353,6 +652,13 @@

Select multiple inputs

+
@@ -366,263 +672,234 @@

Select multiple inputs

+
- + +
- + + +
+ +
+ + +
- + + + +
+ +
+ + + + + +
+ +
+ +
+
- diff --git a/public/test/select-one/index.html b/public/test/select-one/index.html index c0aea2d2e..345ff283b 100644 --- a/public/test/select-one/index.html +++ b/public/test/select-one/index.html @@ -4,7 +4,7 @@ Choices - + @@ -66,6 +66,24 @@

Select one inputs

+
@@ -80,6 +98,15 @@

Select one inputs

+ +
@@ -94,6 +121,14 @@

Select one inputs

+
@@ -106,19 +141,55 @@

Select one inputs

id="choices-disabled-choice-via-options" > +
-
- +
+
+ +
+
@@ -133,6 +204,13 @@

Select one inputs

+
@@ -146,6 +224,15 @@

Select one inputs

+
@@ -159,6 +246,14 @@

Select one inputs

+
@@ -172,6 +267,14 @@

Select one inputs

+
@@ -185,6 +288,14 @@

Select one inputs

+
@@ -201,6 +312,13 @@

Select one inputs

+
@@ -217,6 +335,37 @@

Select one inputs

+ +
+ +
+ + +
@@ -228,6 +377,22 @@

Select one inputs

> +
@@ -253,6 +418,14 @@

Select one inputs

+
@@ -261,7 +434,6 @@

Select one inputs

class="form-control" name="choices-groups" id="choices-groups" - multiple > @@ -274,6 +446,37 @@

Select one inputs

+ +
+ +
+ + +
@@ -294,6 +497,24 @@

Select one inputs

+
@@ -303,6 +524,40 @@

Select one inputs

name="choices-custom-properties" id="choices-custom-properties" > +
@@ -325,6 +580,14 @@

Select one inputs

>Label Four +
@@ -334,6 +597,37 @@

Select one inputs

name="choices-non-string-values" id="choices-non-string-values" > +
@@ -347,6 +641,13 @@

Select one inputs

+
@@ -362,6 +663,13 @@

Select one inputs

+
@@ -374,33 +682,133 @@

Select one inputs

+
- + +
- + + +
+ +
+ + +
- + +
@@ -416,267 +824,57 @@

Select one inputs

+ +
+ +
+ +
+
- diff --git a/public/test/text/index.html b/public/test/text/index.html index 87d8f825f..89687aae4 100644 --- a/public/test/text/index.html +++ b/public/test/text/index.html @@ -4,7 +4,7 @@ Choices - + @@ -59,41 +59,127 @@

Text inputs

+
+
+
+
- + +
- + + +
+ +
+ + +
- + +
+
@@ -103,6 +189,19 @@

Text inputs

id="choices-add-item-filter" type="text" /> +
@@ -112,6 +211,32 @@

Text inputs

id="choices-adding-items-disabled" type="text" /> + +
+ +
+ +
+ +
+
@@ -122,21 +247,63 @@

Text inputs

type="text" disabled /> +
+
+
+
@@ -144,104 +311,65 @@

Text inputs

+ +
+ +
+ + + + +
+ +
+ +
+ +
+ - diff --git a/public/types/cypress/e2e/select-multiple.spec.d.ts b/public/types/cypress/e2e/select-multiple.spec.d.ts deleted file mode 100644 index 6c3e952d4..000000000 --- a/public/types/cypress/e2e/select-multiple.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=select-multiple.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/e2e/select-multiple.spec.d.ts.map b/public/types/cypress/e2e/select-multiple.spec.d.ts.map deleted file mode 100644 index fd77f545a..000000000 --- a/public/types/cypress/e2e/select-multiple.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"select-multiple.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/select-multiple.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/e2e/select-one.spec.d.ts b/public/types/cypress/e2e/select-one.spec.d.ts deleted file mode 100644 index 9de7a41d0..000000000 --- a/public/types/cypress/e2e/select-one.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=select-one.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/e2e/select-one.spec.d.ts.map b/public/types/cypress/e2e/select-one.spec.d.ts.map deleted file mode 100644 index cb7b6e202..000000000 --- a/public/types/cypress/e2e/select-one.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"select-one.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/select-one.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/e2e/text.spec.d.ts b/public/types/cypress/e2e/text.spec.d.ts deleted file mode 100644 index cfb9d2df9..000000000 --- a/public/types/cypress/e2e/text.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=text.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/e2e/text.spec.d.ts.map b/public/types/cypress/e2e/text.spec.d.ts.map deleted file mode 100644 index 94ccbad20..000000000 --- a/public/types/cypress/e2e/text.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"text.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/e2e/text.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/integration/select-multiple.spec.d.ts b/public/types/cypress/integration/select-multiple.spec.d.ts deleted file mode 100644 index 6c3e952d4..000000000 --- a/public/types/cypress/integration/select-multiple.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=select-multiple.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/integration/select-multiple.spec.d.ts.map b/public/types/cypress/integration/select-multiple.spec.d.ts.map deleted file mode 100644 index b1fdcc1f5..000000000 --- a/public/types/cypress/integration/select-multiple.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"select-multiple.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/select-multiple.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/integration/select-one.spec.d.ts b/public/types/cypress/integration/select-one.spec.d.ts deleted file mode 100644 index 9de7a41d0..000000000 --- a/public/types/cypress/integration/select-one.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=select-one.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/integration/select-one.spec.d.ts.map b/public/types/cypress/integration/select-one.spec.d.ts.map deleted file mode 100644 index d2a6b77bd..000000000 --- a/public/types/cypress/integration/select-one.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"select-one.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/select-one.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/integration/text.spec.d.ts b/public/types/cypress/integration/text.spec.d.ts deleted file mode 100644 index cfb9d2df9..000000000 --- a/public/types/cypress/integration/text.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=text.spec.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/integration/text.spec.d.ts.map b/public/types/cypress/integration/text.spec.d.ts.map deleted file mode 100644 index cb8c740b2..000000000 --- a/public/types/cypress/integration/text.spec.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"text.spec.d.ts","sourceRoot":"","sources":["../../../../cypress/integration/text.spec.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/plugins/index.d.ts b/public/types/cypress/plugins/index.d.ts deleted file mode 100644 index 2e1bed6c4..000000000 --- a/public/types/cypress/plugins/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare function _exports(on: any, config: any): void; -export = _exports; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/plugins/index.d.ts.map b/public/types/cypress/plugins/index.d.ts.map deleted file mode 100644 index 51c72de17..000000000 --- a/public/types/cypress/plugins/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../cypress/plugins/index.js"],"names":[],"mappings":"AAaiB,sDAGhB"} \ No newline at end of file diff --git a/public/types/cypress/support/commands.d.ts b/public/types/cypress/support/commands.d.ts deleted file mode 100644 index b7ad2b326..000000000 --- a/public/types/cypress/support/commands.d.ts +++ /dev/null @@ -1 +0,0 @@ -//# sourceMappingURL=commands.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/support/commands.d.ts.map b/public/types/cypress/support/commands.d.ts.map deleted file mode 100644 index b4cb53248..000000000 --- a/public/types/cypress/support/commands.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../cypress/support/commands.js"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/support/e2e.d.ts b/public/types/cypress/support/e2e.d.ts deleted file mode 100644 index aafedb01d..000000000 --- a/public/types/cypress/support/e2e.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=e2e.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/support/e2e.d.ts.map b/public/types/cypress/support/e2e.d.ts.map deleted file mode 100644 index 9d3830302..000000000 --- a/public/types/cypress/support/e2e.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"e2e.d.ts","sourceRoot":"","sources":["../../../../cypress/support/e2e.js"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/cypress/support/index.d.ts b/public/types/cypress/support/index.d.ts deleted file mode 100644 index e26a57a8c..000000000 --- a/public/types/cypress/support/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/cypress/support/index.d.ts.map b/public/types/cypress/support/index.d.ts.map deleted file mode 100644 index 02df40286..000000000 --- a/public/types/cypress/support/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../cypress/support/index.js"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/index.d.ts b/public/types/src/index.d.ts index dee565e39..8a789802f 100644 --- a/public/types/src/index.d.ts +++ b/public/types/src/index.d.ts @@ -4,4 +4,3 @@ export * from './scripts/constants'; export * from './scripts/defaults'; export { default as templates } from './scripts/templates'; export default Choices; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/src/index.d.ts.map b/public/types/src/index.d.ts.map deleted file mode 100644 index c7f1a9237..000000000 --- a/public/types/src/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAExC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE3D,eAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/actions/choices.d.ts b/public/types/src/scripts/actions/choices.d.ts index 61a1b30e5..45f6458a1 100644 --- a/public/types/src/scripts/actions/choices.d.ts +++ b/public/types/src/scripts/actions/choices.d.ts @@ -1,44 +1,24 @@ -import { ACTION_TYPES } from '../constants'; -import { Choice } from '../interfaces/choice'; -export interface AddChoiceAction { - type: typeof ACTION_TYPES.ADD_CHOICE; - id: number; - value: string; - label: string; - groupId: number; - disabled: boolean; - elementId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; +import { ChoiceFull } from '../interfaces/choice-full'; +import { ActionType } from '../interfaces'; +import { SearchResult } from '../interfaces/search'; +import { AnyAction } from '../interfaces/store'; +export type ChoiceActions = AddChoiceAction | RemoveChoiceAction | FilterChoicesAction | ActivateChoicesAction | ClearChoicesAction; +export interface AddChoiceAction extends AnyAction { + choice: ChoiceFull; } -export interface Result { - item: T; - score: number; +export interface RemoveChoiceAction extends AnyAction { + choice: ChoiceFull; } -export interface FilterChoicesAction { - type: typeof ACTION_TYPES.FILTER_CHOICES; - results: Result[]; +export interface FilterChoicesAction extends AnyAction { + results: SearchResult[]; } -export interface ActivateChoicesAction { - type: typeof ACTION_TYPES.ACTIVATE_CHOICES; +export interface ActivateChoicesAction extends AnyAction { active: boolean; } -export interface ClearChoicesAction { - type: typeof ACTION_TYPES.CLEAR_CHOICES; +export interface ClearChoicesAction extends AnyAction { } -export declare const addChoice: ({ value, label, id, groupId, disabled, elementId, customProperties, placeholder, keyCode, }: { - value: any; - label: any; - id: any; - groupId: any; - disabled: any; - elementId: any; - customProperties: any; - placeholder: any; - keyCode: any; -}) => AddChoiceAction; -export declare const filterChoices: (results: Result[]) => FilterChoicesAction; +export declare const addChoice: (choice: ChoiceFull) => AddChoiceAction; +export declare const removeChoice: (choice: ChoiceFull) => RemoveChoiceAction; +export declare const filterChoices: (results: SearchResult[]) => FilterChoicesAction; export declare const activateChoices: (active?: boolean) => ActivateChoicesAction; export declare const clearChoices: () => ClearChoicesAction; -//# sourceMappingURL=choices.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/choices.d.ts.map b/public/types/src/scripts/actions/choices.d.ts.map deleted file mode 100644 index 2e69e60c7..000000000 --- a/public/types/src/scripts/actions/choices.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,YAAY,CAAC,UAAU,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,OAAO,YAAY,CAAC,gBAAgB,CAAC;IAC3C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,YAAY,CAAC,aAAa,CAAC;CACzC;AAED,eAAO,MAAM,SAAS;;;;;;;;;;MAUlB,eAWF,CAAC;AAEH,eAAO,MAAM,aAAa,YACf,OAAO,MAAM,CAAC,EAAE,KACxB,mBAGD,CAAC;AAEH,eAAO,MAAM,eAAe,wBAAoB,qBAG9C,CAAC;AAEH,eAAO,MAAM,YAAY,QAAO,kBAE9B,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/actions/choices.test.d.ts b/public/types/src/scripts/actions/choices.test.d.ts deleted file mode 100644 index 1b34de17b..000000000 --- a/public/types/src/scripts/actions/choices.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=choices.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/choices.test.d.ts.map b/public/types/src/scripts/actions/choices.test.d.ts.map deleted file mode 100644 index 0f1c3a577..000000000 --- a/public/types/src/scripts/actions/choices.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/choices.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/actions/groups.d.ts b/public/types/src/scripts/actions/groups.d.ts index ad7a8b7c7..4f96b6d7d 100644 --- a/public/types/src/scripts/actions/groups.d.ts +++ b/public/types/src/scripts/actions/groups.d.ts @@ -1,15 +1,8 @@ -import { ACTION_TYPES } from '../constants'; -export interface AddGroupAction { - type: typeof ACTION_TYPES.ADD_GROUP; - id: number; - value: string; - active: boolean; - disabled: boolean; +import { GroupFull } from '../interfaces/group-full'; +import { ActionType } from '../interfaces'; +import { AnyAction } from '../interfaces/store'; +export type GroupActions = AddGroupAction; +export interface AddGroupAction extends AnyAction { + group: GroupFull; } -export declare const addGroup: ({ value, id, active, disabled, }: { - id: number; - value: string; - active: boolean; - disabled: boolean; -}) => AddGroupAction; -//# sourceMappingURL=groups.d.ts.map \ No newline at end of file +export declare const addGroup: (group: GroupFull) => AddGroupAction; diff --git a/public/types/src/scripts/actions/groups.d.ts.map b/public/types/src/scripts/actions/groups.d.ts.map deleted file mode 100644 index 4fd751608..000000000 --- a/public/types/src/scripts/actions/groups.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,YAAY,CAAC,SAAS,CAAC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,QAAQ;QAMf,MAAM;WACH,MAAM;YACL,OAAO;cACL,OAAO;MACf,cAMF,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/actions/groups.test.d.ts b/public/types/src/scripts/actions/groups.test.d.ts deleted file mode 100644 index 8d44d56ef..000000000 --- a/public/types/src/scripts/actions/groups.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=groups.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/groups.test.d.ts.map b/public/types/src/scripts/actions/groups.test.d.ts.map deleted file mode 100644 index 80ca388ff..000000000 --- a/public/types/src/scripts/actions/groups.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"groups.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/groups.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/actions/items.d.ts b/public/types/src/scripts/actions/items.d.ts index 96b0b2a8b..d4af0b89f 100644 --- a/public/types/src/scripts/actions/items.d.ts +++ b/public/types/src/scripts/actions/items.d.ts @@ -1,35 +1,17 @@ -import { ACTION_TYPES } from '../constants'; -export interface AddItemAction { - type: typeof ACTION_TYPES.ADD_ITEM; - id: number; - value: string; - label: string; - choiceId: number; - groupId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; +import { ChoiceFull } from '../interfaces/choice-full'; +import { ActionType } from '../interfaces'; +import { AnyAction } from '../interfaces/store'; +export type ItemActions = AddItemAction | RemoveItemAction | HighlightItemAction; +export interface AddItemAction extends AnyAction { + item: ChoiceFull; } -export interface RemoveItemAction { - type: typeof ACTION_TYPES.REMOVE_ITEM; - id: number; - choiceId: number; +export interface RemoveItemAction extends AnyAction { + item: ChoiceFull; } -export interface HighlightItemAction { - type: typeof ACTION_TYPES.HIGHLIGHT_ITEM; - id: number; +export interface HighlightItemAction extends AnyAction { + item: ChoiceFull; highlighted: boolean; } -export declare const addItem: ({ value, label, id, choiceId, groupId, customProperties, placeholder, keyCode, }: { - id: number; - value: string; - label: string; - choiceId: number; - groupId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; -}) => AddItemAction; -export declare const removeItem: (id: number, choiceId: number) => RemoveItemAction; -export declare const highlightItem: (id: number, highlighted: boolean) => HighlightItemAction; -//# sourceMappingURL=items.d.ts.map \ No newline at end of file +export declare const addItem: (item: ChoiceFull) => AddItemAction; +export declare const removeItem: (item: ChoiceFull) => RemoveItemAction; +export declare const highlightItem: (item: ChoiceFull, highlighted: boolean) => HighlightItemAction; diff --git a/public/types/src/scripts/actions/items.d.ts.map b/public/types/src/scripts/actions/items.d.ts.map deleted file mode 100644 index 2ad259099..000000000 --- a/public/types/src/scripts/actions/items.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,YAAY,CAAC,QAAQ,CAAC;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,YAAY,CAAC,WAAW,CAAC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,OAAO;QAUd,MAAM;WACH,MAAM;WACN,MAAM;cACH,MAAM;aACP,MAAM;sBACG,MAAM;iBACX,OAAO;aACX,MAAM;MACb,aAUF,CAAC;AAEH,eAAO,MAAM,UAAU,OAAQ,MAAM,YAAY,MAAM,KAAG,gBAIxD,CAAC;AAEH,eAAO,MAAM,aAAa,OACpB,MAAM,eACG,OAAO,KACnB,mBAID,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/actions/items.test.d.ts b/public/types/src/scripts/actions/items.test.d.ts deleted file mode 100644 index e81d58b44..000000000 --- a/public/types/src/scripts/actions/items.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=items.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/items.test.d.ts.map b/public/types/src/scripts/actions/items.test.d.ts.map deleted file mode 100644 index 77773c21a..000000000 --- a/public/types/src/scripts/actions/items.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"items.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/items.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/actions/misc.d.ts b/public/types/src/scripts/actions/misc.d.ts deleted file mode 100644 index 65e1faf1c..000000000 --- a/public/types/src/scripts/actions/misc.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ACTION_TYPES } from '../constants'; -import { State } from '../interfaces/state'; -export interface ClearAllAction { - type: typeof ACTION_TYPES.CLEAR_ALL; -} -export interface ResetToAction { - type: typeof ACTION_TYPES.RESET_TO; - state: State; -} -export interface SetIsLoadingAction { - type: typeof ACTION_TYPES.SET_IS_LOADING; - isLoading: boolean; -} -export declare const clearAll: () => ClearAllAction; -export declare const resetTo: (state: State) => ResetToAction; -export declare const setIsLoading: (isLoading: boolean) => SetIsLoadingAction; -//# sourceMappingURL=misc.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/misc.d.ts.map b/public/types/src/scripts/actions/misc.d.ts.map deleted file mode 100644 index 0e77eb24b..000000000 --- a/public/types/src/scripts/actions/misc.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/misc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,YAAY,CAAC,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,YAAY,CAAC,QAAQ,CAAC;IACnC,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,YAAY,CAAC,cAAc,CAAC;IACzC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,QAAQ,QAAO,cAE1B,CAAC;AAEH,eAAO,MAAM,OAAO,UAAW,KAAK,KAAG,aAGrC,CAAC;AAEH,eAAO,MAAM,YAAY,cAAe,OAAO,KAAG,kBAGhD,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/actions/misc.test.d.ts b/public/types/src/scripts/actions/misc.test.d.ts deleted file mode 100644 index c81b2ccc6..000000000 --- a/public/types/src/scripts/actions/misc.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=misc.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/actions/misc.test.d.ts.map b/public/types/src/scripts/actions/misc.test.d.ts.map deleted file mode 100644 index 912fa1897..000000000 --- a/public/types/src/scripts/actions/misc.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"misc.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/actions/misc.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/choices.d.ts b/public/types/src/scripts/choices.d.ts index accf731a8..778f2e6f0 100644 --- a/public/types/src/scripts/choices.d.ts +++ b/public/types/src/scripts/choices.d.ts @@ -1,22 +1,28 @@ import { Container, Dropdown, Input, List, WrappedInput, WrappedSelect } from './components'; -import { Choice } from './interfaces/choice'; -import { Group } from './interfaces/group'; -import { Item } from './interfaces/item'; -import { Notice } from './interfaces/notice'; +import { InputChoice } from './interfaces/input-choice'; +import { InputGroup } from './interfaces/input-group'; import { Options } from './interfaces/options'; -import { State } from './interfaces/state'; +import { StateChangeSet } from './interfaces/state'; import Store from './store/store'; -import templates from './templates'; +import { ChoiceFull } from './interfaces/choice-full'; +import { GroupFull } from './interfaces/group-full'; +import { PassedElementType } from './interfaces'; +import { EventChoice } from './interfaces/event-choice'; +import { NoticeType, Templates } from './interfaces/templates'; +import { Searcher } from './interfaces/search'; /** * Choices * @author Josh Johnson */ -declare class Choices implements Choices { +declare class Choices { + static version: string; static get defaults(): { options: Partial; - templates: typeof templates; + allOptions: Options; + templates: Templates; }; initialised: boolean; + initialisedOK?: boolean; config: Options; passedElement: WrappedInput | WrappedSelect; containerOuter: Container; @@ -25,15 +31,17 @@ declare class Choices implements Choices { itemList: List; input: Input; dropdown: Dropdown; + _elementType: PassedElementType; _isTextElement: boolean; _isSelectOneElement: boolean; _isSelectMultipleElement: boolean; _isSelectElement: boolean; + _hasNonChoicePlaceholder: boolean; + _canAddUserChoices: boolean; _store: Store; - _templates: typeof templates; - _initialState: State; - _currentState: State; - _prevState: State; + _templates: Templates; + _lastAddedChoiceId: number; + _lastAddedGroupId: number; _currentValue: string; _canSearch: boolean; _isScrollingOnIe: boolean; @@ -46,17 +54,21 @@ declare class Choices implements Choices { _idNames: { itemChoice: string; }; - _presetGroups: Group[] | HTMLOptGroupElement[] | Element[]; - _presetOptions: Item[] | HTMLOptionElement[]; - _presetChoices: Partial[]; - _presetItems: Item[] | string[]; + _presetChoices: (ChoiceFull | GroupFull)[]; + _initialItems: string[]; + _searcher: Searcher; + _notice?: { + type: NoticeType; + text: string; + }; + _docRoot: ShadowRoot | HTMLElement; constructor(element?: string | Element | HTMLInputElement | HTMLSelectElement, userConfig?: Partial); init(): void; destroy(): void; enable(): this; disable(): this; - highlightItem(item: Item, runEvent?: boolean): this; - unhighlightItem(item: Item): this; + highlightItem(item: InputChoice, runEvent?: boolean): this; + unhighlightItem(item: InputChoice, runEvent?: boolean): this; highlightAll(): this; unhighlightAll(): this; removeActiveItemsByValue(value: string): this; @@ -64,8 +76,8 @@ declare class Choices implements Choices { removeHighlightedItems(runEvent?: boolean): this; showDropdown(preventInputFocus?: boolean): this; hideDropdown(preventInputBlur?: boolean): this; - getValue(valueOnly?: boolean): string[] | Item[] | Item | string; - setValue(items: string[] | Item[]): this; + getValue(valueOnly?: boolean): string[] | EventChoice[] | EventChoice | string; + setValue(items: string[] | InputChoice[]): this; setChoiceByValue(value: string | string[]): this; /** * Set choices of select input via an array of objects (or function that returns array of object or promise of it), @@ -130,37 +142,45 @@ declare class Choices implements Choices { * }], 'value', 'label', false); * ``` */ - setChoices(choicesArrayOrFetcher?: Choice[] | Group[] | ((instance: Choices) => Choice[] | Promise), value?: string, label?: string, replaceChoices?: boolean): this | Promise; + setChoices(choicesArrayOrFetcher?: (InputChoice | InputGroup)[] | ((instance: Choices) => (InputChoice | InputGroup)[] | Promise<(InputChoice | InputGroup)[]>), value?: string | null, label?: string, replaceChoices?: boolean): this | Promise; + refresh(withEvents?: boolean, selectFirstOption?: boolean, deselectAll?: boolean): this; + removeChoice(value: string): this; clearChoices(): this; clearStore(): this; clearInput(): this; - _render(): void; + _validateConfig(): void; + _render(changes?: StateChangeSet): void; _renderChoices(): void; _renderItems(): void; - _createGroupsFragment(groups: Group[], choices: Choice[], fragment?: DocumentFragment): DocumentFragment; - _createChoicesFragment(choices: Choice[], fragment?: DocumentFragment, withinGroup?: boolean): DocumentFragment; - _createItemsFragment(items: Item[], fragment?: DocumentFragment): DocumentFragment; + _createGroupsFragment(groups: GroupFull[], choices: ChoiceFull[], fragment?: DocumentFragment): DocumentFragment; + _createChoicesFragment(choices: ChoiceFull[], fragment?: DocumentFragment, withinGroup?: boolean): DocumentFragment; + _createItemsFragment(items: InputChoice[], fragment?: DocumentFragment): DocumentFragment; + _displayNotice(text: string, type: NoticeType, openDropdown?: boolean): void; + _clearNotice(): void; + _renderNotice(): void; + _getChoiceForOutput(choice?: ChoiceFull, keyCode?: number): EventChoice | undefined; _triggerChange(value: any): void; - _selectPlaceholderChoice(placeholderChoice: Choice): void; - _handleButtonAction(activeItems?: Item[], element?: HTMLElement): void; - _handleItemAction(activeItems?: Item[], element?: HTMLElement, hasShiftKey?: boolean): void; - _handleChoiceAction(activeItems?: Item[], element?: HTMLElement): void; - _handleBackspace(activeItems?: Item[]): void; - _startLoading(): void; - _stopLoading(): void; + _handleButtonAction(element?: HTMLElement): void; + _handleItemAction(element?: HTMLElement, hasShiftKey?: boolean): void; + _handleChoiceAction(element?: HTMLElement): boolean; + _handleBackspace(items: ChoiceFull[]): void; + _loadChoices(): void; _handleLoadingState(setLoading?: boolean): void; - _handleSearch(value: string): void; - _canAddItem(activeItems: Item[], value: string): Notice; - _searchChoices(value: string): number; + _handleSearch(value?: string): void; + _canAddItems(): boolean; + _canCreateItem(value: string): boolean; + _searchChoices(value: string): number | null; + _stopSearch(): void; _addEventListeners(): void; _removeEventListeners(): void; _onKeyDown(event: KeyboardEvent): void; - _onKeyUp({ target, keyCode, }: Pick): void; + _onKeyUp(): void; + _onInput(): void; _onSelectKey(event: KeyboardEvent, hasItems: boolean): void; - _onEnterKey(event: KeyboardEvent, activeItems: Item[], hasActiveDropdown: boolean): void; - _onEscapeKey(hasActiveDropdown: boolean): void; + _onEnterKey(event: KeyboardEvent, hasActiveDropdown: boolean): void; + _onEscapeKey(event: KeyboardEvent, hasActiveDropdown: boolean): void; _onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void; - _onDeleteKey(event: KeyboardEvent, activeItems: Item[], hasFocusedInput: boolean): void; + _onDeleteKey(event: KeyboardEvent, items: ChoiceFull[], hasFocusedInput: boolean): void; _onTouchMove(): void; _onTouchEnd(event: TouchEvent): void; /** @@ -177,42 +197,17 @@ declare class Choices implements Choices { _onBlur({ target }: Pick): void; _onFormReset(): void; _highlightChoice(el?: HTMLElement | null): void; - _addItem({ value, label, choiceId, groupId, customProperties, placeholder, keyCode, }: { - value: string; - label?: string | null; - choiceId?: number; - groupId?: number; - customProperties?: object; - placeholder?: boolean; - keyCode?: number; - }): void; - _removeItem(item: Item): void; - _addChoice({ value, label, isSelected, isDisabled, groupId, customProperties, placeholder, keyCode, }: { - value: string; - label?: string | null; - isSelected?: boolean; - isDisabled?: boolean; - groupId?: number; - customProperties?: Record; - placeholder?: boolean; - keyCode?: number; - }): void; - _addGroup({ group, id, valueKey, labelKey }: { - group: any; - id: any; - valueKey?: string | undefined; - labelKey?: string | undefined; - }): void; - _getTemplate(template: string, ...args: any): any; + _addItem(item: ChoiceFull, withEvents?: boolean, userTriggered?: boolean): void; + _removeItem(item: ChoiceFull): void; + _addChoice(choice: ChoiceFull, withEvents?: boolean, userTriggered?: boolean): void; + _addGroup(group: GroupFull, withEvents?: boolean): void; _createTemplates(): void; _createElements(): void; _createStructure(): void; - _addPredefinedGroups(groups: Group[] | HTMLOptGroupElement[] | Element[]): void; - _addPredefinedChoices(choices: Partial[]): void; - _addPredefinedItems(items: Item[] | string[]): void; - _setChoiceOrItem(item: any): void; - _findAndSelectChoiceByValue(value: string): void; + _initStore(): void; + _addPredefinedChoices(choices: (ChoiceFull | GroupFull)[], selectFirstOption?: boolean, withEvents?: boolean): void; + _findAndSelectChoiceByValue(value: string, userTriggered?: boolean): boolean; _generatePlaceholderValue(): string | null; + _warnChoicesInitFailed(caller: string): void; } export default Choices; -//# sourceMappingURL=choices.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/choices.d.ts.map b/public/types/src/scripts/choices.d.ts.map deleted file mode 100644 index 32cb7a2e5..000000000 --- a/public/types/src/scripts/choices.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../src/scripts/choices.ts"],"names":[],"mappings":"AAcA,OAAO,EACL,SAAS,EACT,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,aAAa,EACd,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAe3C,OAAO,KAAK,MAAM,eAAe,CAAC;AAClC,OAAO,SAAS,MAAM,aAAa,CAAC;AASpC;;;GAGG;AACH,cAAM,OAAQ,YAAW,OAAO;IAC9B,MAAM,KAAK,QAAQ,IAAI;QACrB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,SAAS,EAAE,OAAO,SAAS,CAAC;KAC7B,CASA;IAED,WAAW,EAAE,OAAO,CAAC;IAErB,MAAM,EAAE,OAAO,CAAC;IAEhB,aAAa,EAAE,YAAY,GAAG,aAAa,CAAC;IAE5C,cAAc,EAAE,SAAS,CAAC;IAE1B,cAAc,EAAE,SAAS,CAAC;IAE1B,UAAU,EAAE,IAAI,CAAC;IAEjB,QAAQ,EAAE,IAAI,CAAC;IAEf,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,QAAQ,CAAC;IAEnB,cAAc,EAAE,OAAO,CAAC;IAExB,mBAAmB,EAAE,OAAO,CAAC;IAE7B,wBAAwB,EAAE,OAAO,CAAC;IAElC,gBAAgB,EAAE,OAAO,CAAC;IAE1B,MAAM,EAAE,KAAK,CAAC;IAEd,UAAU,EAAE,OAAO,SAAS,CAAC;IAE7B,aAAa,EAAE,KAAK,CAAC;IAErB,aAAa,EAAE,KAAK,CAAC;IAErB,UAAU,EAAE,KAAK,CAAC;IAElB,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,OAAO,EAAE,OAAO,CAAC;IAEjB,YAAY,EAAE,OAAO,CAAC;IAEtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,OAAO,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAE/B,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,aAAa,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3D,cAAc,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE7C,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAElC,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;gBAG9B,OAAO,GACH,MAAM,GACN,OAAO,GACP,gBAAgB,GAChB,iBAAmC,EACvC,UAAU,GAAE,OAAO,CAAC,OAAO,CAAM;IAuLnC,IAAI,IAAI,IAAI;IA+BZ,OAAO,IAAI,IAAI;IAmBf,MAAM,IAAI,IAAI;IAcd,OAAO,IAAI,IAAI;IAcf,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,UAAO,GAAG,IAAI;IAsBhD,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAmBjC,YAAY,IAAI,IAAI;IAMpB,cAAc,IAAI,IAAI;IAMtB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAQ7C,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAQ3C,sBAAsB,CAAC,QAAQ,UAAQ,GAAG,IAAI;IAa9C,YAAY,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,IAAI;IAmB/C,YAAY,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,IAAI;IAoB9C,QAAQ,CAAC,SAAS,UAAQ,GAAG,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM;IAc9D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI;IAUxC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAchD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8DG;IACH,UAAU,CACR,qBAAqB,GACjB,MAAM,EAAE,GACR,KAAK,EAAE,GACP,CAAC,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAM,EAC9D,KAAK,SAAU,EACf,KAAK,SAAU,EACf,cAAc,UAAQ,GACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA+FvB,YAAY,IAAI,IAAI;IAMpB,UAAU,IAAI,IAAI;IAMlB,UAAU,IAAI,IAAI;IAYlB,OAAO,IAAI,IAAI;IA8Bf,cAAc,IAAI,IAAI;IA6EtB,YAAY,IAAI,IAAI;IAcpB,qBAAqB,CACnB,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,GAAE,gBAAoD,GAC7D,gBAAgB;IA8BnB,sBAAsB,CACpB,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,GAAE,gBAAoD,EAC9D,WAAW,UAAQ,GAClB,gBAAgB;IAyEnB,oBAAoB,CAClB,KAAK,EAAE,IAAI,EAAE,EACb,QAAQ,GAAE,gBAAoD,GAC7D,gBAAgB;IAgCnB,cAAc,CAAC,KAAK,KAAA,GAAG,IAAI;IAU3B,wBAAwB,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI;IAYzD,mBAAmB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IA4BtE,iBAAiB,CACf,WAAW,CAAC,EAAE,IAAI,EAAE,EACpB,OAAO,CAAC,EAAE,WAAW,EACrB,WAAW,UAAQ,GAClB,IAAI;IA4BP,mBAAmB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IAoDtE,gBAAgB,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI;IAwB5C,aAAa,IAAI,IAAI;IAIrB,YAAY,IAAI,IAAI;IAIpB,mBAAmB,CAAC,UAAU,UAAO,GAAG,IAAI;IAuC5C,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IA4BlC,WAAW,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAwDvD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IA6BrC,kBAAkB,IAAI,IAAI;IAsD1B,qBAAqB,IAAI,IAAI;IAmC7B,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAsDtC,QAAQ,CAAC,EACP,MAAM,EACN,OAAO,GACR,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,SAAS,CAAC,GAAG,IAAI;IAoCnD,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAmB3D,WAAW,CACT,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,IAAI,EAAE,EACnB,iBAAiB,EAAE,OAAO,GACzB,IAAI;IA2CP,YAAY,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAO9C,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAgEvE,YAAY,CACV,KAAK,EAAE,aAAa,EACpB,WAAW,EAAE,IAAI,EAAE,EACnB,eAAe,EAAE,OAAO,GACvB,IAAI;IAaP,YAAY,IAAI,IAAI;IAMpB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAyBpC;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAyCrC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAM1D,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAkCtD,QAAQ,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IAiCtD,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAI;IA+CrD,YAAY,IAAI,IAAI;IAIpB,gBAAgB,CAAC,EAAE,GAAE,WAAW,GAAG,IAAW,GAAG,IAAI;IAmDrD,QAAQ,CAAC,EACP,KAAK,EACL,KAAY,EACZ,QAAa,EACb,OAAY,EACZ,gBAAqB,EACrB,WAAmB,EACnB,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IA+CR,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAmB7B,UAAU,CAAC,EACT,KAAK,EACL,KAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,OAAY,EACZ,gBAAqB,EACrB,WAAmB,EACnB,OAAY,GACb,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IAqCR,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,QAAkB,EAAE,QAAkB,EAAE;;;;;KAAA,GAAG,IAAI;IA6CtE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,GAAG;IAIjD,gBAAgB,IAAI,IAAI;IAcxB,eAAe,IAAI,IAAI;IA6CvB,gBAAgB,IAAI,IAAI;IAmDxB,oBAAoB,CAClB,MAAM,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,GAClD,IAAI;IA2BP,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI;IA0DvD,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI;IAoBnD,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAgDjC,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAoBhD,yBAAyB,IAAI,MAAM,GAAG,IAAI;CA2B3C;AAED,eAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/choices.test.d.ts b/public/types/src/scripts/choices.test.d.ts deleted file mode 100644 index 1b34de17b..000000000 --- a/public/types/src/scripts/choices.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=choices.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/choices.test.d.ts.map b/public/types/src/scripts/choices.test.d.ts.map deleted file mode 100644 index 13f8b43e0..000000000 --- a/public/types/src/scripts/choices.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/choices.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/container.d.ts b/public/types/src/scripts/components/container.d.ts index ad8ac4e6a..e4b551b6b 100644 --- a/public/types/src/scripts/components/container.d.ts +++ b/public/types/src/scripts/components/container.d.ts @@ -8,7 +8,6 @@ export default class Container { position: PositionOptionsType; isOpen: boolean; isFlipped: boolean; - isFocussed: boolean; isDisabled: boolean; isLoading: boolean; constructor({ element, type, classNames, position, }: { @@ -17,8 +16,6 @@ export default class Container { classNames: ClassNames; position: PositionOptionsType; }); - addEventListeners(): void; - removeEventListeners(): void; /** * Determine whether container should be flipped based on passed * dropdown position @@ -28,16 +25,12 @@ export default class Container { removeActiveDescendant(): void; open(dropdownPos: number): void; close(): void; - focus(): void; addFocusState(): void; removeFocusState(): void; enable(): void; disable(): void; - wrap(element: HTMLSelectElement | HTMLInputElement | HTMLElement): void; + wrap(element: HTMLElement): void; unwrap(element: HTMLElement): void; addLoadingState(): void; removeLoadingState(): void; - _onFocus(): void; - _onBlur(): void; } -//# sourceMappingURL=container.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/container.d.ts.map b/public/types/src/scripts/components/container.d.ts.map deleted file mode 100644 index e253b2fb0..000000000 --- a/public/types/src/scripts/components/container.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/container.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B,OAAO,EAAE,WAAW,CAAC;IAErB,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,mBAAmB,CAAC;IAE9B,MAAM,EAAE,OAAO,CAAC;IAEhB,SAAS,EAAE,OAAO,CAAC;IAEnB,UAAU,EAAE,OAAO,CAAC;IAEpB,UAAU,EAAE,OAAO,CAAC;IAEpB,SAAS,EAAE,OAAO,CAAC;gBAEP,EACV,OAAO,EACP,IAAI,EACJ,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;QACvB,QAAQ,EAAE,mBAAmB,CAAC;KAC/B;IAcD,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAkBxC,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI;IAIrD,sBAAsB,IAAI,IAAI;IAI9B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAW/B,KAAK,IAAI,IAAI;IAab,KAAK,IAAI,IAAI;IAMb,aAAa,IAAI,IAAI;IAIrB,gBAAgB,IAAI,IAAI;IAIxB,MAAM,IAAI,IAAI;IASd,OAAO,IAAI,IAAI;IASf,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,WAAW,GAAG,IAAI;IAIvE,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IASlC,eAAe,IAAI,IAAI;IAMvB,kBAAkB,IAAI,IAAI;IAM1B,QAAQ,IAAI,IAAI;IAIhB,OAAO,IAAI,IAAI;CAGhB"} \ No newline at end of file diff --git a/public/types/src/scripts/components/container.test.d.ts b/public/types/src/scripts/components/container.test.d.ts deleted file mode 100644 index 1628d9d7d..000000000 --- a/public/types/src/scripts/components/container.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=container.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/container.test.d.ts.map b/public/types/src/scripts/components/container.test.d.ts.map deleted file mode 100644 index 85b9faa6b..000000000 --- a/public/types/src/scripts/components/container.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"container.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/container.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/dropdown.d.ts b/public/types/src/scripts/components/dropdown.d.ts index a6f574251..4fd23e3e7 100644 --- a/public/types/src/scripts/components/dropdown.d.ts +++ b/public/types/src/scripts/components/dropdown.d.ts @@ -14,7 +14,6 @@ export default class Dropdown { * Bottom position of dropdown in viewport coordinates */ get distanceFromTopWindow(): number; - getChild(selector: string): HTMLElement | null; /** * Show dropdown to user by adding active state class */ @@ -24,4 +23,3 @@ export default class Dropdown { */ hide(): this; } -//# sourceMappingURL=dropdown.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/dropdown.d.ts.map b/public/types/src/scripts/components/dropdown.d.ts.map deleted file mode 100644 index 129ec116c..000000000 --- a/public/types/src/scripts/components/dropdown.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/dropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,QAAQ;IAC3B,OAAO,EAAE,WAAW,CAAC;IAErB,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,OAAO,CAAC;gBAEN,EACV,OAAO,EACP,IAAI,EACJ,UAAU,GACX,EAAE;QACD,OAAO,EAAE,WAAW,CAAC;QACrB,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;KACxB;IAOD;;OAEG;IACH,IAAI,qBAAqB,IAAI,MAAM,CAElC;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAI9C;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,IAAI,IAAI,IAAI;CAOb"} \ No newline at end of file diff --git a/public/types/src/scripts/components/dropdown.test.d.ts b/public/types/src/scripts/components/dropdown.test.d.ts deleted file mode 100644 index 2dff0fae5..000000000 --- a/public/types/src/scripts/components/dropdown.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=dropdown.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/dropdown.test.d.ts.map b/public/types/src/scripts/components/dropdown.test.d.ts.map deleted file mode 100644 index 4c26ccd8c..000000000 --- a/public/types/src/scripts/components/dropdown.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"dropdown.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/dropdown.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/index.d.ts b/public/types/src/scripts/components/index.d.ts index b37addf93..fe9659264 100644 --- a/public/types/src/scripts/components/index.d.ts +++ b/public/types/src/scripts/components/index.d.ts @@ -5,4 +5,3 @@ import List from './list'; import WrappedInput from './wrapped-input'; import WrappedSelect from './wrapped-select'; export { Dropdown, Container, Input, List, WrappedInput, WrappedSelect }; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/index.d.ts.map b/public/types/src/scripts/components/index.d.ts.map deleted file mode 100644 index 4e478ab46..000000000 --- a/public/types/src/scripts/components/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,SAAS,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,IAAI,MAAM,QAAQ,CAAC;AAC1B,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAC3C,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/components/input.d.ts b/public/types/src/scripts/components/input.d.ts index 3b36571dd..f1d5aaa9f 100644 --- a/public/types/src/scripts/components/input.d.ts +++ b/public/types/src/scripts/components/input.d.ts @@ -16,7 +16,6 @@ export default class Input { set placeholder(placeholder: string); get value(): string; set value(value: string); - get rawValue(): string; addEventListeners(): void; removeEventListeners(): void; enable(): void; @@ -36,4 +35,3 @@ export default class Input { _onFocus(): void; _onBlur(): void; } -//# sourceMappingURL=input.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/input.d.ts.map b/public/types/src/scripts/components/input.d.ts.map deleted file mode 100644 index 8828eb16f..000000000 --- a/public/types/src/scripts/components/input.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/input.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,OAAO,EAAE,gBAAgB,CAAC;IAE1B,IAAI,EAAE,iBAAiB,CAAC;IAExB,UAAU,EAAE,UAAU,CAAC;IAEvB,YAAY,EAAE,OAAO,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,UAAU,EAAE,OAAO,CAAC;gBAER,EACV,OAAO,EACP,IAAI,EACJ,UAAU,EACV,YAAY,GACb,EAAE;QACD,OAAO,EAAE,gBAAgB,CAAC;QAC1B,IAAI,EAAE,iBAAiB,CAAC;QACxB,UAAU,EAAE,UAAU,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;KACvB;IAcD,IAAI,WAAW,CAAC,WAAW,EAAE,MAAM,EAElC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,iBAAiB,IAAI,IAAI;IAazB,oBAAoB,IAAI,IAAI;IAO5B,MAAM,IAAI,IAAI;IAKd,OAAO,IAAI,IAAI;IAKf,KAAK,IAAI,IAAI;IAMb,IAAI,IAAI,IAAI;IAMZ,KAAK,CAAC,QAAQ,UAAO,GAAG,IAAI;IAY5B;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAOhB,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI;IAIrD,sBAAsB,IAAI,IAAI;IAI9B,QAAQ,IAAI,IAAI;IAMhB,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAMrC,QAAQ,IAAI,IAAI;IAIhB,OAAO,IAAI,IAAI;CAGhB"} \ No newline at end of file diff --git a/public/types/src/scripts/components/input.test.d.ts b/public/types/src/scripts/components/input.test.d.ts deleted file mode 100644 index f6118e429..000000000 --- a/public/types/src/scripts/components/input.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=input.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/input.test.d.ts.map b/public/types/src/scripts/components/input.test.d.ts.map deleted file mode 100644 index be4336853..000000000 --- a/public/types/src/scripts/components/input.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"input.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/input.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/list.d.ts b/public/types/src/scripts/components/list.d.ts index 51a086854..baaa1c94e 100644 --- a/public/types/src/scripts/components/list.d.ts +++ b/public/types/src/scripts/components/list.d.ts @@ -6,13 +6,10 @@ export default class List { element: HTMLElement; }); clear(): void; - append(node: Element | DocumentFragment): void; - getChild(selector: string): HTMLElement | null; - hasChildren(): boolean; + prepend(node: Element | DocumentFragment): void; scrollToTop(): void; scrollToChildElement(element: HTMLElement, direction: 1 | -1): void; _scrollDown(scrollPos: number, strength: number, destination: number): void; _scrollUp(scrollPos: number, strength: number, destination: number): void; _animateScroll(destination: number, direction: number): void; } -//# sourceMappingURL=list.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/list.d.ts.map b/public/types/src/scripts/components/list.d.ts.map deleted file mode 100644 index 8b9068f42..000000000 --- a/public/types/src/scripts/components/list.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/list.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB,OAAO,EAAE,WAAW,CAAC;IAErB,SAAS,EAAE,MAAM,CAAC;IAElB,MAAM,EAAE,MAAM,CAAC;gBAEH,EAAE,OAAO,EAAE,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE;IAMjD,KAAK,IAAI,IAAI;IAIb,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI;IAI9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAI9C,WAAW,IAAI,OAAO;IAItB,WAAW,IAAI,IAAI;IAInB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;IAwBnE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAO3E,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAOzE,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAyB7D"} \ No newline at end of file diff --git a/public/types/src/scripts/components/list.test.d.ts b/public/types/src/scripts/components/list.test.d.ts deleted file mode 100644 index ce43e8d40..000000000 --- a/public/types/src/scripts/components/list.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=list.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/list.test.d.ts.map b/public/types/src/scripts/components/list.test.d.ts.map deleted file mode 100644 index ad131680b..000000000 --- a/public/types/src/scripts/components/list.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"list.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/list.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-element.d.ts b/public/types/src/scripts/components/wrapped-element.d.ts index ae18b82e5..ba2444494 100644 --- a/public/types/src/scripts/components/wrapped-element.d.ts +++ b/public/types/src/scripts/components/wrapped-element.d.ts @@ -1,7 +1,8 @@ import { ClassNames } from '../interfaces/class-names'; import { EventType } from '../interfaces/event-type'; -export default class WrappedElement { - element: HTMLInputElement | HTMLSelectElement; +import { EventMap } from '../interfaces'; +export default class WrappedElement { + element: T; classNames: ClassNames; isDisabled: boolean; constructor({ element, classNames }: { @@ -16,6 +17,5 @@ export default class WrappedElement { reveal(): void; enable(): void; disable(): void; - triggerEvent(eventType: EventType, data?: object): void; + triggerEvent(eventType: EventType, data?: EventMap[K]['detail']): void; } -//# sourceMappingURL=wrapped-element.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-element.d.ts.map b/public/types/src/scripts/components/wrapped-element.d.ts.map deleted file mode 100644 index 2df95bf8e..000000000 --- a/public/types/src/scripts/components/wrapped-element.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-element.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IAE9C,UAAU,EAAE,UAAU,CAAC;IAEvB,UAAU,EAAE,OAAO,CAAC;gBAER,EAAE,OAAO,EAAE,UAAU,EAAE;;;KAAA;IAcnC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,OAAO,IAAI,IAAI;IAkBf,MAAM,IAAI,IAAI;IAsBd,MAAM,IAAI,IAAI;IAMd,OAAO,IAAI,IAAI;IAMf,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;CAGxD"} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-element.test.d.ts b/public/types/src/scripts/components/wrapped-element.test.d.ts deleted file mode 100644 index a9838b851..000000000 --- a/public/types/src/scripts/components/wrapped-element.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=wrapped-element.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-element.test.d.ts.map b/public/types/src/scripts/components/wrapped-element.test.d.ts.map deleted file mode 100644 index fb26915d6..000000000 --- a/public/types/src/scripts/components/wrapped-element.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-element.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-element.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-input.d.ts b/public/types/src/scripts/components/wrapped-input.d.ts index c4c352402..6b44b5805 100644 --- a/public/types/src/scripts/components/wrapped-input.d.ts +++ b/public/types/src/scripts/components/wrapped-input.d.ts @@ -1,14 +1,3 @@ -import { ClassNames } from '../interfaces/class-names'; import WrappedElement from './wrapped-element'; -export default class WrappedInput extends WrappedElement { - element: HTMLInputElement; - delimiter: string; - constructor({ element, classNames, delimiter, }: { - element: HTMLInputElement; - classNames: ClassNames; - delimiter: string; - }); - get value(): string; - set value(value: string); +export default class WrappedInput extends WrappedElement { } -//# sourceMappingURL=wrapped-input.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-input.d.ts.map b/public/types/src/scripts/components/wrapped-input.d.ts.map deleted file mode 100644 index 224197b86..000000000 --- a/public/types/src/scripts/components/wrapped-input.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-input.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,cAAc;IACtD,OAAO,EAAE,gBAAgB,CAAC;IAE1B,SAAS,EAAE,MAAM,CAAC;gBAEN,EACV,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE;QACD,OAAO,EAAE,gBAAgB,CAAC;QAC1B,UAAU,EAAE,UAAU,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB;IAKD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;CACF"} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-input.test.d.ts b/public/types/src/scripts/components/wrapped-input.test.d.ts deleted file mode 100644 index 5b233465a..000000000 --- a/public/types/src/scripts/components/wrapped-input.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=wrapped-input.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-input.test.d.ts.map b/public/types/src/scripts/components/wrapped-input.test.d.ts.map deleted file mode 100644 index 28ba6ce03..000000000 --- a/public/types/src/scripts/components/wrapped-input.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-input.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-input.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-select.d.ts b/public/types/src/scripts/components/wrapped-select.d.ts index 5b4f647b8..559018fa5 100644 --- a/public/types/src/scripts/components/wrapped-select.d.ts +++ b/public/types/src/scripts/components/wrapped-select.d.ts @@ -1,19 +1,20 @@ import { ClassNames } from '../interfaces/class-names'; -import { Item } from '../interfaces/item'; import WrappedElement from './wrapped-element'; -export default class WrappedSelect extends WrappedElement { - element: HTMLSelectElement; +import { GroupFull } from '../interfaces/group-full'; +import { ChoiceFull } from '../interfaces/choice-full'; +export default class WrappedSelect extends WrappedElement { classNames: ClassNames; template: (data: object) => HTMLOptionElement; - constructor({ element, classNames, template, }: { + extractPlaceholder: boolean; + constructor({ element, classNames, template, extractPlaceholder, }: { element: HTMLSelectElement; classNames: ClassNames; template: (data: object) => HTMLOptionElement; + extractPlaceholder: boolean; }); get placeholderOption(): HTMLOptionElement | null; - get optionGroups(): Element[]; - get options(): Item[] | HTMLOptionElement[]; - set options(options: Item[] | HTMLOptionElement[]); - appendDocFragment(fragment: DocumentFragment): void; + addOptions(choices: ChoiceFull[]): void; + optionsAsChoices(): (ChoiceFull | GroupFull)[]; + _optionToChoice(option: HTMLOptionElement): ChoiceFull; + _optgroupToChoice(optgroup: HTMLOptGroupElement): GroupFull; } -//# sourceMappingURL=wrapped-select.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-select.d.ts.map b/public/types/src/scripts/components/wrapped-select.d.ts.map deleted file mode 100644 index 47ebbf79b..000000000 --- a/public/types/src/scripts/components/wrapped-select.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-select.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,cAAc;IACvD,OAAO,EAAE,iBAAiB,CAAC;IAE3B,UAAU,EAAE,UAAU,CAAC;IAEvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAC;gBAElC,EACV,OAAO,EACP,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,iBAAiB,CAAC;QAC3B,UAAU,EAAE,UAAU,CAAC;QACvB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,iBAAiB,CAAC;KAC/C;IAKD,IAAI,iBAAiB,IAAI,iBAAiB,GAAG,IAAI,CAMhD;IAED,IAAI,YAAY,IAAI,OAAO,EAAE,CAE5B;IAED,IAAI,OAAO,IAAI,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAE1C;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,EAahD;IAED,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;CAIpD"} \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-select.test.d.ts b/public/types/src/scripts/components/wrapped-select.test.d.ts deleted file mode 100644 index 414d6156e..000000000 --- a/public/types/src/scripts/components/wrapped-select.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=wrapped-select.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/components/wrapped-select.test.d.ts.map b/public/types/src/scripts/components/wrapped-select.test.d.ts.map deleted file mode 100644 index e97409561..000000000 --- a/public/types/src/scripts/components/wrapped-select.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"wrapped-select.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/components/wrapped-select.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/constants.d.ts b/public/types/src/scripts/constants.d.ts index 1891ccca2..b16044a29 100644 --- a/public/types/src/scripts/constants.d.ts +++ b/public/types/src/scripts/constants.d.ts @@ -1,11 +1,4 @@ -import { ActionType } from './interfaces/action-type'; -import { EventType } from './interfaces/event-type'; -import { KeyCodeMap } from './interfaces/keycode-map'; -export declare const EVENTS: Record; -export declare const ACTION_TYPES: Record; -export declare const KEY_CODES: KeyCodeMap; export declare const TEXT_TYPE: HTMLInputElement['type']; export declare const SELECT_ONE_TYPE: HTMLSelectElement['type']; export declare const SELECT_MULTIPLE_TYPE: HTMLSelectElement['type']; -export declare const SCROLLING_SPEED = 4; -//# sourceMappingURL=constants.d.ts.map \ No newline at end of file +export declare const SCROLLING_SPEED: number; diff --git a/public/types/src/scripts/constants.d.ts.map b/public/types/src/scripts/constants.d.ts.map deleted file mode 100644 index 1a564df75..000000000 --- a/public/types/src/scripts/constants.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/scripts/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,CAW/C,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,CAYvD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,UAUvB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,gBAAgB,CAAC,MAAM,CAAU,CAAC;AAC1D,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC,MAAM,CAAgB,CAAC;AACvE,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,CAAC,MAAM,CACxC,CAAC;AAEpB,eAAO,MAAM,eAAe,IAAI,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/constants.test.d.ts b/public/types/src/scripts/constants.test.d.ts deleted file mode 100644 index bc2a88b9f..000000000 --- a/public/types/src/scripts/constants.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=constants.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/constants.test.d.ts.map b/public/types/src/scripts/constants.test.d.ts.map deleted file mode 100644 index 20f779978..000000000 --- a/public/types/src/scripts/constants.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"constants.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/constants.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/defaults.d.ts b/public/types/src/scripts/defaults.d.ts index db8a9a92f..e9cdce089 100644 --- a/public/types/src/scripts/defaults.d.ts +++ b/public/types/src/scripts/defaults.d.ts @@ -2,4 +2,3 @@ import { ClassNames } from './interfaces/class-names'; import { Options } from './interfaces/options'; export declare const DEFAULT_CLASSNAMES: ClassNames; export declare const DEFAULT_CONFIG: Options; -//# sourceMappingURL=defaults.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/defaults.d.ts.map b/public/types/src/scripts/defaults.d.ts.map deleted file mode 100644 index dffe8a888..000000000 --- a/public/types/src/scripts/defaults.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../../src/scripts/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,eAAO,MAAM,kBAAkB,EAAE,UA2BhC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,OA+C5B,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/action-type.d.ts b/public/types/src/scripts/interfaces/action-type.d.ts index d7d694f57..156999cee 100644 --- a/public/types/src/scripts/interfaces/action-type.d.ts +++ b/public/types/src/scripts/interfaces/action-type.d.ts @@ -1,2 +1,12 @@ -export type ActionType = 'ADD_CHOICE' | 'FILTER_CHOICES' | 'ACTIVATE_CHOICES' | 'CLEAR_CHOICES' | 'ADD_GROUP' | 'ADD_ITEM' | 'REMOVE_ITEM' | 'HIGHLIGHT_ITEM' | 'CLEAR_ALL' | 'RESET_TO' | 'SET_IS_LOADING'; -//# sourceMappingURL=action-type.d.ts.map \ No newline at end of file +export declare const ActionType: { + readonly ADD_CHOICE: "ADD_CHOICE"; + readonly REMOVE_CHOICE: "REMOVE_CHOICE"; + readonly FILTER_CHOICES: "FILTER_CHOICES"; + readonly ACTIVATE_CHOICES: "ACTIVATE_CHOICES"; + readonly CLEAR_CHOICES: "CLEAR_CHOICES"; + readonly ADD_GROUP: "ADD_GROUP"; + readonly ADD_ITEM: "ADD_ITEM"; + readonly REMOVE_ITEM: "REMOVE_ITEM"; + readonly HIGHLIGHT_ITEM: "HIGHLIGHT_ITEM"; +}; +export type ActionTypes = (typeof ActionType)[keyof typeof ActionType]; diff --git a/public/types/src/scripts/interfaces/action-type.d.ts.map b/public/types/src/scripts/interfaces/action-type.d.ts.map deleted file mode 100644 index 9721610aa..000000000 --- a/public/types/src/scripts/interfaces/action-type.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"action-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/action-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,WAAW,GACX,UAAU,GACV,aAAa,GACb,gBAAgB,GAChB,WAAW,GACX,UAAU,GACV,gBAAgB,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/build-flags.d.ts b/public/types/src/scripts/interfaces/build-flags.d.ts new file mode 100644 index 000000000..c803580a5 --- /dev/null +++ b/public/types/src/scripts/interfaces/build-flags.d.ts @@ -0,0 +1,9 @@ +export declare const canUseDom: boolean; +export declare const searchFuse: string | undefined; +/** + * These are not directly used, as an exported object (even as const) will prevent tree-shake away code paths + */ +export declare const BuildFlags: { + readonly searchFuse: string | undefined; + readonly canUseDom: boolean; +}; diff --git a/public/types/src/scripts/interfaces/choice-full.d.ts b/public/types/src/scripts/interfaces/choice-full.d.ts new file mode 100644 index 000000000..bd3088540 --- /dev/null +++ b/public/types/src/scripts/interfaces/choice-full.d.ts @@ -0,0 +1,20 @@ +import { StringUntrusted } from './string-untrusted'; +export type CustomProperties = Record | string; +export interface ChoiceFull { + id: number; + highlighted: boolean; + element?: HTMLOptionElement | HTMLOptGroupElement; + labelClass?: Array; + labelDescription?: string; + customProperties?: CustomProperties; + disabled: boolean; + active: boolean; + elementId?: string; + groupId: number; + label: StringUntrusted | string; + placeholder: boolean; + selected: boolean; + value: string; + score: number; + rank: number; +} diff --git a/public/types/src/scripts/interfaces/choice.d.ts b/public/types/src/scripts/interfaces/choice.d.ts deleted file mode 100644 index db222d054..000000000 --- a/public/types/src/scripts/interfaces/choice.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Choice { - id?: number; - customProperties?: Record; - disabled?: boolean; - active?: boolean; - elementId?: number; - groupId?: number; - keyCode?: number; - label: string; - placeholder?: boolean; - selected?: boolean; - value: any; - score?: number; - choices?: Choice[]; -} -//# sourceMappingURL=choice.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/choice.d.ts.map b/public/types/src/scripts/interfaces/choice.d.ts.map deleted file mode 100644 index b692cb7af..000000000 --- a/public/types/src/scripts/interfaces/choice.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choice.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/choice.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/choices.d.ts b/public/types/src/scripts/interfaces/choices.d.ts deleted file mode 100644 index 613a2ab0d..000000000 --- a/public/types/src/scripts/interfaces/choices.d.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Options } from 'deepmerge'; -import { Store } from 'redux'; -import { WrappedInput, WrappedSelect, Container, List, Input, Dropdown } from '../components'; -import { Choice } from './choice'; -import { Group } from './group'; -import { Item } from './item'; -import { State } from './state'; -import templates from '../templates'; -export interface Choices { - initialised: boolean; - config: Options; - passedElement: WrappedInput | WrappedSelect; - containerOuter: Container; - containerInner: Container; - choiceList: List; - itemList: List; - input: Input; - dropdown: Dropdown; - _isTextElement: boolean; - _isSelectOneElement: boolean; - _isSelectMultipleElement: boolean; - _isSelectElement: boolean; - _store: Store; - _templates: typeof templates; - _initialState: State; - _currentState: State; - _prevState: State; - _currentValue: string; - _canSearch: boolean; - _isScrollingOnIe: boolean; - _highlightPosition: number; - _wasTap: boolean; - _isSearching: boolean; - _placeholderValue: string | null; - _baseId: string; - _direction: HTMLElement['dir']; - _idNames: { - itemChoice: string; - }; - _presetGroups: Group[] | HTMLOptGroupElement[] | Element[]; - _presetOptions: Item[] | HTMLOptionElement[]; - _presetChoices: Partial[]; - _presetItems: Item[] | string[]; - new (element: string | Element | HTMLInputElement | HTMLSelectElement, userConfig: Partial): any; -} -//# sourceMappingURL=choices.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/choices.d.ts.map b/public/types/src/scripts/interfaces/choices.d.ts.map deleted file mode 100644 index acb6e0859..000000000 --- a/public/types/src/scripts/interfaces/choices.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,SAAS,EACT,IAAI,EACJ,KAAK,EACL,QAAQ,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAEhB,aAAa,EAAE,YAAY,GAAG,aAAa,CAAC;IAE5C,cAAc,EAAE,SAAS,CAAC;IAE1B,cAAc,EAAE,SAAS,CAAC;IAE1B,UAAU,EAAE,IAAI,CAAC;IAEjB,QAAQ,EAAE,IAAI,CAAC;IAEf,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,QAAQ,CAAC;IAEnB,cAAc,EAAE,OAAO,CAAC;IAExB,mBAAmB,EAAE,OAAO,CAAC;IAE7B,wBAAwB,EAAE,OAAO,CAAC;IAElC,gBAAgB,EAAE,OAAO,CAAC;IAE1B,MAAM,EAAE,KAAK,CAAC;IAEd,UAAU,EAAE,OAAO,SAAS,CAAC;IAE7B,aAAa,EAAE,KAAK,CAAC;IAErB,aAAa,EAAE,KAAK,CAAC;IAErB,UAAU,EAAE,KAAK,CAAC;IAElB,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,OAAO,CAAC;IAEpB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,kBAAkB,EAAE,MAAM,CAAC;IAE3B,OAAO,EAAE,OAAO,CAAC;IAEjB,YAAY,EAAE,OAAO,CAAC;IAEtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC,OAAO,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAE/B,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,aAAa,EAAE,KAAK,EAAE,GAAG,mBAAmB,EAAE,GAAG,OAAO,EAAE,CAAC;IAE3D,cAAc,EAAE,IAAI,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAE7C,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IAElC,YAAY,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAEhC,KACE,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,gBAAgB,GAAG,iBAAiB,EAChE,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,OAC5B;CACH"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/class-names.d.ts b/public/types/src/scripts/interfaces/class-names.d.ts index 09818ef6b..b6283385c 100644 --- a/public/types/src/scripts/interfaces/class-names.d.ts +++ b/public/types/src/scripts/interfaces/class-names.d.ts @@ -1,56 +1,61 @@ /** Classes added to HTML generated by By default classnames follow the BEM notation. */ export interface ClassNames { - /** @default 'choices' */ - containerOuter: string; - /** @default 'choices__inner' */ - containerInner: string; - /** @default 'choices__input' */ - input: string; - /** @default 'choices__input--cloned' */ - inputCloned: string; - /** @default 'choices__list' */ - list: string; - /** @default 'choices__list--multiple' */ - listItems: string; - /** @default 'choices__list--single' */ - listSingle: string; - /** @default 'choices__list--dropdown' */ - listDropdown: string; - /** @default 'choices__item' */ - item: string; - /** @default 'choices__item--selectable' */ - itemSelectable: string; - /** @default 'choices__item--disabled' */ - itemDisabled: string; - /** @default 'choices__item--choice' */ - itemChoice: string; - /** @default 'choices__placeholder' */ - placeholder: string; - /** @default 'choices__group' */ - group: string; - /** @default 'choices__heading' */ - groupHeading: string; - /** @default 'choices__button' */ - button: string; - /** @default 'is-active' */ - activeState: string; - /** @default 'is-focused' */ - focusState: string; - /** @default 'is-open' */ - openState: string; - /** @default 'is-disabled' */ - disabledState: string; - /** @default 'is-highlighted' */ - highlightedState: string; - /** @default 'is-selected' */ - selectedState: string; - /** @default 'is-flipped' */ - flippedState: string; - /** @default 'is-loading' */ - loadingState: string; - /** @default 'has-no-results' */ - noResults: string; - /** @default 'has-no-choices' */ - noChoices: string; + /** @default ['choices'] */ + containerOuter: string | Array; + /** @default ['choices__inner'] */ + containerInner: string | Array; + /** @default ['choices__input'] */ + input: string | Array; + /** @default ['choices__input--cloned'] */ + inputCloned: string | Array; + /** @default ['choices__list'] */ + list: string | Array; + /** @default ['choices__list--multiple'] */ + listItems: string | Array; + /** @default ['choices__list--single'] */ + listSingle: string | Array; + /** @default ['choices__list--dropdown'] */ + listDropdown: string | Array; + /** @default ['choices__item'] */ + item: string | Array; + /** @default ['choices__item--selectable'] */ + itemSelectable: string | Array; + /** @default ['choices__item--disabled'] */ + itemDisabled: string | Array; + /** @default ['choices__item--choice'] */ + itemChoice: string | Array; + /** @default ['choices__description'] */ + description: string | Array; + /** @default ['choices__placeholder'] */ + placeholder: string | Array; + /** @default ['choices__group'] */ + group: string | Array; + /** @default ['choices__heading'] */ + groupHeading: string | Array; + /** @default ['choices__button'] */ + button: string | Array; + /** @default ['is-active'] */ + activeState: string | Array; + /** @default ['is-focused'] */ + focusState: string | Array; + /** @default ['is-open'] */ + openState: string | Array; + /** @default ['is-disabled'] */ + disabledState: string | Array; + /** @default ['is-highlighted'] */ + highlightedState: string | Array; + /** @default ['is-selected'] */ + selectedState: string | Array; + /** @default ['is-flipped'] */ + flippedState: string | Array; + /** @default ['is-loading'] */ + loadingState: string | Array; + /** @default ['choices__notice'] */ + notice: string | Array; + /** @default ['choices__item--selectable', 'add-choice'] */ + addChoice: string | Array; + /** @default ['has-no-results'] */ + noResults: string | Array; + /** @default ['has-no-choices'] */ + noChoices: string | Array; } -//# sourceMappingURL=class-names.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/class-names.d.ts.map b/public/types/src/scripts/interfaces/class-names.d.ts.map deleted file mode 100644 index b5c606878..000000000 --- a/public/types/src/scripts/interfaces/class-names.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"class-names.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/class-names.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,WAAW,UAAU;IACzB,yBAAyB;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/event-choice.d.ts b/public/types/src/scripts/interfaces/event-choice.d.ts new file mode 100644 index 000000000..5fbc45c22 --- /dev/null +++ b/public/types/src/scripts/interfaces/event-choice.d.ts @@ -0,0 +1,6 @@ +import { InputChoice } from './input-choice'; +export interface EventChoice extends InputChoice { + element?: HTMLOptionElement | HTMLOptGroupElement; + groupValue?: string; + keyCode?: number; +} diff --git a/public/types/src/scripts/interfaces/event-type.d.ts b/public/types/src/scripts/interfaces/event-type.d.ts index b2d97ac9b..71c97d871 100644 --- a/public/types/src/scripts/interfaces/event-type.d.ts +++ b/public/types/src/scripts/interfaces/event-type.d.ts @@ -1,2 +1,12 @@ -export type EventType = 'addItem' | 'removeItem' | 'highlightItem' | 'unhighlightItem' | 'choice' | 'change' | 'search' | 'showDropdown' | 'hideDropdown' | 'highlightChoice'; -//# sourceMappingURL=event-type.d.ts.map \ No newline at end of file +export declare const enum EventType { + showDropdown = "showDropdown", + hideDropdown = "hideDropdown", + change = "change", + choice = "choice", + search = "search", + addItem = "addItem", + removeItem = "removeItem", + highlightItem = "highlightItem", + highlightChoice = "highlightChoice", + unhighlightItem = "unhighlightItem" +} diff --git a/public/types/src/scripts/interfaces/event-type.d.ts.map b/public/types/src/scripts/interfaces/event-type.d.ts.map deleted file mode 100644 index 4ce8a6fcc..000000000 --- a/public/types/src/scripts/interfaces/event-type.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"event-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/event-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,YAAY,GACZ,eAAe,GACf,iBAAiB,GACjB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,cAAc,GACd,iBAAiB,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/group-full.d.ts b/public/types/src/scripts/interfaces/group-full.d.ts new file mode 100644 index 000000000..ae92a62ff --- /dev/null +++ b/public/types/src/scripts/interfaces/group-full.d.ts @@ -0,0 +1,9 @@ +import { ChoiceFull } from './choice-full'; +export interface GroupFull { + id: number; + active: boolean; + disabled: boolean; + label: string; + element?: HTMLOptGroupElement; + choices: ChoiceFull[]; +} diff --git a/public/types/src/scripts/interfaces/group.d.ts b/public/types/src/scripts/interfaces/group.d.ts deleted file mode 100644 index 3fa395aac..000000000 --- a/public/types/src/scripts/interfaces/group.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Group { - id?: number; - active?: boolean; - disabled?: boolean; - value: any; -} -//# sourceMappingURL=group.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/group.d.ts.map b/public/types/src/scripts/interfaces/group.d.ts.map deleted file mode 100644 index bff19fb83..000000000 --- a/public/types/src/scripts/interfaces/group.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/group.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,KAAK;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;CACZ"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/index.d.ts b/public/types/src/scripts/interfaces/index.d.ts index 66ecddcfa..267e573eb 100644 --- a/public/types/src/scripts/interfaces/index.d.ts +++ b/public/types/src/scripts/interfaces/index.d.ts @@ -1,16 +1,14 @@ export * from './action-type'; -export * from './choice'; -export * from './choices'; +export * from './input-choice'; +export * from './input-group'; +export * from './event-choice'; export * from './class-names'; export * from './event-type'; -export * from './group'; export * from './item'; export * from './keycode-map'; -export * from './notice'; export * from './options'; export * from './passed-element'; export * from './passed-element-type'; export * from './position-options-type'; export * from './state'; export * from './types'; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/index.d.ts.map b/public/types/src/scripts/interfaces/index.d.ts.map deleted file mode 100644 index d28ff02d5..000000000 --- a/public/types/src/scripts/interfaces/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/input-choice.d.ts b/public/types/src/scripts/interfaces/input-choice.d.ts new file mode 100644 index 000000000..714826570 --- /dev/null +++ b/public/types/src/scripts/interfaces/input-choice.d.ts @@ -0,0 +1,14 @@ +import { StringUntrusted } from './string-untrusted'; +export interface InputChoice { + id?: number; + highlighted?: boolean; + labelClass?: string | Array; + labelDescription?: string; + customProperties?: Record | string; + disabled?: boolean; + active?: boolean; + label: StringUntrusted | string; + placeholder?: boolean; + selected?: boolean; + value: any; +} diff --git a/public/types/src/scripts/interfaces/input-group.d.ts b/public/types/src/scripts/interfaces/input-group.d.ts new file mode 100644 index 000000000..fae9be6b8 --- /dev/null +++ b/public/types/src/scripts/interfaces/input-group.d.ts @@ -0,0 +1,10 @@ +import { InputChoice } from './input-choice'; +import { StringUntrusted } from './string-untrusted'; +export interface InputGroup { + id?: number; + active?: boolean; + disabled?: boolean; + label?: StringUntrusted | string; + value: string; + choices: InputChoice[]; +} diff --git a/public/types/src/scripts/interfaces/item.d.ts b/public/types/src/scripts/interfaces/item.d.ts index 1d58b80a7..b164f2654 100644 --- a/public/types/src/scripts/interfaces/item.d.ts +++ b/public/types/src/scripts/interfaces/item.d.ts @@ -1,6 +1,17 @@ -import { Choice } from './choice'; -export interface Item extends Choice { - choiceId?: number; - highlighted?: boolean; +import { InputChoice } from './input-choice'; +import { InputGroup } from './input-group'; +/** + * @deprecated Use InputChoice instead + */ +export interface Item extends InputChoice { +} +/** + * @deprecated Use InputChoice instead + */ +export interface Choice extends InputChoice { +} +/** + * @deprecated Use InputGroup instead + */ +export interface Group extends InputGroup { } -//# sourceMappingURL=item.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/item.d.ts.map b/public/types/src/scripts/interfaces/item.d.ts.map deleted file mode 100644 index 15f89f151..000000000 --- a/public/types/src/scripts/interfaces/item.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,IAAK,SAAQ,MAAM;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/keycode-map.d.ts b/public/types/src/scripts/interfaces/keycode-map.d.ts index f7ece2bcf..62a59a235 100644 --- a/public/types/src/scripts/interfaces/keycode-map.d.ts +++ b/public/types/src/scripts/interfaces/keycode-map.d.ts @@ -1,12 +1,11 @@ -export interface KeyCodeMap { - BACK_KEY: 46; - DELETE_KEY: 8; - ENTER_KEY: 13; - A_KEY: 65; - ESC_KEY: 27; - UP_KEY: 38; - DOWN_KEY: 40; - PAGE_UP_KEY: 33; - PAGE_DOWN_KEY: 34; +export declare const enum KeyCodeMap { + BACK_KEY = 46, + DELETE_KEY = 8, + ENTER_KEY = 13, + A_KEY = 65, + ESC_KEY = 27, + UP_KEY = 38, + DOWN_KEY = 40, + PAGE_UP_KEY = 33, + PAGE_DOWN_KEY = 34 } -//# sourceMappingURL=keycode-map.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/keycode-map.d.ts.map b/public/types/src/scripts/interfaces/keycode-map.d.ts.map deleted file mode 100644 index 04580068d..000000000 --- a/public/types/src/scripts/interfaces/keycode-map.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"keycode-map.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/keycode-map.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,EAAE,CAAC;IACb,UAAU,EAAE,CAAC,CAAC;IACd,SAAS,EAAE,EAAE,CAAC;IACd,KAAK,EAAE,EAAE,CAAC;IACV,OAAO,EAAE,EAAE,CAAC;IACZ,MAAM,EAAE,EAAE,CAAC;IACX,QAAQ,EAAE,EAAE,CAAC;IACb,WAAW,EAAE,EAAE,CAAC;IAChB,aAAa,EAAE,EAAE,CAAC;CACnB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/notice.d.ts b/public/types/src/scripts/interfaces/notice.d.ts deleted file mode 100644 index 071f8d43c..000000000 --- a/public/types/src/scripts/interfaces/notice.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Notice { - response: boolean; - notice: string; -} -//# sourceMappingURL=notice.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/notice.d.ts.map b/public/types/src/scripts/interfaces/notice.d.ts.map deleted file mode 100644 index f2983f3a1..000000000 --- a/public/types/src/scripts/interfaces/notice.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"notice.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/notice.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/options.d.ts b/public/types/src/scripts/interfaces/options.d.ts index 0952366a5..a582eac0c 100644 --- a/public/types/src/scripts/interfaces/options.d.ts +++ b/public/types/src/scripts/interfaces/options.d.ts @@ -1,9 +1,9 @@ -import Fuse from 'fuse.js'; -import { Choices } from './choices'; -import { Choice } from './choice'; +import { IFuseOptions } from 'fuse.js'; +import { InputChoice } from './input-choice'; import { ClassNames } from './class-names'; import { PositionOptionsType } from './position-options-type'; import { Types } from './types'; +export declare const ObjectsInConfig: string[]; /** * Choices options interface * @@ -51,7 +51,7 @@ export interface Options { * * @default [] */ - items: string[] | Choice[]; + items: string[] | InputChoice[]; /** * Add choices (see terminology) to select input. * @@ -74,12 +74,31 @@ export interface Options { * description: 'Custom description about Option 2', * random: 'Another random custom property' * }, + * }, + * { + * label: 'Group 1', + * choices: [{ + * value: 'Option 3', + * label: 'Option 4', + * selected: true, + * disabled: false, + * }, + * { + * value: 'Option 2', + * label: 'Option 2', + * selected: false, + * disabled: true, + * customProperties: { + * description: 'Custom description about Option 2', + * random: 'Another random custom property' + * } + * }] * }] * ``` * * @default [] */ - choices: Choice[]; + choices: InputChoice[]; /** * The amount of choices to be rendered within the dropdown list `("-1" indicates no limit)`. This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice. * @@ -96,6 +115,36 @@ export interface Options { * @default -1 */ maxItemCount: number; + /** + * Control how the dropdown closes after making a selection for select-one or select-multiple + * + * 'auto' defaults based on backing-element type: + * select-one: true + * select-multiple: false + * + * **Input types affected:** select-one, select-multiple + * + * @default 'auto' + */ + closeDropdownOnSelect: boolean | 'auto'; + /** + * Make select-multiple with a max item count of 1 work similar to select-one does. + * Selecting an item will auto-close the dropdown and swap any existing item for the just selected choice. + * If applied to a select-one, it functions as above and not the standard select-one. + * + * **Input types affected:** select-one, select-multiple + * + * @default false + */ + singleModeForMultiSelect: boolean; + /** + * Whether a user can add choices dynamically. + * + * **Input types affected:** select-one, select-multiple + * + * @default false + */ + addChoices: boolean; /** * Whether a user can add items. * @@ -107,22 +156,53 @@ export interface Options { /** * A filter that will need to pass for a user to successfully add an item. * - * **Input types affected:** text + * **Input types affected:** text, select-one, select-multiple * - * @default null + * @default (value) => !!value && value !== '' */ addItemFilter: string | RegExp | Types.FilterFunction | null; /** * The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + * The raw non-sanitised value is passed as a 2nd argument. * - * **Input types affected:** text + * Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + * + * **Input types affected:** text, one-select, select-one, select-multiple * * @default * ``` - * (value) => `Press Enter to add "${value}"`; + * (value, valueRaw) => `Press Enter to add "${value}"`; * ``` */ addItemText: string | Types.NoticeStringFunction; + /** + * The text/icon for the remove button. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + * The raw non-sanitised value is passed as a 2nd argument. + * + * Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + * + * **Input types affected:** text, select-one, select-multiple + * + * @default + * ``` + * (value, valueRaw) => `Remove item`; + * ``` + */ + removeItemIconText: string | Types.NoticeStringFunction; + /** + * The text for the remove button's aria label. To access the item's value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + * The raw non-sanitised value is passed as a 2nd argument. + * + * Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + * + * **Input types affected:** text, select-one, select-multiple + * + * @default + * ``` + * (value, valueRaw) => `Remove item: ${value}`; + * ``` + */ + removeItemLabelText: string | Types.NoticeStringFunction; /** * Whether a user can remove items. * @@ -139,6 +219,14 @@ export interface Options { * @default false */ removeItemButton: boolean; + /** + * Align item remove button left vs right. + * + * **Input types affected:** text, select-one, select-multiple + * + * @default false + */ + removeItemButtonAlignLeft: boolean; /** * Whether a user can edit items. An item's value can be edited by pressing the backspace. * @@ -152,17 +240,25 @@ export interface Options { * If `false`, all elements (placeholder, items, etc.) will be treated as plain text. * If `true`, this can be used to perform XSS scripting attacks if you load choices from a remote source. * - * **Deprecation Warning:** This will default to `false` in a future release. - * * **Input types affected:** text, select-one, select-multiple * - * @default true + * @default false */ allowHTML: boolean; + /** + * Whether HTML should be escaped on input when `addItems` or `addChoices` is true. + * If `false`, user input will be treated as plain text. + * If `true`, this can be used to perform XSS scripting attacks if you load previously submitted choices from a remote source. + * + * **Input types affected:** text, select-one, select-multiple + * + * @default false + */ + allowHtmlUserInput: boolean; /** * Whether each inputted/chosen item should be unique. * - * **Input types affected:** text, select-multiple + * **Input types affected:** text * * @default true */ @@ -186,9 +282,7 @@ export interface Options { /** * Whether a search area should be shown. * - * @note Multiple select boxes will always show search areas. - * - * **Input types affected:** select-one + * **Input types affected:** select-one, select-multiple * * @default true */ @@ -210,7 +304,7 @@ export interface Options { */ searchFloor: number; /** - * The maximum amount of search results to show. + * The maximum amount of search results to show. `("-1" indicates no limit)` * * **Input types affected:** select-one, select-multiple * @@ -241,6 +335,10 @@ export interface Options { * @default true */ resetScrollPosition: boolean; + /** + * The shadow root for use within ShadowDom + */ + shadowRoot: ShadowRoot | null; /** * Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given. * @@ -274,7 +372,7 @@ export interface Options { * * @default sortByAlpha */ - sorter: (current: Choice, next: Choice) => number; + sorter: (current: Types.RecordToCompare, next: Types.RecordToCompare) => number; /** * Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value. * @@ -282,8 +380,7 @@ export interface Options { * * @note For single select boxes, the recommended way of adding a placeholder is as follows: * ``` - * * * * @@ -379,12 +476,16 @@ export interface Options { /** * If no duplicates are allowed, and the value already exists in the array. * + * Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + * * @default 'Only unique values can be added' */ uniqueItemText: string | Types.NoticeStringFunction; /** * The text that is shown when addItemFilter is passed and it returns false * + * Return type must be safe to insert into HTML (ie use the 1st argument which is sanitised) + * * **Input types affected:** text * * @default 'Only values matching specific conditions can be added' @@ -410,7 +511,7 @@ export interface Options { /** * Choices uses the great Fuse library for searching. You can find more options here: https://fusejs.io/api/options.html */ - fuseOptions: Fuse.IFuseOptions; + fuseOptions: IFuseOptions; /** * ID of the connected label to improve a11y. If set, aria-labelledby will be added. */ @@ -424,30 +525,30 @@ export interface Options { * * @default null */ - callbackOnInit: ((this: Choices) => void) | null; + callbackOnInit: (() => void) | null; /** * Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined here [https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067]. * * **Input types affected:** text, select-one, select-multiple * - * @note For each callback, this refers to the current instance of This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`. + * @note For each callback, `this` refers to the current instance of Choices. This can be useful if you need access to methods `(this.disable())`. * * @example * ``` * const example = new Choices(element, { - * callbackOnCreateTemplates: function (template) { + * callbackOnCreateTemplates: function (template, originalTemplates) { * var classNames = this.config.classNames; * return { * item: (data) => { * return template(` - *
+ *
* ${data.label} *
* `); * }, * choice: (data) => { * return template(` - *
0 ? 'role="treeitem"' : 'role="option"'}> + *
0 ? 'role="treeitem"' : 'role="option"'}> * ${data.label} *
* `); @@ -459,6 +560,6 @@ export interface Options { * * @default null */ - callbackOnCreateTemplates: ((template: Types.StrToEl) => void) | null; + callbackOnCreateTemplates: ((template: Types.StrToEl, escapeForTemplate: Types.EscapeForTemplateFn) => void) | null; + appendGroupInSearch: false; } -//# sourceMappingURL=options.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/options.d.ts.map b/public/types/src/scripts/interfaces/options.d.ts.map deleted file mode 100644 index 7d1f58d58..000000000 --- a/public/types/src/scripts/interfaces/options.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/options.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC;;;;;;;;GAQG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;;OAMG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;IAE7D;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEjD;;;;;;OAMG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAE1B;;;;;;OAMG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAE/B;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;;OAQG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;OAMG;IACH,QAAQ,EAAE,mBAAmB,CAAC;IAE9B;;;;;;OAMG;IACH,mBAAmB,EAAE,OAAO,CAAC;IAE7B;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;;;;;OAMG;IACH,eAAe,EAAE,OAAO,CAAC;IAEzB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAElD;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;;;OAMG;IACH,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;;;OAMG;IACH,qBAAqB,EAAE,MAAM,GAAG,QAAQ,CAAC;IAEzC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;IAE7C;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;IAE7C;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,GAAG,KAAK,CAAC,mBAAmB,CAAC;IAEhD;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEpD;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,CAAC;IAEvD;;;;;;;;;OASG;IACH,aAAa,EAAE,KAAK,CAAC,oBAAoB,CAAC;IAE1C;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;OAEG;IACH,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAExC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;;OAQG;IACH,cAAc,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,yBAAyB,EAAE,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CACvE"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/passed-element-type.d.ts b/public/types/src/scripts/interfaces/passed-element-type.d.ts index 19ca50682..492258227 100644 --- a/public/types/src/scripts/interfaces/passed-element-type.d.ts +++ b/public/types/src/scripts/interfaces/passed-element-type.d.ts @@ -1,2 +1 @@ export type PassedElementType = 'text' | 'select-one' | 'select-multiple'; -//# sourceMappingURL=passed-element-type.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/passed-element-type.d.ts.map b/public/types/src/scripts/interfaces/passed-element-type.d.ts.map deleted file mode 100644 index e49017369..000000000 --- a/public/types/src/scripts/interfaces/passed-element-type.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"passed-element-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/passed-element-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,YAAY,GAAG,iBAAiB,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/passed-element.d.ts b/public/types/src/scripts/interfaces/passed-element.d.ts index 54172d70b..a2816df51 100644 --- a/public/types/src/scripts/interfaces/passed-element.d.ts +++ b/public/types/src/scripts/interfaces/passed-element.d.ts @@ -1,17 +1,5 @@ -import { Choices } from './choices'; -import { Choice } from './choice'; -import { ClassNames } from './class-names'; -import { EventType } from './event-type'; -import { PassedElementType } from './passed-element-type'; -export interface PassedElement extends HTMLElement { - classNames: ClassNames; - element: (HTMLInputElement | HTMLSelectElement) & { - addEventListener(type: K, listener: (this: HTMLInputElement | HTMLSelectElement, ev: EventMap[K]) => void, options?: boolean | AddEventListenerOptions): void; - }; - type: PassedElementType; - isDisabled: boolean; - parentInstance: Choices; -} +import { InputChoice } from './input-choice'; +import { EventChoice } from './event-choice'; /** * Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object. */ @@ -21,15 +9,9 @@ export interface EventMap { * * **Input types affected:** text, select-one, select-multiple * - * Arguments: id, value, label, groupValue, keyCode + * Arguments: id, value, label, groupValue */ - addItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - keyCode: number; - }>; + addItem: CustomEvent; /** * Triggered each time an item is removed (programmatically or by the user). * @@ -37,12 +19,7 @@ export interface EventMap { * * Arguments: id, value, label, groupValue */ - removeItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; + removeItem: CustomEvent; /** * Triggered each time an item is highlighted. * @@ -50,12 +27,7 @@ export interface EventMap { * * Arguments: id, value, label, groupValue */ - highlightItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; + highlightItem: CustomEvent; /** * Triggered each time an item is unhighlighted. * @@ -63,12 +35,7 @@ export interface EventMap { * * Arguments: id, value, label, groupValue */ - unhighlightItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; + unhighlightItem: CustomEvent; /** * Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input. * @@ -77,7 +44,7 @@ export interface EventMap { * Arguments: choice: Choice */ choice: CustomEvent<{ - choice: Choice; + choice: InputChoice; }>; /** * Triggered each time an item is added/removed **by a user**. @@ -123,7 +90,6 @@ export interface EventMap { * Arguments: el is the choice.passedElement that was affected. */ highlightChoice: CustomEvent<{ - el: PassedElement; + el: HTMLElement; }>; } -//# sourceMappingURL=passed-element.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/passed-element.d.ts.map b/public/types/src/scripts/interfaces/passed-element.d.ts.map deleted file mode 100644 index 4798e53f4..000000000 --- a/public/types/src/scripts/interfaces/passed-element.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"passed-element.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/passed-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,CAAC,gBAAgB,GAAG,iBAAiB,CAAC,GAAG;QAEhD,gBAAgB,CAAC,CAAC,SAAS,SAAS,EAClC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CACR,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,EAC1C,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,KACZ,IAAI,EACT,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;KACT,CAAC;IACF,IAAI,EAAE,iBAAiB,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;;;;;OAMG;IACH,OAAO,EAAE,WAAW,CAAC;QACnB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,UAAU,EAAE,WAAW,CAAC;QACtB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,aAAa,EAAE,WAAW,CAAC;QACzB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,eAAe,EAAE,WAAW,CAAC;QAC3B,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAExC;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEvC;;;;;;OAMG;IACH,MAAM,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE5D;;;;;;OAMG;IACH,YAAY,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAErC;;;;;;OAMG;IACH,YAAY,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAErC;;;;;OAKG;IACH,eAAe,EAAE,WAAW,CAAC;QAAE,EAAE,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;CACrD"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/position-options-type.d.ts b/public/types/src/scripts/interfaces/position-options-type.d.ts index 694e3d26e..d23b4e6e5 100644 --- a/public/types/src/scripts/interfaces/position-options-type.d.ts +++ b/public/types/src/scripts/interfaces/position-options-type.d.ts @@ -1,2 +1 @@ export type PositionOptionsType = 'auto' | 'top' | 'bottom'; -//# sourceMappingURL=position-options-type.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/position-options-type.d.ts.map b/public/types/src/scripts/interfaces/position-options-type.d.ts.map deleted file mode 100644 index f89c21214..000000000 --- a/public/types/src/scripts/interfaces/position-options-type.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"position-options-type.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/position-options-type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/search.d.ts b/public/types/src/scripts/interfaces/search.d.ts new file mode 100644 index 000000000..4788b9863 --- /dev/null +++ b/public/types/src/scripts/interfaces/search.d.ts @@ -0,0 +1,11 @@ +export interface SearchResult { + item: T; + score: number; + rank: number; +} +export interface Searcher { + reset(): void; + isEmptyIndex(): boolean; + index(data: T[]): void; + search(needle: string): SearchResult[]; +} diff --git a/public/types/src/scripts/interfaces/state.d.ts b/public/types/src/scripts/interfaces/state.d.ts index a713d0505..31320e950 100644 --- a/public/types/src/scripts/interfaces/state.d.ts +++ b/public/types/src/scripts/interfaces/state.d.ts @@ -1,10 +1,10 @@ -import { Choice } from './choice'; -import { Group } from './group'; -import { Item } from './item'; +import { ChoiceFull } from './choice-full'; +import { GroupFull } from './group-full'; export interface State { - choices: Choice[]; - groups: Group[]; - items: Item[]; - loading: boolean; + choices: ChoiceFull[]; + groups: GroupFull[]; + items: ChoiceFull[]; } -//# sourceMappingURL=state.d.ts.map \ No newline at end of file +export type StateChangeSet = { + [K in keyof State]: boolean; +}; diff --git a/public/types/src/scripts/interfaces/state.d.ts.map b/public/types/src/scripts/interfaces/state.d.ts.map deleted file mode 100644 index 0907396d1..000000000 --- a/public/types/src/scripts/interfaces/state.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB"} \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/store.d.ts b/public/types/src/scripts/interfaces/store.d.ts new file mode 100644 index 000000000..6b928387c --- /dev/null +++ b/public/types/src/scripts/interfaces/store.d.ts @@ -0,0 +1,64 @@ +import { StateChangeSet, State } from './state'; +import { ChoiceFull } from './choice-full'; +import { GroupFull } from './group-full'; +import { ActionTypes } from './action-type'; +export interface AnyAction { + type: A; +} +export interface StateUpdate { + update: boolean; + state: T; +} +export type Reducer = (state: T, action: AnyAction) => StateUpdate; +export type StoreListener = (changes: StateChangeSet) => void; +export interface Store { + dispatch(action: AnyAction): void; + subscribe(onChange: StoreListener): void; + withTxn(func: () => void): void; + reset(): void; + get defaultState(): State; + /** + * Get store object + */ + get state(): State; + /** + * Get items from store + */ + get items(): ChoiceFull[]; + /** + * Get highlighted items from store + */ + get highlightedActiveItems(): ChoiceFull[]; + /** + * Get choices from store + */ + get choices(): ChoiceFull[]; + /** + * Get active choices from store + */ + get activeChoices(): ChoiceFull[]; + /** + * Get choices that can be searched (excluding placeholders) + */ + get searchableChoices(): ChoiceFull[]; + /** + * Get groups from store + */ + get groups(): GroupFull[]; + /** + * Get active groups from store + */ + get activeGroups(): GroupFull[]; + /** + * Get loading state from store + */ + inTxn(): boolean; + /** + * Get single choice by it's ID + */ + getChoiceById(id: number): ChoiceFull | undefined; + /** + * Get group by group id + */ + getGroupById(id: number): GroupFull | undefined; +} diff --git a/public/types/src/scripts/interfaces/string-pre-escaped.d.ts b/public/types/src/scripts/interfaces/string-pre-escaped.d.ts new file mode 100644 index 000000000..2f8ed2c9b --- /dev/null +++ b/public/types/src/scripts/interfaces/string-pre-escaped.d.ts @@ -0,0 +1,3 @@ +export interface StringPreEscaped { + readonly trusted: string; +} diff --git a/public/types/src/scripts/interfaces/string-untrusted.d.ts b/public/types/src/scripts/interfaces/string-untrusted.d.ts new file mode 100644 index 000000000..983c91ee2 --- /dev/null +++ b/public/types/src/scripts/interfaces/string-untrusted.d.ts @@ -0,0 +1,4 @@ +export interface StringUntrusted { + readonly escaped: string; + readonly raw: string; +} diff --git a/public/types/src/scripts/interfaces/templates.d.ts b/public/types/src/scripts/interfaces/templates.d.ts new file mode 100644 index 000000000..6e19e674d --- /dev/null +++ b/public/types/src/scripts/interfaces/templates.d.ts @@ -0,0 +1,27 @@ +import { PassedElementType } from './passed-element-type'; +import { StringPreEscaped } from './string-pre-escaped'; +import { ChoiceFull } from './choice-full'; +import { GroupFull } from './group-full'; +import { Options } from './options'; +export type TemplateOptions = Pick; +export declare const NoticeTypes: { + readonly noChoices: "no-choices"; + readonly noResults: "no-results"; + readonly addChoice: "add-choice"; + readonly generic: ""; +}; +export type NoticeType = (typeof NoticeTypes)[keyof typeof NoticeTypes]; +export interface Templates { + containerOuter({ classNames: { containerOuter }, }: TemplateOptions, dir: HTMLElement['dir'], isSelectElement: boolean, isSelectOneElement: boolean, searchEnabled: boolean, passedElementType: PassedElementType, labelId: string): HTMLDivElement; + containerInner({ classNames: { containerInner } }: TemplateOptions): HTMLDivElement; + itemList({ classNames: { list, listSingle, listItems }, }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement; + placeholder({ allowHTML, classNames: { placeholder }, }: TemplateOptions, value: StringPreEscaped | string): HTMLDivElement; + item({ allowHTML, removeItemButtonAlignLeft, classNames: { item, button, highlightedState, itemSelectable, placeholder }, }: TemplateOptions, choice: ChoiceFull, removeItemButton: boolean): HTMLDivElement; + choiceList({ classNames: { list }, }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement; + choiceGroup({ allowHTML, classNames: { group, groupHeading, itemDisabled }, }: TemplateOptions, { id, label, disabled }: GroupFull): HTMLDivElement; + choice({ allowHTML, classNames: { item, itemChoice, itemSelectable, selectedState, itemDisabled, description, placeholder }, }: TemplateOptions, choice: ChoiceFull, selectText: string): HTMLDivElement; + input({ classNames: { input, inputCloned }, }: TemplateOptions, placeholderValue: string | null): HTMLInputElement; + dropdown({ classNames: { list, listDropdown } }: TemplateOptions): HTMLDivElement; + notice({ allowHTML, classNames: { item, itemChoice, addChoice, noResults, noChoices }, }: TemplateOptions, innerText: string, type: NoticeType): HTMLDivElement; + option(choice: ChoiceFull): HTMLOptionElement; +} diff --git a/public/types/src/scripts/interfaces/types.d.ts b/public/types/src/scripts/interfaces/types.d.ts index 5a2cf64ff..716ad8ff0 100644 --- a/public/types/src/scripts/interfaces/types.d.ts +++ b/public/types/src/scripts/interfaces/types.d.ts @@ -1,9 +1,15 @@ +import { StringUntrusted } from './string-untrusted'; +import { StringPreEscaped } from './string-pre-escaped'; export declare namespace Types { type StrToEl = (str: string) => HTMLElement | HTMLInputElement | HTMLOptionElement; + type EscapeForTemplateFn = (allowHTML: boolean, s: StringUntrusted | StringPreEscaped | string) => string; type StringFunction = () => string; - type NoticeStringFunction = (value: string) => string; + type NoticeStringFunction = (value: string, valueRaw: string) => string; type NoticeLimitFunction = (maxItemCount: number) => string; type FilterFunction = (value: string) => boolean; type ValueCompareFunction = (value1: string, value2: string) => boolean; + interface RecordToCompare { + value?: StringUntrusted | string; + label?: StringUntrusted | string; + } } -//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/interfaces/types.d.ts.map b/public/types/src/scripts/interfaces/types.d.ts.map deleted file mode 100644 index 4c44c1d1e..000000000 --- a/public/types/src/scripts/interfaces/types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/interfaces/types.ts"],"names":[],"mappings":"AAAA,yBAAiB,KAAK,CAAC;IACrB,KAAY,OAAO,GAAG,CACpB,GAAG,EAAE,MAAM,KACR,WAAW,GAAG,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,KAAY,cAAc,GAAG,MAAM,MAAM,CAAC;IAC1C,KAAY,oBAAoB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D,KAAY,mBAAmB,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,KAAY,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,KAAY,oBAAoB,GAAG,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,KACX,OAAO,CAAC;CACd"} \ No newline at end of file diff --git a/public/types/src/scripts/lib/choice-input.d.ts b/public/types/src/scripts/lib/choice-input.d.ts new file mode 100644 index 000000000..6953e3742 --- /dev/null +++ b/public/types/src/scripts/lib/choice-input.d.ts @@ -0,0 +1,9 @@ +import { InputChoice } from '../interfaces/input-choice'; +import { InputGroup } from '../interfaces/input-group'; +import { GroupFull } from '../interfaces/group-full'; +import { ChoiceFull } from '../interfaces/choice-full'; +type MappedInputTypeToChoiceType = T extends InputGroup ? GroupFull : ChoiceFull; +export declare const coerceBool: (arg: unknown, defaultValue?: boolean) => boolean; +export declare const stringToHtmlClass: (input: string | string[] | undefined) => string[] | undefined; +export declare const mapInputToChoice: (value: T, allowGroup: boolean) => MappedInputTypeToChoiceType; +export {}; diff --git a/public/types/src/scripts/lib/html-guard-statements.d.ts b/public/types/src/scripts/lib/html-guard-statements.d.ts new file mode 100644 index 000000000..5eeadf4f0 --- /dev/null +++ b/public/types/src/scripts/lib/html-guard-statements.d.ts @@ -0,0 +1,4 @@ +export declare const isHtmlInputElement: (e: Element) => e is HTMLInputElement; +export declare const isHtmlSelectElement: (e: Element) => e is HTMLSelectElement; +export declare const isHtmlOption: (e: Element) => e is HTMLOptionElement; +export declare const isHtmlOptgroup: (e: Element) => e is HTMLOptGroupElement; diff --git a/public/types/src/scripts/lib/utils.d.ts b/public/types/src/scripts/lib/utils.d.ts index 3eff9ed81..fd45a5f02 100644 --- a/public/types/src/scripts/lib/utils.d.ts +++ b/public/types/src/scripts/lib/utils.d.ts @@ -1,28 +1,27 @@ -import { Choice } from '../interfaces/choice'; import { EventType } from '../interfaces/event-type'; -export declare const getRandomNumber: (min: number, max: number) => number; -export declare const generateChars: (length: number) => string; +import { StringUntrusted } from '../interfaces/string-untrusted'; +import { StringPreEscaped } from '../interfaces/string-pre-escaped'; +import { ChoiceFull } from '../interfaces/choice-full'; +import { Types } from '../interfaces/types'; export declare const generateId: (element: HTMLInputElement | HTMLSelectElement, prefix: string) => string; -export declare const getType: (obj: any) => string; -export declare const isType: (type: string, obj: any) => boolean; -export declare const wrap: (element: HTMLElement, wrapper?: HTMLElement) => HTMLElement; -export declare const getAdjacentEl: (startEl: Element, selector: string, direction?: number) => Element; +export declare const getAdjacentEl: (startEl: HTMLElement, selector: string, direction?: number) => HTMLElement | null; export declare const isScrolledIntoView: (element: HTMLElement, parent: HTMLElement, direction?: number) => boolean; -export declare const sanitise: (value: string | T) => string | T; +export declare const sanitise: (value: T | StringUntrusted | StringPreEscaped | string) => T | string; export declare const strToEl: (str: string) => Element; -interface RecordToCompare { - value: string; - label?: string; -} -export declare const sortByAlpha: ({ value, label }: RecordToCompare, { value: value2, label: label2 }: RecordToCompare) => number; -export declare const sortByScore: (a: Pick, b: Pick) => number; +export declare const resolveNoticeFunction: (fn: Types.NoticeStringFunction | string, value: string) => string; +export declare const resolveStringFunction: (fn: Types.StringFunction | string) => string; +export declare const unwrapStringForRaw: (s?: StringUntrusted | StringPreEscaped | string) => string; +export declare const unwrapStringForEscaped: (s?: StringUntrusted | StringPreEscaped | string) => string; +export declare const escapeForTemplate: (allowHTML: boolean, s: StringUntrusted | StringPreEscaped | string) => string; +export declare const sortByAlpha: ({ value, label }: Types.RecordToCompare, { value: value2, label: label2 }: Types.RecordToCompare) => number; +export declare const sortByScore: (a: Pick, b: Pick) => number; +export declare const sortByRank: (a: Pick, b: Pick) => number; export declare const dispatchEvent: (element: HTMLElement, type: EventType, customArgs?: object | null) => boolean; -export declare const existsInArray: (array: any[], value: string, key?: string) => boolean; -export declare const cloneObject: (obj: object) => object; +export declare const cloneObject: (obj: T) => T; /** * Returns an array of keys present on the first but missing on the second object */ export declare const diff: (a: Record, b: Record) => string[]; -export declare const parseCustomProperties: (customProperties: any) => any; -export {}; -//# sourceMappingURL=utils.d.ts.map \ No newline at end of file +export declare const getClassNames: (ClassNames: Array | string) => Array; +export declare const getClassNamesSelector: (option: string | Array | null) => string; +export declare const parseCustomProperties: (customProperties?: string) => object | string; diff --git a/public/types/src/scripts/lib/utils.d.ts.map b/public/types/src/scripts/lib/utils.d.ts.map deleted file mode 100644 index 7c1131e29..000000000 --- a/public/types/src/scripts/lib/utils.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/lib/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD,eAAO,MAAM,eAAe,QAAS,MAAM,OAAO,MAAM,KAAG,MACZ,CAAC;AAEhD,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,MAC6B,CAAC;AAE7E,eAAO,MAAM,UAAU,YACZ,gBAAgB,GAAG,iBAAiB,UACrC,MAAM,KACb,MASF,CAAC;AAEF,eAAO,MAAM,OAAO,QAAS,GAAG,KAAG,MACe,CAAC;AAEnD,eAAO,MAAM,MAAM,SAAU,MAAM,OAAO,GAAG,KAAG,OACY,CAAC;AAE7D,eAAO,MAAM,IAAI,YACN,WAAW,YACX,WAAW,KACnB,WAUF,CAAC;AAEF,eAAO,MAAM,aAAa,YACf,OAAO,YACN,MAAM,yBAEf,OAYF,CAAC;AAEF,eAAO,MAAM,kBAAkB,YACpB,WAAW,UACZ,WAAW,yBAElB,OAkBF,CAAC;AAEF,eAAO,MAAM,QAAQ,sCAUpB,CAAC;AAEF,eAAO,MAAM,OAAO,QAAe,MAAM,KAAK,OAc1C,CAAC;AAEL,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AACD,eAAO,MAAM,WAAW,qBACI,eAAe,oCACE,eAAe,KACzD,MAKC,CAAC;AAEL,eAAO,MAAM,WAAW,MACnB,KAAK,MAAM,EAAE,OAAO,CAAC,KACrB,KAAK,MAAM,EAAE,OAAO,CAAC,KACvB,MAKF,CAAC;AAEF,eAAO,MAAM,aAAa,YACf,WAAW,QACd,SAAS,eACH,MAAM,GAAG,IAAI,KACxB,OAQF,CAAC;AAEF,eAAO,MAAM,aAAa,UACjB,GAAG,EAAE,SACL,MAAM,mBAEZ,OAOC,CAAC;AAEL,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,MACT,CAAC;AAElC;;GAEG;AACH,eAAO,MAAM,IAAI,MACZ,OAAO,MAAM,EAAE,GAAG,CAAC,KACnB,OAAO,MAAM,EAAE,GAAG,CAAC,KACrB,MAAM,EAKR,CAAC;AAEF,eAAO,MAAM,qBAAqB,6BAAuB,GAUxD,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/lib/utils.test.d.ts b/public/types/src/scripts/lib/utils.test.d.ts deleted file mode 100644 index c727c85cb..000000000 --- a/public/types/src/scripts/lib/utils.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=utils.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/lib/utils.test.d.ts.map b/public/types/src/scripts/lib/utils.test.d.ts.map deleted file mode 100644 index 58a4177e4..000000000 --- a/public/types/src/scripts/lib/utils.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/lib/utils.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/choices.d.ts b/public/types/src/scripts/reducers/choices.d.ts index bf907900a..f9485f6a4 100644 --- a/public/types/src/scripts/reducers/choices.d.ts +++ b/public/types/src/scripts/reducers/choices.d.ts @@ -1,8 +1,8 @@ -import { AddChoiceAction, FilterChoicesAction, ActivateChoicesAction, ClearChoicesAction } from '../actions/choices'; -import { AddItemAction, RemoveItemAction } from '../actions/items'; -import { Choice } from '../interfaces/choice'; -export declare const defaultState: never[]; -type ActionTypes = AddChoiceAction | FilterChoicesAction | ActivateChoicesAction | ClearChoicesAction | AddItemAction | RemoveItemAction | Record; -export default function choices(state?: Choice[], action?: ActionTypes): Choice[]; +import { State } from '../interfaces'; +import { StateUpdate } from '../interfaces/store'; +import { ChoiceActions } from '../actions/choices'; +import { ItemActions } from '../actions/items'; +type ActionTypes = ChoiceActions | ItemActions; +type StateType = State['choices']; +export default function choices(s: StateType, action: ActionTypes): StateUpdate; export {}; -//# sourceMappingURL=choices.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/choices.d.ts.map b/public/types/src/scripts/reducers/choices.d.ts.map deleted file mode 100644 index efdac490c..000000000 --- a/public/types/src/scripts/reducers/choices.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/choices.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GACZ,eAAe,GACf,mBAAmB,GACnB,qBAAqB,GACrB,kBAAkB,GAClB,aAAa,GACb,gBAAgB,GAChB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1B,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,KAAK,GAAE,MAAM,EAAiB,EAC9B,MAAM,GAAE,WAAgB,GACvB,MAAM,EAAE,CAwGV"} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/choices.test.d.ts b/public/types/src/scripts/reducers/choices.test.d.ts deleted file mode 100644 index 1b34de17b..000000000 --- a/public/types/src/scripts/reducers/choices.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=choices.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/choices.test.d.ts.map b/public/types/src/scripts/reducers/choices.test.d.ts.map deleted file mode 100644 index abcc18fae..000000000 --- a/public/types/src/scripts/reducers/choices.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"choices.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/choices.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/groups.d.ts b/public/types/src/scripts/reducers/groups.d.ts index c60ba2387..d91bf7f75 100644 --- a/public/types/src/scripts/reducers/groups.d.ts +++ b/public/types/src/scripts/reducers/groups.d.ts @@ -1,9 +1,8 @@ -import { AddGroupAction } from '../actions/groups'; -import { ClearChoicesAction } from '../actions/choices'; -import { Group } from '../interfaces/group'; +import { GroupActions } from '../actions/groups'; import { State } from '../interfaces/state'; -export declare const defaultState: never[]; -type ActionTypes = AddGroupAction | ClearChoicesAction | Record; -export default function groups(state?: Group[], action?: ActionTypes): State['groups']; +import { StateUpdate } from '../interfaces/store'; +import { ChoiceActions } from '../actions/choices'; +type ActionTypes = ChoiceActions | GroupActions; +type StateType = State['groups']; +export default function groups(s: StateType, action: ActionTypes): StateUpdate; export {}; -//# sourceMappingURL=groups.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/groups.d.ts.map b/public/types/src/scripts/reducers/groups.d.ts.map deleted file mode 100644 index df2d6b5c2..000000000 --- a/public/types/src/scripts/reducers/groups.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GAAG,cAAc,GAAG,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/E,MAAM,CAAC,OAAO,UAAU,MAAM,CAC5B,KAAK,GAAE,KAAK,EAAiB,EAC7B,MAAM,GAAE,WAAgB,GACvB,KAAK,CAAC,QAAQ,CAAC,CAwBjB"} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/groups.test.d.ts b/public/types/src/scripts/reducers/groups.test.d.ts deleted file mode 100644 index 8d44d56ef..000000000 --- a/public/types/src/scripts/reducers/groups.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=groups.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/groups.test.d.ts.map b/public/types/src/scripts/reducers/groups.test.d.ts.map deleted file mode 100644 index ea6a989c1..000000000 --- a/public/types/src/scripts/reducers/groups.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"groups.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/groups.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/index.d.ts b/public/types/src/scripts/reducers/index.d.ts deleted file mode 100644 index 212bcc132..000000000 --- a/public/types/src/scripts/reducers/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export declare const defaultState: { - groups: never[]; - items: never[]; - choices: never[]; - loading: boolean; -}; -declare const rootReducer: (passedState: any, action: any) => object; -export default rootReducer; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/index.d.ts.map b/public/types/src/scripts/reducers/index.d.ts.map deleted file mode 100644 index d95e352b2..000000000 --- a/public/types/src/scripts/reducers/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY;;;;;CAKxB,CAAC;AASF,QAAA,MAAM,WAAW,qCAA0B,MAa1C,CAAC;AAEF,eAAe,WAAW,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/index.test.d.ts b/public/types/src/scripts/reducers/index.test.d.ts deleted file mode 100644 index 121d59b38..000000000 --- a/public/types/src/scripts/reducers/index.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=index.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/index.test.d.ts.map b/public/types/src/scripts/reducers/index.test.d.ts.map deleted file mode 100644 index c182b406a..000000000 --- a/public/types/src/scripts/reducers/index.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/index.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/items.d.ts b/public/types/src/scripts/reducers/items.d.ts index 4b825f9d5..1d8700bb0 100644 --- a/public/types/src/scripts/reducers/items.d.ts +++ b/public/types/src/scripts/reducers/items.d.ts @@ -1,8 +1,8 @@ -import { AddItemAction, RemoveItemAction, HighlightItemAction } from '../actions/items'; -import { Item } from '../interfaces/item'; +import { ItemActions } from '../actions/items'; import { State } from '../interfaces/state'; -export declare const defaultState: never[]; -type ActionTypes = AddItemAction | RemoveItemAction | HighlightItemAction | Record; -export default function items(state?: Item[], action?: ActionTypes): State['items']; +import { ChoiceActions } from '../actions/choices'; +import { StateUpdate } from '../interfaces/store'; +type ActionTypes = ChoiceActions | ItemActions; +type StateType = State['items']; +export default function items(s: StateType, action: ActionTypes): StateUpdate; export {}; -//# sourceMappingURL=items.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/items.d.ts.map b/public/types/src/scripts/reducers/items.d.ts.map deleted file mode 100644 index 517343b94..000000000 --- a/public/types/src/scripts/reducers/items.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/items.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,SAAK,CAAC;AAE/B,KAAK,WAAW,GACZ,aAAa,GACb,gBAAgB,GAChB,mBAAmB,GACnB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1B,MAAM,CAAC,OAAO,UAAU,KAAK,CAC3B,KAAK,GAAE,IAAI,EAAiB,EAC5B,MAAM,GAAE,WAAgB,GACvB,KAAK,CAAC,OAAO,CAAC,CA0DhB"} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/items.test.d.ts b/public/types/src/scripts/reducers/items.test.d.ts deleted file mode 100644 index e81d58b44..000000000 --- a/public/types/src/scripts/reducers/items.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=items.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/items.test.d.ts.map b/public/types/src/scripts/reducers/items.test.d.ts.map deleted file mode 100644 index 641925713..000000000 --- a/public/types/src/scripts/reducers/items.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"items.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/items.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/loading.d.ts b/public/types/src/scripts/reducers/loading.d.ts deleted file mode 100644 index b5518f0eb..000000000 --- a/public/types/src/scripts/reducers/loading.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SetIsLoadingAction } from '../actions/misc'; -import { State } from '../interfaces/state'; -export declare const defaultState = false; -type ActionTypes = SetIsLoadingAction | Record; -declare const general: (state?: boolean, action?: ActionTypes) => State['loading']; -export default general; -//# sourceMappingURL=loading.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/loading.d.ts.map b/public/types/src/scripts/reducers/loading.d.ts.map deleted file mode 100644 index 34945a609..000000000 --- a/public/types/src/scripts/reducers/loading.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"loading.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/loading.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAE5C,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,KAAK,WAAW,GAAG,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE9D,QAAA,MAAM,OAAO,6BAEH,WAAW,KAClB,KAAK,CAAC,SAAS,CAUjB,CAAC;AAEF,eAAe,OAAO,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/reducers/loading.test.d.ts b/public/types/src/scripts/reducers/loading.test.d.ts deleted file mode 100644 index b1d0de1d6..000000000 --- a/public/types/src/scripts/reducers/loading.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=loading.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/reducers/loading.test.d.ts.map b/public/types/src/scripts/reducers/loading.test.d.ts.map deleted file mode 100644 index 1f2efe5ce..000000000 --- a/public/types/src/scripts/reducers/loading.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"loading.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/reducers/loading.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/search/fuse.d.ts b/public/types/src/scripts/search/fuse.d.ts new file mode 100644 index 000000000..53134d21a --- /dev/null +++ b/public/types/src/scripts/search/fuse.d.ts @@ -0,0 +1,14 @@ +import { default as FuseFull, IFuseOptions } from 'fuse.js'; +import { default as FuseBasic } from 'fuse.js/basic'; +import { Options } from '../interfaces/options'; +import { Searcher, SearchResult } from '../interfaces/search'; +export declare class SearchByFuse implements Searcher { + _fuseOptions: IFuseOptions; + _haystack: T[]; + _fuse: FuseFull | FuseBasic | undefined; + constructor(config: Options); + index(data: T[]): void; + reset(): void; + isEmptyIndex(): boolean; + search(needle: string): SearchResult[]; +} diff --git a/public/types/src/scripts/search/index.d.ts b/public/types/src/scripts/search/index.d.ts new file mode 100644 index 000000000..ebc42d6fe --- /dev/null +++ b/public/types/src/scripts/search/index.d.ts @@ -0,0 +1,3 @@ +import { Options } from '../interfaces'; +import { Searcher } from '../interfaces/search'; +export declare function getSearcher(config: Options): Searcher; diff --git a/public/types/src/scripts/search/prefix-filter.d.ts b/public/types/src/scripts/search/prefix-filter.d.ts new file mode 100644 index 000000000..e0868f381 --- /dev/null +++ b/public/types/src/scripts/search/prefix-filter.d.ts @@ -0,0 +1,11 @@ +import { Options } from '../interfaces'; +import { Searcher, SearchResult } from '../interfaces/search'; +export declare class SearchByPrefixFilter implements Searcher { + _fields: string[]; + _haystack: T[]; + constructor(config: Options); + index(data: T[]): void; + reset(): void; + isEmptyIndex(): boolean; + search(_needle: string): SearchResult[]; +} diff --git a/public/types/src/scripts/store/store.d.ts b/public/types/src/scripts/store/store.d.ts index b6ff41204..fe16bf86b 100644 --- a/public/types/src/scripts/store/store.d.ts +++ b/public/types/src/scripts/store/store.d.ts @@ -1,74 +1,57 @@ -import { Store as IStore, AnyAction } from 'redux'; -import { Choice } from '../interfaces/choice'; -import { Group } from '../interfaces/group'; -import { Item } from '../interfaces/item'; -import { State } from '../interfaces/state'; -export default class Store { - _store: IStore; - constructor(); - /** - * Subscribe store to function call (wrapped Redux method) - */ - subscribe(onChange: () => void): void; - /** - * Dispatch event to store (wrapped Redux method) - */ +import { AnyAction, Store as IStore, StoreListener } from '../interfaces/store'; +import { StateChangeSet, State } from '../interfaces/state'; +import { ChoiceFull } from '../interfaces/choice-full'; +import { GroupFull } from '../interfaces/group-full'; +export default class Store implements IStore { + _state: State; + _listeners: StoreListener[]; + _txn: number; + _changeSet?: StateChangeSet; + get defaultState(): State; + changeSet(init: boolean): StateChangeSet; + reset(): void; + subscribe(onChange: StoreListener): void; dispatch(action: AnyAction): void; + withTxn(func: () => void): void; /** - * Get store object (wrapping Redux method) + * Get store object */ get state(): State; /** * Get items from store */ - get items(): Item[]; - /** - * Get active items from store - */ - get activeItems(): Item[]; + get items(): ChoiceFull[]; /** * Get highlighted items from store */ - get highlightedActiveItems(): Item[]; + get highlightedActiveItems(): ChoiceFull[]; /** * Get choices from store */ - get choices(): Choice[]; + get choices(): ChoiceFull[]; /** * Get active choices from store */ - get activeChoices(): Choice[]; - /** - * Get selectable choices from store - */ - get selectableChoices(): Choice[]; + get activeChoices(): ChoiceFull[]; /** * Get choices that can be searched (excluding placeholders) */ - get searchableChoices(): Choice[]; - /** - * Get placeholder choice from store - */ - get placeholderChoice(): Choice | undefined; + get searchableChoices(): ChoiceFull[]; /** * Get groups from store */ - get groups(): Group[]; + get groups(): GroupFull[]; /** * Get active groups from store */ - get activeGroups(): Group[]; - /** - * Get loading state from store - */ - isLoading(): boolean; + get activeGroups(): GroupFull[]; + inTxn(): boolean; /** * Get single choice by it's ID */ - getChoiceById(id: string): Choice | undefined; + getChoiceById(id: number): ChoiceFull | undefined; /** * Get group by group id */ - getGroupById(id: number): Group | undefined; + getGroupById(id: number): GroupFull | undefined; } -//# sourceMappingURL=store.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/store/store.d.ts.map b/public/types/src/scripts/store/store.d.ts.map deleted file mode 100644 index 32a519e7b..000000000 --- a/public/types/src/scripts/store/store.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/store/store.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,IAAI,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAG5C,MAAM,CAAC,OAAO,OAAO,KAAK;IACxB,MAAM,EAAE,MAAM,CAAC;;IAUf;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAIrC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIjC;;OAEG;IACH,IAAI,KAAK,IAAI,KAAK,CAEjB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,IAAI,EAAE,CAElB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,IAAI,EAAE,CAExB;IAED;;OAEG;IACH,IAAI,sBAAsB,IAAI,IAAI,EAAE,CAEnC;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,MAAM,EAAE,CAEtB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,EAAE,CAE5B;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAEhC;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,EAAE,CAIhC;IAED;;OAEG;IACH,IAAI,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAI1C;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,KAAK,EAAE,CAEpB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,KAAK,EAAE,CAW1B;IAED;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;OAEG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;CAG5C"} \ No newline at end of file diff --git a/public/types/src/scripts/store/store.test.d.ts b/public/types/src/scripts/store/store.test.d.ts deleted file mode 100644 index 6ffd6f9ed..000000000 --- a/public/types/src/scripts/store/store.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=store.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/store/store.test.d.ts.map b/public/types/src/scripts/store/store.test.d.ts.map deleted file mode 100644 index fa49f4c70..000000000 --- a/public/types/src/scripts/store/store.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../../../src/scripts/store/store.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/public/types/src/scripts/templates.d.ts b/public/types/src/scripts/templates.d.ts index 03a5ebfeb..6bdc114f6 100644 --- a/public/types/src/scripts/templates.d.ts +++ b/public/types/src/scripts/templates.d.ts @@ -1,25 +1,8 @@ /** * Helpers to create HTML elements used by Choices - * Can be overridden by providing `callbackOnCreateTemplates` option + * Can be overridden by providing `callbackOnCreateTemplates` option. + * `Choices.defaults.templates` allows access to the default template methods from `callbackOnCreateTemplates` */ -import { Choice } from './interfaces/choice'; -import { Group } from './interfaces/group'; -import { Item } from './interfaces/item'; -import { PassedElementType } from './interfaces/passed-element-type'; -type TemplateOptions = Record<'classNames' | 'allowHTML', any>; -declare const templates: { - containerOuter({ classNames: { containerOuter } }: TemplateOptions, dir: HTMLElement['dir'], isSelectElement: boolean, isSelectOneElement: boolean, searchEnabled: boolean, passedElementType: PassedElementType, labelId: string): HTMLDivElement; - containerInner({ classNames: { containerInner }, }: TemplateOptions): HTMLDivElement; - itemList({ classNames: { list, listSingle, listItems } }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement; - placeholder({ allowHTML, classNames: { placeholder } }: TemplateOptions, value: string): HTMLDivElement; - item({ allowHTML, classNames: { item, button, highlightedState, itemSelectable, placeholder, }, }: TemplateOptions, { id, value, label, customProperties, active, disabled, highlighted, placeholder: isPlaceholder, }: Item, removeItemButton: boolean): HTMLDivElement; - choiceList({ classNames: { list } }: TemplateOptions, isSelectOneElement: boolean): HTMLDivElement; - choiceGroup({ allowHTML, classNames: { group, groupHeading, itemDisabled }, }: TemplateOptions, { id, value, disabled }: Group): HTMLDivElement; - choice({ allowHTML, classNames: { item, itemChoice, itemSelectable, selectedState, itemDisabled, placeholder, }, }: TemplateOptions, { id, value, label, groupId, elementId, disabled: isDisabled, selected: isSelected, placeholder: isPlaceholder, }: Choice, selectText: string): HTMLDivElement; - input({ classNames: { input, inputCloned } }: TemplateOptions, placeholderValue: string): HTMLInputElement; - dropdown({ classNames: { list, listDropdown }, }: TemplateOptions): HTMLDivElement; - notice({ allowHTML, classNames: { item, itemChoice, noResults, noChoices }, }: TemplateOptions, innerText: string, type?: 'no-choices' | 'no-results' | ''): HTMLDivElement; - option({ label, value, customProperties, active, disabled, }: Item): HTMLOptionElement; -}; +import { Templates as TemplatesInterface } from './interfaces/templates'; +declare const templates: TemplatesInterface; export default templates; -//# sourceMappingURL=templates.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/templates.d.ts.map b/public/types/src/scripts/templates.d.ts.map deleted file mode 100644 index ac8b7018b..000000000 --- a/public/types/src/scripts/templates.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../../src/scripts/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGrE,KAAK,eAAe,GAAG,MAAM,CAAC,YAAY,GAAG,WAAW,EAAE,GAAG,CAAC,CAAC;AAE/D,QAAA,MAAM,SAAS;uDAEyB,eAAe,OAC9C,WAAW,CAAC,KAAK,CAAC,mBACN,OAAO,sBACJ,OAAO,iBACZ,OAAO,qBACH,iBAAiB,WAC3B,MAAM,GACd,cAAc;wDAiCd,eAAe,GAAG,cAAc;8DAOgB,eAAe,sBAC5C,OAAO,GAC1B,cAAc;4DAO6B,eAAe,SACpD,MAAM,GACZ,cAAc;uGAiBZ,eAAe,sGAUf,IAAI,oBACW,OAAO,GACxB,cAAc;yCAmDW,eAAe,sBACrB,OAAO,GAC1B,cAAc;mFAiBZ,eAAe,2BACO,KAAK,GAC7B,cAAc;wHAsCZ,eAAe,qHAUf,MAAM,cACG,MAAM,GACjB,cAAc;kDAqCyB,eAAe,oBACrC,MAAM,GACvB,gBAAgB;sDAmBhB,eAAe,GAAG,cAAc;mFAa9B,eAAe,aACP,MAAM,SACX,YAAY,GAAG,YAAY,GAAG,EAAE,GACrC,cAAc;kEAqBd,IAAI,GAAG,iBAAiB;CAW5B,CAAC;AAEF,eAAe,SAAS,CAAC"} \ No newline at end of file diff --git a/public/types/src/scripts/templates.test.d.ts b/public/types/src/scripts/templates.test.d.ts deleted file mode 100644 index ba2e643a9..000000000 --- a/public/types/src/scripts/templates.test.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export {}; -//# sourceMappingURL=templates.test.d.ts.map \ No newline at end of file diff --git a/public/types/src/scripts/templates.test.d.ts.map b/public/types/src/scripts/templates.test.d.ts.map deleted file mode 100644 index d7d77ed38..000000000 --- a/public/types/src/scripts/templates.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../../../src/scripts/templates.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/lint-staged.config.js b/scripts/lint-staged.config.js similarity index 100% rename from lint-staged.config.js rename to scripts/lint-staged.config.js diff --git a/scripts/rollup.config.mjs b/scripts/rollup.config.mjs new file mode 100644 index 000000000..392f3f433 --- /dev/null +++ b/scripts/rollup.config.mjs @@ -0,0 +1,205 @@ +import replace from '@rollup/plugin-replace'; +import nodeResolve from'@rollup/plugin-node-resolve'; +import babel from '@rollup/plugin-babel'; +import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import * as fs from 'node:fs'; +import server from './server.mjs'; + +// @ts-ignore +const pckg = require('../package.json'); + +const buildFeatures = { + CHOICES_SEARCH_FUSE: "full", // "basic" / "null" + CHOICES_CAN_USE_DOM: "1", // "0" +} + +// Allow the following to manual set feature flags; +// npm run js:build-dev -- --environment SEARCH_FUSE:null +let defaultBuild = {}; +Object.keys(buildFeatures).forEach((k) => { + defaultBuild[k] = process.env[k] || buildFeatures[k]; +}) + +const builds = [ + { + name: ".", + features: defaultBuild + }, + { + name: "search-basic", + features: { + ...buildFeatures, + CHOICES_SEARCH_FUSE: "basic" + } + }, + { + name: "search-prefix", + features: { + ...buildFeatures, + CHOICES_SEARCH_FUSE: "null" + } + }, +]; + +const outputTypes = { + js : { + prefix: 'iife', + ext: 'js', + format: 'iife', + default: false, + }, + umd : { + ext: 'js', + format: 'umd', + default: true, + }, + cjs : { + ext: 'cjs', + format: 'cjs', + default: false, + }, + mjs : { + ext: 'mjs', + format: 'es', + default: true, + }, +}; + +const OUTPUT_TYPES = (process.env.OUTPUT_TYPES || Object.keys(outputTypes).filter(k => outputTypes[k].default).join(',')).split(',') + +const FILENAME = 'choices' +const VERSION = process.env.VERSION || pckg.version +const AUTHOR = pckg.author +const HOMEPAGE = pckg.homepage +const banner = `/*! choices.js v${VERSION} | © ${new Date().getFullYear()} ${AUTHOR} | ${HOMEPAGE} */\n` + +const withDeclarations = !!process.env.WITH_D_TS_FILES; +if (withDeclarations) { + [ + 'public/types/src', + 'public/assets/scripts', + ].forEach((p) => { + if (fs.existsSync(p)) { + fs.rmSync(p, { recursive: true }); + } + + fs.mkdirSync(p, { recursive: true }); + }); +} + +const candidateBuilds = process.env.TARGET + ? builds.filter((build) => build.name === process.env.TARGET) + : builds; + +const suffix = (s, suffix) => { + return s + (suffix ? '.' + suffix : '') +} + +function genConfig(buildConfig) { + // built-in vars + const vars = { + __VERSION__: VERSION, + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify('production') + } + + if ('features' in buildConfig) { + Object.keys(buildConfig.features).forEach((key) => { + if (buildConfig.features[key] === 'undefined' || buildConfig.features[key] === 'null') { + vars[`process.env.${key}`] = 'false'; + } else { + vars[`process.env.${key}`] = JSON.stringify(buildConfig.features[key]) + } + }) + } + + const config = { + input: 'src/entry.js', + plugins: [ + nodeResolve(), + typescript({ + tsconfig: 'src/tsconfig.json', + // https://github.com/rollup/plugins/issues/1495 + // @rollup/plugin-typescript just doesn't want to reliably generate .d.ts files when "composite" is true, so just copy the tsconfig.json definition around + // Additionally tsc no longer accepts relative directories which escape declarationDir + "declaration": withDeclarations, + // declarationDir in src/tsconfig.json with a value of "./" magically maps to "./src" here... + "declarationDir": withDeclarations ? "./src" : undefined, + "declarationMap": false, + }), + replace(vars) + ], + output: [], + } + + const output = [false, true]; + output.forEach((minify) => { + OUTPUT_TYPES.forEach((t) => { + const type = outputTypes[t]; + if (!type) { + return; + } + + if (minify && type.format === 'es') { + return; + } + + let f = `public/assets/scripts/${FILENAME}`; + + f = suffix(f, buildConfig.name !== '.' ? buildConfig.name : ''); + f = suffix(f, type.prefix ? type.prefix : ''); + f = suffix(f, minify ? 'min' : ''); + f = suffix(f, type.ext); + + const output = { + banner, + file: f, + format: type.format, + name: 'Choices', + exports: 'default', + plugins: [] + }; + if (type.format !== 'es') { + config.plugins.push(babel({ babelHelpers: 'bundled' })) + } + + if (minify && type.format !== 'es') { + output.plugins.push(terser({ + compress: { + passes: 2, + pure_getters: true, + pure_new: true, + // unsafe: true, + } + })) + } + + config.output.push(output); + }) + }); + + if (config.output.length === 0) { + return false; + } + + return config +} + +let buildConfig = []; +candidateBuilds.forEach((build) => { + buildConfig.push(genConfig(build)); +}); + +buildConfig = buildConfig.filter((b) => !!b); +if (buildConfig.length === 0) { + console.log('No valid build targets or feature combinations.'); +} else { + const localServer = server(); + if (localServer) { + buildConfig[0].plugins.push(localServer) + } +} + +// noinspection JSUnusedGlobalSymbols +export default buildConfig; \ No newline at end of file diff --git a/scripts/server.mjs b/scripts/server.mjs new file mode 100644 index 000000000..dd5a00434 --- /dev/null +++ b/scripts/server.mjs @@ -0,0 +1,18 @@ +import dev from 'rollup-plugin-dev'; + +export default function server() { + const WATCH_HOST = process.env.WATCH_HOST; + + if (!WATCH_HOST) { + return void 0; + } + const WATCH_PORT = process.env.WATCH_PORT || 3001; + + return dev({ + dirs: ['public'], + host: WATCH_HOST, + port: WATCH_PORT, + force: !!process.env.CI, + // silent: !!process.env.CI + }); +}; \ No newline at end of file diff --git a/server.js b/server.js deleted file mode 100644 index 5f31746cd..000000000 --- a/server.js +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable no-console,global-require,import/no-extraneous-dependencies */ -const express = require('express'); -const path = require('path'); - -const PORT = process.env.PORT || 3001; -const DIST_DIR = path.resolve(__dirname, 'public'); - -const app = express(); - -if (process.env.NODE_ENV !== 'production') { - const webpack = require('webpack'); - const webpackDevMiddleware = require('webpack-dev-middleware'); - const webpackHotMiddleware = require('webpack-hot-middleware'); - const config = require('./webpack.config.dev'); - - console.log('Compiling bundle... 👷🏽'); - const compiler = webpack(config); - - app.use( - webpackDevMiddleware(compiler, { - publicPath: '/assets/scripts/', - stats: { - colors: true, - }, - }), - ); - - app.use(webpackHotMiddleware(compiler)); - - app.get('/data', (req, res) => { - // prevent endpoint from being cached - res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate'); - res.header('Expires', '-1'); - res.header('Pragma', 'no-cache'); - - const fakeData = [...new Array(50)].map((_, index) => ({ - label: `Label ${index + 1}`, - value: `Value ${index + 1}`, - })); - - setTimeout(() => { - res.status(200).send(fakeData); - }, 1000); - }); -} - -app.use(express.static(DIST_DIR)); - -const server = app.listen(PORT, err => { - if (err) { - console.log(err); - } - - console.log(`Listening at http://localhost:${server.address().port} 👂`); -}); - -process.on('SIGTERM', () => { - console.log('Shutting down server'); - if (server) { - server.close(err => { - if (err) { - console.log('Failed to shut down server'); - process.exit(1); - } - - console.log('Shut down server'); - process.exit(0); - }); - } else { - process.exit(0); - } -}); - -module.exports = server; diff --git a/src/entry.js b/src/entry.js new file mode 100644 index 000000000..d7840877b --- /dev/null +++ b/src/entry.js @@ -0,0 +1,3 @@ +import Choices from './scripts/choices'; + +export default Choices; diff --git a/src/scripts/actions/choices.test.ts b/src/scripts/actions/choices.test.ts deleted file mode 100644 index d023b2550..000000000 --- a/src/scripts/actions/choices.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { expect } from 'chai'; -import * as actions from './choices'; - -describe('actions/choices', () => { - describe('addChoice action', () => { - it('returns ADD_CHOICE action', () => { - const value = 'test'; - const label = 'test'; - const id = 1; - const groupId = 1; - const disabled = false; - const elementId = 1; - const customProperties = { test: true }; - const placeholder = true; - const keyCode = 10; - - const expectedAction: actions.AddChoiceAction = { - type: 'ADD_CHOICE', - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, - }; - - expect( - actions.addChoice({ - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, - }), - ).to.eql(expectedAction); - }); - }); - - describe('filterChoices action', () => { - it('returns FILTER_CHOICES action', () => { - const results = Array(10); - const expectedAction: actions.FilterChoicesAction = { - type: 'FILTER_CHOICES', - results, - }; - - expect(actions.filterChoices(results)).to.eql(expectedAction); - }); - }); - - describe('activateChoices action', () => { - describe('not passing active parameter', () => { - it('returns ACTIVATE_CHOICES action', () => { - const expectedAction: actions.ActivateChoicesAction = { - type: 'ACTIVATE_CHOICES', - active: true, - }; - - expect(actions.activateChoices()).to.eql(expectedAction); - }); - }); - - describe('passing active parameter', () => { - it('returns ACTIVATE_CHOICES action', () => { - const active = true; - const expectedAction: actions.ActivateChoicesAction = { - type: 'ACTIVATE_CHOICES', - active, - }; - - expect(actions.activateChoices(active)).to.eql(expectedAction); - }); - }); - }); - - describe('clearChoices action', () => { - it('returns CLEAR_CHOICES action', () => { - const expectedAction: actions.ClearChoicesAction = { - type: 'CLEAR_CHOICES', - }; - - expect(actions.clearChoices()).to.eql(expectedAction); - }); - }); -}); diff --git a/src/scripts/actions/choices.ts b/src/scripts/actions/choices.ts index db95c99a5..ec36e6bf1 100644 --- a/src/scripts/actions/choices.ts +++ b/src/scripts/actions/choices.ts @@ -1,73 +1,53 @@ -import { ACTION_TYPES } from '../constants'; -import { Choice } from '../interfaces/choice'; - -export interface AddChoiceAction { - type: typeof ACTION_TYPES.ADD_CHOICE; - id: number; - value: string; - label: string; - groupId: number; - disabled: boolean; - elementId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; +import { ChoiceFull } from '../interfaces/choice-full'; +import { ActionType } from '../interfaces'; +import { SearchResult } from '../interfaces/search'; +import { AnyAction } from '../interfaces/store'; + +export type ChoiceActions = + | AddChoiceAction + | RemoveChoiceAction + | FilterChoicesAction + | ActivateChoicesAction + | ClearChoicesAction; + +export interface AddChoiceAction extends AnyAction { + choice: ChoiceFull; } -export interface Result { - item: T; - score: number; +export interface RemoveChoiceAction extends AnyAction { + choice: ChoiceFull; } -export interface FilterChoicesAction { - type: typeof ACTION_TYPES.FILTER_CHOICES; - results: Result[]; +export interface FilterChoicesAction extends AnyAction { + results: SearchResult[]; } -export interface ActivateChoicesAction { - type: typeof ACTION_TYPES.ACTIVATE_CHOICES; +export interface ActivateChoicesAction extends AnyAction { active: boolean; } -export interface ClearChoicesAction { - type: typeof ACTION_TYPES.CLEAR_CHOICES; -} +export interface ClearChoicesAction extends AnyAction {} + +export const addChoice = (choice: ChoiceFull): AddChoiceAction => ({ + type: ActionType.ADD_CHOICE, + choice, +}); -export const addChoice = ({ - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, -}): AddChoiceAction => ({ - type: ACTION_TYPES.ADD_CHOICE, - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, +export const removeChoice = (choice: ChoiceFull): RemoveChoiceAction => ({ + type: ActionType.REMOVE_CHOICE, + choice, }); -export const filterChoices = ( - results: Result[], -): FilterChoicesAction => ({ - type: ACTION_TYPES.FILTER_CHOICES, +export const filterChoices = (results: SearchResult[]): FilterChoicesAction => ({ + type: ActionType.FILTER_CHOICES, results, }); export const activateChoices = (active = true): ActivateChoicesAction => ({ - type: ACTION_TYPES.ACTIVATE_CHOICES, + type: ActionType.ACTIVATE_CHOICES, active, }); export const clearChoices = (): ClearChoicesAction => ({ - type: ACTION_TYPES.CLEAR_CHOICES, + type: ActionType.CLEAR_CHOICES, }); diff --git a/src/scripts/actions/groups.test.ts b/src/scripts/actions/groups.test.ts deleted file mode 100644 index f56e29c27..000000000 --- a/src/scripts/actions/groups.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { expect } from 'chai'; -import * as actions from './groups'; - -describe('actions/groups', () => { - describe('addGroup action', () => { - it('returns ADD_GROUP action', () => { - const value = 'test'; - const id = 1; - const active = true; - const disabled = false; - - const expectedAction: actions.AddGroupAction = { - type: 'ADD_GROUP', - value, - id, - active, - disabled, - }; - - expect(actions.addGroup({ value, id, active, disabled })).to.eql( - expectedAction, - ); - }); - }); -}); diff --git a/src/scripts/actions/groups.ts b/src/scripts/actions/groups.ts index 9c4a24e6e..f9828424d 100644 --- a/src/scripts/actions/groups.ts +++ b/src/scripts/actions/groups.ts @@ -1,27 +1,14 @@ -import { ACTION_TYPES } from '../constants'; +import { GroupFull } from '../interfaces/group-full'; +import { ActionType } from '../interfaces'; +import { AnyAction } from '../interfaces/store'; -export interface AddGroupAction { - type: typeof ACTION_TYPES.ADD_GROUP; - id: number; - value: string; - active: boolean; - disabled: boolean; +export type GroupActions = AddGroupAction; + +export interface AddGroupAction extends AnyAction { + group: GroupFull; } -export const addGroup = ({ - value, - id, - active, - disabled, -}: { - id: number; - value: string; - active: boolean; - disabled: boolean; -}): AddGroupAction => ({ - type: ACTION_TYPES.ADD_GROUP, - value, - id, - active, - disabled, +export const addGroup = (group: GroupFull): AddGroupAction => ({ + type: ActionType.ADD_GROUP, + group, }); diff --git a/src/scripts/actions/items.test.ts b/src/scripts/actions/items.test.ts deleted file mode 100644 index 28b9ba037..000000000 --- a/src/scripts/actions/items.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { expect } from 'chai'; -import * as actions from './items'; - -describe('actions/items', () => { - describe('addItem action', () => { - it('returns ADD_ITEM action', () => { - const value = 'test'; - const label = 'test'; - const id = 1; - const choiceId = 1; - const groupId = 1; - const customProperties = { test: true }; - const placeholder = true; - const keyCode = 10; - - const expectedAction: actions.AddItemAction = { - type: 'ADD_ITEM', - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, - }; - - expect( - actions.addItem({ - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, - }), - ).to.eql(expectedAction); - }); - }); - - describe('removeItem action', () => { - it('returns REMOVE_ITEM action', () => { - const id = 1; - const choiceId = 1; - - const expectedAction: actions.RemoveItemAction = { - type: 'REMOVE_ITEM', - id, - choiceId, - }; - - expect(actions.removeItem(id, choiceId)).to.eql(expectedAction); - }); - }); - - describe('highlightItem action', () => { - it('returns HIGHLIGHT_ITEM action', () => { - const id = 1; - const highlighted = true; - - const expectedAction: actions.HighlightItemAction = { - type: 'HIGHLIGHT_ITEM', - id, - highlighted, - }; - - expect(actions.highlightItem(id, highlighted)).to.eql(expectedAction); - }); - }); -}); diff --git a/src/scripts/actions/items.ts b/src/scripts/actions/items.ts index e7ec8a497..5cd78d37c 100644 --- a/src/scripts/actions/items.ts +++ b/src/scripts/actions/items.ts @@ -1,70 +1,34 @@ -import { ACTION_TYPES } from '../constants'; +import { ChoiceFull } from '../interfaces/choice-full'; +import { ActionType } from '../interfaces'; +import { AnyAction } from '../interfaces/store'; -export interface AddItemAction { - type: typeof ACTION_TYPES.ADD_ITEM; - id: number; - value: string; - label: string; - choiceId: number; - groupId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; +export type ItemActions = AddItemAction | RemoveItemAction | HighlightItemAction; + +export interface AddItemAction extends AnyAction { + item: ChoiceFull; } -export interface RemoveItemAction { - type: typeof ACTION_TYPES.REMOVE_ITEM; - id: number; - choiceId: number; +export interface RemoveItemAction extends AnyAction { + item: ChoiceFull; } -export interface HighlightItemAction { - type: typeof ACTION_TYPES.HIGHLIGHT_ITEM; - id: number; +export interface HighlightItemAction extends AnyAction { + item: ChoiceFull; highlighted: boolean; } -export const addItem = ({ - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, -}: { - id: number; - value: string; - label: string; - choiceId: number; - groupId: number; - customProperties: object; - placeholder: boolean; - keyCode: number; -}): AddItemAction => ({ - type: ACTION_TYPES.ADD_ITEM, - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, +export const addItem = (item: ChoiceFull): AddItemAction => ({ + type: ActionType.ADD_ITEM, + item, }); -export const removeItem = (id: number, choiceId: number): RemoveItemAction => ({ - type: ACTION_TYPES.REMOVE_ITEM, - id, - choiceId, +export const removeItem = (item: ChoiceFull): RemoveItemAction => ({ + type: ActionType.REMOVE_ITEM, + item, }); -export const highlightItem = ( - id: number, - highlighted: boolean, -): HighlightItemAction => ({ - type: ACTION_TYPES.HIGHLIGHT_ITEM, - id, +export const highlightItem = (item: ChoiceFull, highlighted: boolean): HighlightItemAction => ({ + type: ActionType.HIGHLIGHT_ITEM, + item, highlighted, }); diff --git a/src/scripts/actions/misc.test.ts b/src/scripts/actions/misc.test.ts deleted file mode 100644 index b9b0d9f23..000000000 --- a/src/scripts/actions/misc.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { expect } from 'chai'; -import { State } from '../interfaces/state'; -import * as actions from './misc'; - -describe('actions/misc', () => { - describe('clearAll action', () => { - it('returns CLEAR_ALL action', () => { - const expectedAction: actions.ClearAllAction = { - type: 'CLEAR_ALL', - }; - - expect(actions.clearAll()).to.eql(expectedAction); - }); - }); - - describe('resetTo action', () => { - it('returns RESET_TO action', () => { - const state: State = { - choices: [], - items: [], - groups: [], - loading: false, - }; - const expectedAction: actions.ResetToAction = { - type: 'RESET_TO', - state, - }; - - expect(actions.resetTo(state)).to.eql(expectedAction); - }); - }); - - describe('setIsLoading action', () => { - describe('setting loading state to true', () => { - it('returns expected action', () => { - const expectedAction: actions.SetIsLoadingAction = { - type: 'SET_IS_LOADING', - isLoading: true, - }; - - expect(actions.setIsLoading(true)).to.eql(expectedAction); - }); - }); - - describe('setting loading state to false', () => { - it('returns expected action', () => { - const expectedAction: actions.SetIsLoadingAction = { - type: 'SET_IS_LOADING', - isLoading: false, - }; - - expect(actions.setIsLoading(false)).to.eql(expectedAction); - }); - }); - }); -}); diff --git a/src/scripts/actions/misc.ts b/src/scripts/actions/misc.ts deleted file mode 100644 index c667b77bd..000000000 --- a/src/scripts/actions/misc.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ACTION_TYPES } from '../constants'; -import { State } from '../interfaces/state'; - -export interface ClearAllAction { - type: typeof ACTION_TYPES.CLEAR_ALL; -} - -export interface ResetToAction { - type: typeof ACTION_TYPES.RESET_TO; - state: State; -} - -export interface SetIsLoadingAction { - type: typeof ACTION_TYPES.SET_IS_LOADING; - isLoading: boolean; -} - -export const clearAll = (): ClearAllAction => ({ - type: ACTION_TYPES.CLEAR_ALL, -}); - -export const resetTo = (state: State): ResetToAction => ({ - type: ACTION_TYPES.RESET_TO, - state, -}); - -export const setIsLoading = (isLoading: boolean): SetIsLoadingAction => ({ - type: ACTION_TYPES.SET_IS_LOADING, - isLoading, -}); diff --git a/src/scripts/choices.ts b/src/scripts/choices.ts index 36a51da40..4e4b8ef90 100644 --- a/src/scripts/choices.ts +++ b/src/scripts/choices.ts @@ -1,85 +1,89 @@ -import merge from 'deepmerge'; /* eslint-disable @typescript-eslint/no-explicit-any */ -import Fuse from 'fuse.js'; - -import { - activateChoices, - addChoice, - clearChoices, - filterChoices, - Result, -} from './actions/choices'; +import { activateChoices, addChoice, removeChoice, clearChoices, filterChoices } from './actions/choices'; import { addGroup } from './actions/groups'; import { addItem, highlightItem, removeItem } from './actions/items'; -import { clearAll, resetTo, setIsLoading } from './actions/misc'; -import { - Container, - Dropdown, - Input, - List, - WrappedInput, - WrappedSelect, -} from './components'; -import { - EVENTS, - KEY_CODES, - SELECT_MULTIPLE_TYPE, - SELECT_ONE_TYPE, - TEXT_TYPE, -} from './constants'; +import { Container, Dropdown, Input, List, WrappedInput, WrappedSelect } from './components'; +import { SELECT_MULTIPLE_TYPE, SELECT_ONE_TYPE, TEXT_TYPE } from './constants'; import { DEFAULT_CONFIG } from './defaults'; -import { Choice } from './interfaces/choice'; -import { Group } from './interfaces/group'; -import { Item } from './interfaces/item'; -import { Notice } from './interfaces/notice'; -import { Options } from './interfaces/options'; -import { PassedElement } from './interfaces/passed-element'; -import { State } from './interfaces/state'; - +import { InputChoice } from './interfaces/input-choice'; +import { InputGroup } from './interfaces/input-group'; +import { Options, ObjectsInConfig } from './interfaces/options'; +import { StateChangeSet } from './interfaces/state'; import { diff, - existsInArray, + escapeForTemplate, generateId, getAdjacentEl, - getType, + getClassNames, + getClassNamesSelector, isScrolledIntoView, - isType, - sortByScore, + resolveNoticeFunction, + resolveStringFunction, + sanitise, + sortByRank, strToEl, - parseCustomProperties, } from './lib/utils'; -import { defaultState } from './reducers'; import Store from './store/store'; -import templates from './templates'; +import { coerceBool, mapInputToChoice } from './lib/choice-input'; +import { ChoiceFull } from './interfaces/choice-full'; +import { GroupFull } from './interfaces/group-full'; +import { EventType, KeyCodeMap, PassedElementType } from './interfaces'; +import { EventChoice } from './interfaces/event-choice'; +import { NoticeType, NoticeTypes, Templates } from './interfaces/templates'; +import { isHtmlInputElement, isHtmlSelectElement } from './lib/html-guard-statements'; +import { Searcher } from './interfaces/search'; +import { getSearcher } from './search'; +// eslint-disable-next-line import/no-named-default +import { default as defaultTemplates } from './templates'; +import { canUseDom } from './interfaces/build-flags'; /** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ const IS_IE11 = + canUseDom && '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style; const USER_DEFAULTS: Partial = {}; +const parseDataSetId = (element?: HTMLElement): number | undefined => { + if (!element) { + return undefined; + } + + const { id } = element.dataset; + + return id ? parseInt(id, 10) : undefined; +}; + /** * Choices * @author Josh Johnson */ -class Choices implements Choices { +class Choices { + static version: string = '__VERSION__'; + static get defaults(): { options: Partial; - templates: typeof templates; + allOptions: Options; + templates: Templates; } { return Object.preventExtensions({ get options(): Partial { return USER_DEFAULTS; }, - get templates(): typeof templates { - return templates; + get allOptions(): Options { + return DEFAULT_CONFIG; + }, + get templates(): Templates { + return defaultTemplates; }, }); } initialised: boolean; + initialisedOK?: boolean = undefined; + config: Options; passedElement: WrappedInput | WrappedSelect; @@ -96,6 +100,8 @@ class Choices implements Choices { dropdown: Dropdown; + _elementType: PassedElementType; + _isTextElement: boolean; _isSelectOneElement: boolean; @@ -104,15 +110,17 @@ class Choices implements Choices { _isSelectElement: boolean; - _store: Store; + _hasNonChoicePlaceholder: boolean = false; - _templates: typeof templates; + _canAddUserChoices: boolean; + + _store: Store; - _initialState: State; + _templates: Templates; - _currentState: State; + _lastAddedChoiceId: number = 0; - _prevState: State; + _lastAddedGroupId: number = 0; _currentValue: string; @@ -136,110 +144,121 @@ class Choices implements Choices { itemChoice: string; }; - _presetGroups: Group[] | HTMLOptGroupElement[] | Element[]; + _presetChoices: (ChoiceFull | GroupFull)[]; - _presetOptions: Item[] | HTMLOptionElement[]; + _initialItems: string[]; - _presetChoices: Partial[]; + _searcher: Searcher; - _presetItems: Item[] | string[]; + _notice?: { + type: NoticeType; + text: string; + }; + + _docRoot: ShadowRoot | HTMLElement; constructor( - element: - | string - | Element - | HTMLInputElement - | HTMLSelectElement = '[data-choice]', + element: string | Element | HTMLInputElement | HTMLSelectElement = '[data-choice]', userConfig: Partial = {}, ) { - if (userConfig.allowHTML === undefined) { - console.warn( - 'Deprecation warning: allowHTML will default to false in a future release. To render HTML in Choices, you will need to set it to true. Setting allowHTML will suppress this message.', - ); - } - - this.config = merge.all( - [DEFAULT_CONFIG, Choices.defaults.options, userConfig], - // When merging array configs, replace with a copy of the userConfig array, - // instead of concatenating with the default array - { arrayMerge: (_, sourceArray) => [...sourceArray] }, - ); + const { defaults } = Choices; + this.config = { + ...defaults.allOptions, + ...defaults.options, + ...userConfig, + } as Options; + ObjectsInConfig.forEach((key) => { + this.config[key] = { + ...defaults.allOptions[key], + ...defaults.options[key], + ...userConfig[key], + }; + }); - const invalidConfigOptions = diff(this.config, DEFAULT_CONFIG); - if (invalidConfigOptions.length) { - console.warn( - 'Unknown config option(s) passed', - invalidConfigOptions.join(', '), - ); + const { config } = this; + if (!config.silent) { + this._validateConfig(); } - const passedElement = - typeof element === 'string' ? document.querySelector(element) : element; + const docRoot = config.shadowRoot || document.documentElement; + this._docRoot = docRoot; + const passedElement = typeof element === 'string' ? docRoot.querySelector(element) : element; if ( - !( - passedElement instanceof HTMLInputElement || - passedElement instanceof HTMLSelectElement - ) + !passedElement || + typeof passedElement !== 'object' || + !(isHtmlInputElement(passedElement) || isHtmlSelectElement(passedElement)) ) { - throw TypeError( - 'Expected one of the following types text|select-one|select-multiple', - ); + if (!passedElement && typeof element === 'string') { + throw TypeError(`Selector ${element} failed to find an element`); + } + throw TypeError(`Expected one of the following types text|select-one|select-multiple`); } - this._isTextElement = passedElement.type === TEXT_TYPE; - this._isSelectOneElement = passedElement.type === SELECT_ONE_TYPE; - this._isSelectMultipleElement = passedElement.type === SELECT_MULTIPLE_TYPE; - this._isSelectElement = - this._isSelectOneElement || this._isSelectMultipleElement; + this._elementType = passedElement.type as PassedElementType; + this._isTextElement = this._elementType === TEXT_TYPE; + if (this._isTextElement || config.maxItemCount !== 1) { + config.singleModeForMultiSelect = false; + } + if (config.singleModeForMultiSelect) { + this._elementType = SELECT_MULTIPLE_TYPE; + } + this._isSelectOneElement = this._elementType === SELECT_ONE_TYPE; + this._isSelectMultipleElement = this._elementType === SELECT_MULTIPLE_TYPE; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; + this._canAddUserChoices = (this._isTextElement && config.addItems) || (this._isSelectElement && config.addChoices); + if (!['auto', 'always'].includes(`${config.renderSelectedChoices}`)) { + config.renderSelectedChoices = 'auto'; + } - this.config.searchEnabled = - this._isSelectMultipleElement || this.config.searchEnabled; + if (config.closeDropdownOnSelect === 'auto') { + config.closeDropdownOnSelect = this._isTextElement || this._isSelectOneElement || config.singleModeForMultiSelect; + } else { + config.closeDropdownOnSelect = coerceBool(config.closeDropdownOnSelect); + } - if (!['auto', 'always'].includes(`${this.config.renderSelectedChoices}`)) { - this.config.renderSelectedChoices = 'auto'; + if (config.placeholder) { + if (config.placeholderValue) { + this._hasNonChoicePlaceholder = true; + } else if (passedElement.dataset.placeholder) { + this._hasNonChoicePlaceholder = true; + config.placeholderValue = passedElement.dataset.placeholder; + } } - if ( - userConfig.addItemFilter && - typeof userConfig.addItemFilter !== 'function' - ) { + if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { const re = - userConfig.addItemFilter instanceof RegExp - ? userConfig.addItemFilter - : new RegExp(userConfig.addItemFilter); + userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); - this.config.addItemFilter = re.test.bind(re); + config.addItemFilter = re.test.bind(re); } if (this._isTextElement) { this.passedElement = new WrappedInput({ element: passedElement as HTMLInputElement, - classNames: this.config.classNames, - delimiter: this.config.delimiter, + classNames: config.classNames, }); } else { + const selectEl = passedElement as HTMLSelectElement; this.passedElement = new WrappedSelect({ - element: passedElement as HTMLSelectElement, - classNames: this.config.classNames, - template: (data: Item): HTMLOptionElement => - this._templates.option(data), + element: selectEl, + classNames: config.classNames, + template: (data: ChoiceFull): HTMLOptionElement => this._templates.option(data), + extractPlaceholder: config.placeholder && !this._hasNonChoicePlaceholder, }); } this.initialised = false; this._store = new Store(); - this._initialState = defaultState; - this._currentState = defaultState; - this._prevState = defaultState; this._currentValue = ''; - this._canSearch = !!this.config.searchEnabled; + config.searchEnabled = (!this._isTextElement && config.searchEnabled) || this._elementType === SELECT_MULTIPLE_TYPE; + this._canSearch = config.searchEnabled; this._isScrollingOnIe = false; this._highlightPosition = 0; this._wasTap = true; this._placeholderValue = this._generatePlaceholderValue(); - this._baseId = generateId(this.passedElement.element, 'choices-'); + this._baseId = generateId(passedElement, 'choices-'); /** * setting direction in cases where it's explicitly set on passedElement @@ -247,13 +266,9 @@ class Choices implements Choices { */ this._direction = this.passedElement.dir; - if (!this._direction) { - const { direction: elementDirection } = window.getComputedStyle( - this.passedElement.element, - ); - const { direction: documentDirection } = window.getComputedStyle( - document.documentElement, - ); + if (canUseDom && !this._direction) { + const { direction: elementDirection } = window.getComputedStyle(this.passedElement.element); + const { direction: documentDirection } = window.getComputedStyle(document.documentElement); if (elementDirection !== documentDirection) { this._direction = elementDirection; } @@ -263,46 +278,13 @@ class Choices implements Choices { itemChoice: 'item-choice', }; - if (this._isSelectElement) { - // Assign preset groups from passed element - this._presetGroups = (this.passedElement as WrappedSelect).optionGroups; - // Assign preset options from passed element - this._presetOptions = (this.passedElement as WrappedSelect).options; - } - - // Assign preset choices from passed object - this._presetChoices = this.config.choices; - // Assign preset items from passed object first - this._presetItems = this.config.items; - // Add any values passed from attribute - if (this.passedElement.value && this._isTextElement) { - const splitValues: string[] = this.passedElement.value.split( - this.config.delimiter, - ); - this._presetItems = (this._presetItems as string[]).concat(splitValues); - } - // Create array of choices from option elements - if ((this.passedElement as WrappedSelect).options) { - (this.passedElement as WrappedSelect).options.forEach((option) => { - this._presetChoices.push({ - value: option.value, - label: option.innerHTML, - selected: !!option.selected, - disabled: option.disabled || option.parentNode.disabled, - placeholder: - option.value === '' || option.hasAttribute('placeholder'), - customProperties: parseCustomProperties( - option.dataset.customProperties, - ), - }); - }); - } - + this._templates = defaults.templates; this._render = this._render.bind(this); this._onFocus = this._onFocus.bind(this); this._onBlur = this._onBlur.bind(this); this._onKeyUp = this._onKeyUp.bind(this); this._onKeyDown = this._onKeyDown.bind(this); + this._onInput = this._onInput.bind(this); this._onClick = this._onClick.bind(this); this._onTouchMove = this._onTouchMove.bind(this); this._onTouchEnd = this._onTouchEnd.bind(this); @@ -317,45 +299,49 @@ class Choices implements Choices { // If element has already been initialised with Choices, fail silently if (this.passedElement.isActive) { - if (!this.config.silent) { - console.warn( - 'Trying to initialise Choices on element already initialised', - { element }, - ); + if (!config.silent) { + console.warn('Trying to initialise Choices on element already initialised', { element }); } this.initialised = true; + this.initialisedOK = false; return; } // Let's go this.init(); + // preserve the selected item list after setup for form reset + this._initialItems = this._store.items.map((choice) => choice.value); } init(): void { - if (this.initialised) { + if (this.initialised || this.initialisedOK !== undefined) { return; } + this._searcher = getSearcher(this.config); + this._loadChoices(); this._createTemplates(); this._createElements(); this._createStructure(); - this._store.subscribe(this._render); - - this._render(); - this._addEventListeners(); - - const shouldDisable = - !this.config.addItems || - this.passedElement.element.hasAttribute('disabled'); - - if (shouldDisable) { + if ( + (this._isTextElement && !this.config.addItems) || + this.passedElement.element.hasAttribute('disabled') || + !!this.passedElement.element.closest('fieldset:disabled') + ) { this.disable(); + } else { + this.enable(); + this._addEventListeners(); } + // should be triggered **after** disabled state to avoid additional re-draws + this._initStore(); + this.initialised = true; + this.initialisedOK = true; const { callbackOnInit } = this.config; // Run callback if it is a function @@ -373,121 +359,129 @@ class Choices implements Choices { this.passedElement.reveal(); this.containerOuter.unwrap(this.passedElement.element); + this._store._listeners = []; // prevents select/input value being wiped this.clearStore(); + this._stopSearch(); - if (this._isSelectElement) { - (this.passedElement as WrappedSelect).options = this._presetOptions; - } - - this._templates = templates; + this._templates = Choices.defaults.templates; this.initialised = false; + this.initialisedOK = undefined; } enable(): this { - if (this.passedElement.isDisabled) { - this.passedElement.enable(); + const { passedElement, containerOuter } = this; + + if (passedElement.isDisabled) { + passedElement.enable(); } - if (this.containerOuter.isDisabled) { + if (containerOuter.isDisabled) { this._addEventListeners(); this.input.enable(); - this.containerOuter.enable(); + containerOuter.enable(); + + this._render(); } return this; } disable(): this { - if (!this.passedElement.isDisabled) { - this.passedElement.disable(); + const { passedElement, containerOuter } = this; + + if (!passedElement.isDisabled) { + passedElement.disable(); } - if (!this.containerOuter.isDisabled) { + if (!containerOuter.isDisabled) { this._removeEventListeners(); this.input.disable(); - this.containerOuter.disable(); + containerOuter.disable(); + + this._render(); } return this; } - highlightItem(item: Item, runEvent = true): this { + highlightItem(item: InputChoice, runEvent = true): this { if (!item || !item.id) { return this; } + const choice = this._store.choices.find((c) => c.id === item.id); + if (!choice || choice.highlighted) { + return this; + } - const { id, groupId = -1, value = '', label = '' } = item; - const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; - - this._store.dispatch(highlightItem(id, true)); + this._store.dispatch(highlightItem(choice, true)); if (runEvent) { - this.passedElement.triggerEvent(EVENTS.highlightItem, { - id, - value, - label, - groupValue: group && group.value ? group.value : null, - }); + this.passedElement.triggerEvent(EventType.highlightItem, this._getChoiceForOutput(choice)); } return this; } - unhighlightItem(item: Item): this { + unhighlightItem(item: InputChoice, runEvent = true): this { if (!item || !item.id) { return this; } + const choice = this._store.choices.find((c) => c.id === item.id); + if (!choice || !choice.highlighted) { + return this; + } - const { id, groupId = -1, value = '', label = '' } = item; - const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + this._store.dispatch(highlightItem(choice, false)); - this._store.dispatch(highlightItem(id, false)); - this.passedElement.triggerEvent(EVENTS.highlightItem, { - id, - value, - label, - groupValue: group && group.value ? group.value : null, - }); + if (runEvent) { + this.passedElement.triggerEvent(EventType.highlightItem, this._getChoiceForOutput(choice)); + } return this; } highlightAll(): this { - this._store.items.forEach((item) => this.highlightItem(item)); + this._store.withTxn(() => { + this._store.items.forEach((item) => this.highlightItem(item)); + }); return this; } unhighlightAll(): this { - this._store.items.forEach((item) => this.unhighlightItem(item)); + this._store.withTxn(() => { + this._store.items.forEach((item) => this.unhighlightItem(item)); + }); return this; } removeActiveItemsByValue(value: string): this { - this._store.activeItems - .filter((item) => item.value === value) - .forEach((item) => this._removeItem(item)); + this._store.withTxn(() => { + this._store.items.filter((item) => item.value === value).forEach((item) => this._removeItem(item)); + }); return this; } removeActiveItems(excludedId: number): this { - this._store.activeItems - .filter(({ id }) => id !== excludedId) - .forEach((item) => this._removeItem(item)); + this._store.withTxn(() => { + this._store.items.filter(({ id }) => id !== excludedId).forEach((item) => this._removeItem(item)); + }); return this; } removeHighlightedItems(runEvent = false): this { - this._store.highlightedActiveItems.forEach((item) => { - this._removeItem(item); - // If this action was performed by the user - // trigger the event - if (runEvent) { - this._triggerChange(item.value); - } + this._store.withTxn(() => { + this._store.highlightedActiveItems.forEach((item) => { + this._removeItem(item); + // If this action was performed by the user + // trigger the event + if (runEvent) { + this._triggerChange(item.value); + } + }); }); return this; @@ -506,7 +500,7 @@ class Choices implements Choices { this.input.focus(); } - this.passedElement.triggerEvent(EVENTS.showDropdown, {}); + this.passedElement.triggerEvent(EventType.showDropdown); }); return this; @@ -526,46 +520,63 @@ class Choices implements Choices { this.input.blur(); } - this.passedElement.triggerEvent(EVENTS.hideDropdown, {}); + this.passedElement.triggerEvent(EventType.hideDropdown); }); return this; } - getValue(valueOnly = false): string[] | Item[] | Item | string { - const values = this._store.activeItems.reduce( - (selectedItems, item) => { - const itemValue = valueOnly ? item.value : item; - selectedItems.push(itemValue); + getValue(valueOnly = false): string[] | EventChoice[] | EventChoice | string { + const values = this._store.items.reduce((selectedItems, item) => { + const itemValue = valueOnly ? item.value : this._getChoiceForOutput(item); + selectedItems.push(itemValue); - return selectedItems; - }, - [], - ); + return selectedItems; + }, []); - return this._isSelectOneElement ? values[0] : values; + return this._isSelectOneElement || this.config.singleModeForMultiSelect ? values[0] : values; } - setValue(items: string[] | Item[]): this { - if (!this.initialised) { + setValue(items: string[] | InputChoice[]): this { + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setValue'); + return this; } - items.forEach((value) => this._setChoiceOrItem(value)); + this._store.withTxn(() => { + items.forEach((value: string | InputChoice) => { + if (value) { + this._addChoice(mapInputToChoice(value, false)); + } + }); + }); + + // @todo integrate with Store + this._searcher.reset(); return this; } setChoiceByValue(value: string | string[]): this { - if (!this.initialised || this._isTextElement) { + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoiceByValue'); + + return this; + } + if (this._isTextElement) { return this; } + this._store.withTxn(() => { + // If only one value has been passed, convert to array + const choiceValue = Array.isArray(value) ? value : [value]; - // If only one value has been passed, convert to array - const choiceValue = Array.isArray(value) ? value : [value]; + // Loop through each value and + choiceValue.forEach((val) => this._findAndSelectChoiceByValue(val)); + }); - // Loop through each value and - choiceValue.forEach((val) => this._findAndSelectChoiceByValue(val)); + // @todo integrate with Store + this._searcher.reset(); return this; } @@ -635,26 +646,23 @@ class Choices implements Choices { */ setChoices( choicesArrayOrFetcher: - | Choice[] - | Group[] - | ((instance: Choices) => Choice[] | Promise) = [], - value = 'value', + | (InputChoice | InputGroup)[] + | ((instance: Choices) => (InputChoice | InputGroup)[] | Promise<(InputChoice | InputGroup)[]>) = [], + value: string | null = 'value', label = 'label', replaceChoices = false, ): this | Promise { - if (!this.initialised) { - throw new ReferenceError( - `setChoices was called on a non-initialized instance of Choices`, - ); + if (!this.initialisedOK) { + this._warnChoicesInitFailed('setChoices'); + + return this; } if (!this._isSelectElement) { throw new TypeError(`setChoices can't be used with INPUT based Choices`); } if (typeof value !== 'string' || !value) { - throw new TypeError( - `value parameter must be a name of 'value' field in passed objects`, - ); + throw new TypeError(`value parameter must be a name of 'value' field in passed objects`); } // Clear choices if needed @@ -672,9 +680,7 @@ class Choices implements Choices { return new Promise((resolve) => requestAnimationFrame(resolve)) .then(() => this._handleLoadingState(true)) .then(() => fetcher) - .then((data: Choice[]) => - this.setChoices(data, value, label, replaceChoices), - ) + .then((data: InputChoice[]) => this.setChoices(data, value, label, replaceChoices)) .catch((err) => { if (!this.config.silent) { console.error(err); @@ -703,47 +709,134 @@ class Choices implements Choices { this.containerOuter.removeLoadingState(); - this._startLoading(); + this._store.withTxn(() => { + const isDefaultValue = value === 'value'; + const isDefaultLabel = label === 'label'; + + choicesArrayOrFetcher.forEach((groupOrChoice: InputGroup | InputChoice) => { + if ('choices' in groupOrChoice) { + let group = groupOrChoice; + if (!isDefaultLabel) { + group = { + ...group, + label: group[label], + } as InputGroup; + } - type ChoiceGroup = { - id: string; - choices: Choice[]; - }; + this._addGroup(mapInputToChoice(group, true)); + } else { + let choice = groupOrChoice; + if (!isDefaultLabel || !isDefaultValue) { + choice = { + ...choice, + value: choice[value], + label: choice[label], + } as InputChoice; + } + this._addChoice(mapInputToChoice(choice, false)); + } + }); + }); + + // @todo integrate with Store + this._searcher.reset(); + + return this; + } - choicesArrayOrFetcher.forEach((groupOrChoice: ChoiceGroup | Choice) => { - if ((groupOrChoice as ChoiceGroup).choices) { - this._addGroup({ - id: groupOrChoice.id ? parseInt(`${groupOrChoice.id}`, 10) : null, - group: groupOrChoice, - valueKey: value, - labelKey: label, + refresh(withEvents: boolean = false, selectFirstOption: boolean = false, deselectAll: boolean = false): this { + if (!this._isSelectElement) { + if (!this.config.silent) { + console.warn('refresh method can only be used on choices backed by a - * + * `; + instance = new Choices('[data-choice]', { + allowHTML: true, + searchEnabled: true, + }); + + expect(instance.config.searchEnabled).to.equal(true); + }); + it('sets searchEnabled to false', () => { + document.body.innerHTML = ` + + `; + instance = new Choices('[data-choice]', { allowHTML: true, searchEnabled: false, @@ -108,7 +124,7 @@ describe('choices', () => { `; - const inputs = document.querySelectorAll('[data-choice]'); + const inputs = document.querySelectorAll('[data-choice]'); expect(inputs.length).to.equal(3); instance = new Choices(undefined, { allowHTML: true }); @@ -121,7 +137,17 @@ describe('choices', () => { document.body.innerHTML = ``; expect(() => new Choices(undefined, { allowHTML: true })).to.throw( TypeError, - 'Expected one of the following types text|select-one|select-multiple', + 'Selector [data-choice] failed to find an element', + ); + }); + }); + + describe('when an element is not of the expected type', () => { + it('throws an error', () => { + document.body.innerHTML = `
`; + expect(() => new Choices(undefined, { allowHTML: true })).to.throw( + TypeError, + 'Selector [data-choice] failed to find an element', ); }); }); @@ -240,9 +266,7 @@ describe('choices', () => { document.body.innerHTML = `
`; - expect( - () => new Choices('[data-choice]', { allowHTML: true }), - ).to.throw( + expect(() => new Choices('[data-choice]', { allowHTML: true })).to.throw( TypeError, 'Expected one of the following types text|select-one|select-multiple', ); @@ -289,6 +313,7 @@ describe('choices', () => { addEventListenersSpy = spy(instance, '_addEventListeners'); instance.initialised = false; + instance.initialisedOK = undefined; instance.init(); }); @@ -317,8 +342,8 @@ describe('choices', () => { expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance._render); }); - it('fires initial render', () => { - expect(renderSpy.called).to.equal(true); + it('does not fire initial render with no items or choices', () => { + expect(renderSpy.called).to.equal(false); }); it('adds event listeners', () => { @@ -344,6 +369,7 @@ describe('choices', () => { describe('not already initialised', () => { beforeEach(() => { instance.initialised = false; + instance.initialisedOK = undefined; instance.destroy(); }); @@ -385,9 +411,7 @@ describe('choices', () => { it('reverts outer container', () => { expect(containerOuterUnwrapSpy.called).to.equal(true); - expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal( - instance.passedElement.element, - ); + expect(containerOuterUnwrapSpy.lastCall.args[0]).to.equal(instance.passedElement.element); }); it('clears store', () => { @@ -432,7 +456,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -492,7 +516,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -553,7 +577,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -571,33 +595,34 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); - it('opens containerOuter', (done) => { - requestAnimationFrame(() => { - expect(containerOuterOpenSpy.called).to.equal(true); - done(); - }); - }); + it('opens containerOuter', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(containerOuterOpenSpy.called).to.equal(true); + done(true); + }); + })); - it('shows dropdown with blurInput flag', (done) => { - requestAnimationFrame(() => { - expect(dropdownShowSpy.called).to.equal(true); - done(); - }); - }); + it('shows dropdown with blurInput flag', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(dropdownShowSpy.called).to.equal(true); + done(true); + }); + })); - it('triggers event on passedElement', (done) => { - requestAnimationFrame(() => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql( - EVENTS.showDropdown, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({}); - done(); - }); - }); + it('triggers event on passedElement', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.deep.equal(EventType.showDropdown); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.undefined; + done(true); + }); + })); describe('passing true focusInput flag with canSearch set to true', () => { beforeEach(() => { @@ -606,12 +631,13 @@ describe('choices', () => { output = instance.showDropdown(true); }); - it('focuses input', (done) => { - requestAnimationFrame(() => { - expect(inputFocusSpy.called).to.equal(true); - done(); - }); - }); + it('focuses input', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(inputFocusSpy.called).to.equal(true); + done(true); + }); + })); }); }); }); @@ -627,10 +653,7 @@ describe('choices', () => { containerOuterCloseSpy = spy(instance.containerOuter, 'close'); dropdownHideSpy = spy(instance.dropdown, 'hide'); inputBlurSpy = spy(instance.input, 'blur'); - inputRemoveActiveDescendantSpy = spy( - instance.input, - 'removeActiveDescendant', - ); + inputRemoveActiveDescendantSpy = spy(instance.input, 'removeActiveDescendant'); passedElementTriggerEventStub = stub(); instance.passedElement.triggerEvent = passedElementTriggerEventStub; @@ -651,7 +674,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -669,33 +692,34 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); - it('closes containerOuter', (done) => { - requestAnimationFrame(() => { - expect(containerOuterCloseSpy.called).to.equal(true); - done(); - }); - }); + it('closes containerOuter', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(containerOuterCloseSpy.called).to.equal(true); + done(true); + }); + })); - it('hides dropdown with blurInput flag', (done) => { - requestAnimationFrame(() => { - expect(dropdownHideSpy.called).to.equal(true); - done(); - }); - }); + it('hides dropdown with blurInput flag', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(dropdownHideSpy.called).to.equal(true); + done(true); + }); + })); - it('triggers event on passedElement', (done) => { - requestAnimationFrame(() => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.eql( - EVENTS.hideDropdown, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({}); - done(); - }); - }); + it('triggers event on passedElement', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.deep.equal(EventType.hideDropdown); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.undefined; + done(true); + }); + })); describe('passing true blurInput flag with canSearch set to true', () => { beforeEach(() => { @@ -704,19 +728,21 @@ describe('choices', () => { output = instance.hideDropdown(true); }); - it('removes active descendants', (done) => { - requestAnimationFrame(() => { - expect(inputRemoveActiveDescendantSpy.called).to.equal(true); - done(); - }); - }); + it('removes active descendants', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(inputRemoveActiveDescendantSpy.called).to.equal(true); + done(true); + }); + })); - it('blurs input', (done) => { - requestAnimationFrame(() => { - expect(inputBlurSpy.called).to.equal(true); - done(); - }); - }); + it('blurs input', () => + new Promise((done) => { + requestAnimationFrame(() => { + expect(inputBlurSpy.called).to.equal(true); + done(true); + }); + })); }); }); }); @@ -725,12 +751,28 @@ describe('choices', () => { let passedElementTriggerEventStub; let storeDispatchSpy; let storeGetGroupByIdStub; + let choicesStub; const groupIdValue = 'Test'; + const item: ChoiceFull = { + groupId: 0, + highlighted: false, + active: false, + disabled: false, + placeholder: false, + selected: false, + id: 1234, + value: 'Test', + label: 'Test', + score: 0, + rank: 0, + }; beforeEach(() => { + choicesStub = stub(instance._store, 'choices').get(() => [item]); passedElementTriggerEventStub = stub(); storeGetGroupByIdStub = stub().returns({ - value: groupIdValue, + id: 4321, + label: groupIdValue, }); storeDispatchSpy = spy(instance._store, 'dispatch'); @@ -739,6 +781,7 @@ describe('choices', () => { }); afterEach(() => { + choicesStub.reset(); storeDispatchSpy.restore(); instance._store.getGroupById.reset(); instance.passedElement.triggerEvent.reset(); @@ -750,7 +793,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -761,67 +804,57 @@ describe('choices', () => { }); describe('item passed', () => { - const item: Item = { - id: 1234, - value: 'Test', - label: 'Test', - }; - describe('passing truthy second paremeter', () => { beforeEach(() => { output = instance.highlightItem(item, true); }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('dispatches highlightItem action with correct arguments', () => { expect(storeDispatchSpy.called).to.equal(true); - expect(storeDispatchSpy.lastCall.args[0]).to.eql({ - type: ACTION_TYPES.HIGHLIGHT_ITEM, - id: item.id, + expect(storeDispatchSpy.lastCall.args[0]).to.deep.equal({ + type: ActionType.HIGHLIGHT_ITEM, + item, highlighted: true, }); }); + }); - describe('item with negative groupId', () => { - beforeEach(() => { - item.groupId = -1; - output = instance.highlightItem(item); - }); + describe('item with negative groupId', () => { + beforeEach(() => { + item.groupId = -1; + output = instance.highlightItem(item); + }); - it('triggers event with null groupValue', () => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal( - EVENTS.highlightItem, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({ - id: item.id, - value: item.value, - label: item.label, - groupValue: null, - }); + it('triggers event with null groupValue', () => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EventType.highlightItem); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.contains({ + id: item.id, + value: item.value, + label: item.label, + groupValue: undefined, }); }); + }); - describe('item without groupId', () => { - beforeEach(() => { - item.groupId = 1; - output = instance.highlightItem(item); - }); + describe('item without groupId', () => { + beforeEach(() => { + item.groupId = 4321; + output = instance.highlightItem(item); + }); - it('triggers event with groupValue', () => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal( - EVENTS.highlightItem, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({ - id: item.id, - value: item.value, - label: item.label, - groupValue: groupIdValue, - }); + it('triggers event with groupValue', () => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EventType.highlightItem); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.contains({ + id: item.id, + value: item.value, + label: item.label, + groupValue: groupIdValue, }); }); }); @@ -836,22 +869,38 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); }); }); }); describe('unhighlightItem', () => { + let choicesStub; let passedElementTriggerEventStub; let storeDispatchSpy; let storeGetGroupByIdStub; const groupIdValue = 'Test'; + const item: ChoiceFull = { + groupId: 0, + highlighted: true, + active: false, + disabled: false, + placeholder: false, + selected: false, + id: 1234, + value: 'Test', + label: 'Test', + score: 0, + rank: 0, + }; beforeEach(() => { + choicesStub = stub(instance._store, 'choices').get(() => [item]); passedElementTriggerEventStub = stub(); storeGetGroupByIdStub = stub().returns({ - value: groupIdValue, + id: 4321, + label: groupIdValue, }); storeDispatchSpy = spy(instance._store, 'dispatch'); @@ -860,6 +909,7 @@ describe('choices', () => { }); afterEach(() => { + choicesStub.reset(); storeDispatchSpy.restore(); instance._store.getGroupById.reset(); instance.passedElement.triggerEvent.reset(); @@ -871,7 +921,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('returns early', () => { @@ -882,74 +932,62 @@ describe('choices', () => { }); describe('item passed', () => { - const item: Item = { - id: 1234, - value: 'Test', - label: 'Test', - }; - describe('passing truthy second paremeter', () => { beforeEach(() => { output = instance.unhighlightItem(item, true); }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('dispatches highlightItem action with correct arguments', () => { expect(storeDispatchSpy.called).to.equal(true); - expect(storeDispatchSpy.lastCall.args[0]).to.eql({ - type: ACTION_TYPES.HIGHLIGHT_ITEM, - id: item.id, + expect(storeDispatchSpy.lastCall.args[0]).to.deep.contains({ + type: ActionType.HIGHLIGHT_ITEM, + item, highlighted: false, }); }); + }); - describe('item with negative groupId', () => { - beforeEach(() => { - item.groupId = -1; - output = instance.unhighlightItem(item); - }); + describe('item with negative groupId', () => { + beforeEach(() => { + item.groupId = -1; + output = instance.unhighlightItem(item); + }); - it('triggers event with null groupValue', () => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal( - EVENTS.highlightItem, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({ - id: item.id, - value: item.value, - label: item.label, - groupValue: null, - }); + it('triggers event with null groupValue', () => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EventType.highlightItem); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.contains({ + value: item.value, + label: item.label, + groupValue: undefined, }); }); + }); - describe('item without groupId', () => { - beforeEach(() => { - item.groupId = 1; - output = instance.highlightItem(item); - }); + describe('item without groupId', () => { + beforeEach(() => { + item.groupId = 4321; + output = instance.unhighlightItem(item); + }); - it('triggers event with groupValue', () => { - expect(passedElementTriggerEventStub.called).to.equal(true); - expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal( - EVENTS.highlightItem, - ); - expect(passedElementTriggerEventStub.lastCall.args[1]).to.eql({ - id: item.id, - value: item.value, - label: item.label, - groupValue: groupIdValue, - }); + it('triggers event with groupValue', () => { + expect(passedElementTriggerEventStub.called).to.equal(true); + expect(passedElementTriggerEventStub.lastCall.args[0]).to.equal(EventType.highlightItem); + expect(passedElementTriggerEventStub.lastCall.args[1]).to.contains({ + value: item.value, + label: item.label, + groupValue: groupIdValue, }); }); }); describe('passing falsey second paremeter', () => { beforeEach(() => { - output = instance.highlightItem(item, false); + output = instance.unhighlightItem(item, false); }); it("doesn't trigger event", () => { @@ -957,7 +995,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); }); }); @@ -993,7 +1031,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('highlights each item in store', () => { @@ -1033,7 +1071,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('unhighlights each item in store', () => { @@ -1058,37 +1096,12 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('dispatches clearChoices action', () => { - expect(storeDispatchStub.lastCall.args[0]).to.eql({ - type: ACTION_TYPES.CLEAR_CHOICES, - }); - }); - }); - - describe('clearStore', () => { - let storeDispatchStub; - - beforeEach(() => { - storeDispatchStub = stub(); - instance._store.dispatch = storeDispatchStub; - - output = instance.clearStore(); - }); - - afterEach(() => { - instance._store.dispatch.reset(); - }); - - it('returns this', () => { - expect(output).to.eql(instance); - }); - - it('dispatches clearAll action', () => { - expect(storeDispatchStub.lastCall.args[0]).to.eql({ - type: ACTION_TYPES.CLEAR_ALL, + expect(storeDispatchStub.lastCall.args[0]).to.deep.equal({ + type: ActionType.CLEAR_CHOICES, }); }); }); @@ -1110,7 +1123,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); describe('text element', () => { @@ -1132,6 +1145,7 @@ describe('choices', () => { instance._isSelectOneElement = true; instance._isTextElement = false; instance.config.searchEnabled = true; + instance._isSearching = true; output = instance.clearInput(); }); @@ -1147,8 +1161,8 @@ describe('choices', () => { it('dispatches activateChoices action', () => { expect(storeDispatchStub.called).to.equal(true); - expect(storeDispatchStub.lastCall.args[0]).to.eql({ - type: ACTION_TYPES.ACTIVATE_CHOICES, + expect(storeDispatchStub.lastCall.args[0]).to.deep.equal({ + type: ActionType.ACTIVATE_CHOICES, active: true, }); }); @@ -1159,10 +1173,22 @@ describe('choices', () => { describe('not initialised', () => { beforeEach(() => { instance.initialised = false; + instance.initialisedOK = undefined; }); it('should throw', () => { - expect(() => instance.setChoices(null)).Throw(ReferenceError); + expect(() => instance.setChoices(null)).Throw(TypeError); + }); + }); + + describe('initialised twice', () => { + it('throws', () => { + instance.initialised = true; + instance.initialisedOK = false; + expect(() => instance.setChoices(null)).to.throw( + TypeError, + 'setChoices called for an element which has multiple instances of Choices initialised on it', + ); }); }); @@ -1186,10 +1212,7 @@ describe('choices', () => { }); it(`should throw on function that doesn't return promise`, () => { - expect(() => instance.setChoices(() => 'boo')).to.throw( - TypeError, - /promise/i, - ); + expect(() => instance.setChoices(() => 'boo')).to.throw(TypeError, /promise/i); }); }); @@ -1200,7 +1223,7 @@ describe('choices', () => { const handleLoadingStateSpy = spy(choice, '_handleLoadingState'); let fetcherCalled = false; - const fetcher = async (inst): Promise => { + const fetcher = async (inst): Promise => { expect(inst).to.eq(choice); fetcherCalled = true; // eslint-disable-next-line no-promise-executor-return @@ -1227,35 +1250,41 @@ describe('choices', () => { }); describe('setValue', () => { - let setChoiceOrItemStub; - const values = [ - 'Value 1', - { - value: 'Value 2', - }, - ]; + let _addChoiceStub; + const value1 = 'Value 1'; + const value2 = { + value: 'Value 2', + }; + const values = [value1, value2]; beforeEach(() => { - setChoiceOrItemStub = stub(); - instance._setChoiceOrItem = setChoiceOrItemStub; + _addChoiceStub = stub(); + instance._addChoice = _addChoiceStub; }); afterEach(() => { - instance._setChoiceOrItem.reset(); + instance._addChoice.reset(); }); describe('not already initialised', () => { - beforeEach(() => { + it('throws', () => { instance.initialised = false; - output = instance.setValue(values); - }); - - it('returns this', () => { - expect(output).to.eql(instance); + instance.initialisedOK = undefined; + expect(() => instance.setValue(values)).to.throw( + TypeError, + 'setValue called on a non-initialised instance of Choices', + ); }); + }); - it('returns early', () => { - expect(setChoiceOrItemStub.called).to.equal(false); + describe('initialised twice', () => { + it('throws', () => { + instance.initialised = true; + instance.initialisedOK = false; + expect(() => instance.setValue(values)).to.throw( + TypeError, + 'setValue called for an element which has multiple instances of Choices initialised on it', + ); }); }); @@ -1266,13 +1295,15 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('sets each value', () => { - expect(setChoiceOrItemStub.callCount).to.equal(2); - expect(setChoiceOrItemStub.firstCall.args[0]).to.equal(values[0]); - expect(setChoiceOrItemStub.secondCall.args[0]).to.equal(values[1]); + expect(_addChoiceStub.callCount).to.equal(2); + expect(_addChoiceStub.firstCall.args[0]).to.be.a('object'); + expect(_addChoiceStub.secondCall.args[0]).to.be.a('object'); + expect(value1).to.equal(_addChoiceStub.firstCall.args[0].value); + expect(value2.value).to.equal(_addChoiceStub.secondCall.args[0].value); }); }); }); @@ -1290,17 +1321,24 @@ describe('choices', () => { }); describe('not already initialised', () => { - beforeEach(() => { + it('throws', () => { instance.initialised = false; - output = instance.setChoiceByValue([]); - }); - - it('returns this', () => { - expect(output).to.eql(instance); + instance.initialisedOK = undefined; + expect(() => instance.setChoiceByValue([])).to.throw( + TypeError, + 'setChoiceByValue called on a non-initialised instance of Choices', + ); }); + }); - it('returns early', () => { - expect(findAndSelectChoiceByValueStub.called).to.equal(false); + describe('initialised twice', () => { + it('throws', () => { + instance.initialised = true; + instance.initialisedOK = false; + expect(() => instance.setChoiceByValue([])).to.throw( + TypeError, + 'setChoiceByValue called for an element which has multiple instances of Choices initialised on it', + ); }); }); @@ -1318,14 +1356,12 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('sets each choice with same value', () => { expect(findAndSelectChoiceByValueStub.called).to.equal(true); - expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal( - value, - ); + expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(value); }); }); @@ -1337,17 +1373,13 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('sets each choice with same value', () => { expect(findAndSelectChoiceByValueStub.callCount).to.equal(2); - expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal( - values[0], - ); - expect(findAndSelectChoiceByValueStub.secondCall.args[0]).to.equal( - values[1], - ); + expect(findAndSelectChoiceByValueStub.firstCall.args[0]).to.equal(values[0]); + expect(findAndSelectChoiceByValueStub.secondCall.args[0]).to.equal(values[1]); }); }); }); @@ -1367,7 +1399,7 @@ describe('choices', () => { ]; beforeEach(() => { - activeItemsStub = stub(instance._store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'items').get(() => items); }); afterEach(() => { @@ -1393,7 +1425,7 @@ describe('choices', () => { }); it('returns all active item values', () => { - expect(output).to.eql(items.map((item) => item.value)); + expect(output).to.deep.equal(items.map((item) => item.value)); }); }); }); @@ -1406,7 +1438,7 @@ describe('choices', () => { }); it('returns a single active item', () => { - expect(output).to.equal(items[0]); + expect(output).to.contain.keys(Object.keys(items[0])); }); }); @@ -1417,7 +1449,9 @@ describe('choices', () => { }); it('returns all active items', () => { - expect(output).to.eql(items); + output.forEach((choice) => { + expect(choice).to.contain.keys(Object.keys(items[0])).all; + }); }); }); }); @@ -1444,7 +1478,7 @@ describe('choices', () => { beforeEach(() => { removeItemStub = stub(); - activeItemsStub = stub(instance._store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'items').get(() => items); instance._removeItem = removeItemStub; output = instance.removeActiveItemsByValue(value); @@ -1482,7 +1516,7 @@ describe('choices', () => { beforeEach(() => { removeItemStub = stub(); - activeItemsStub = stub(instance._store, 'activeItems').get(() => items); + activeItemsStub = stub(instance._store, 'items').get(() => items); instance._removeItem = removeItemStub; }); @@ -1519,6 +1553,88 @@ describe('choices', () => { }); }); + describe('removeChoice', () => { + let choicesStub; + let itemsStub; + let dispatchStub; + let triggerEventStub; + + const items = [ + { + id: 1, + value: 'Test 1', + selected: true, + }, + { + id: 2, + value: 'Test 2', + selected: false, + }, + ]; + + beforeEach(() => { + choicesStub = stub(instance._store, 'choices').get(() => items); + itemsStub = stub(instance._store, 'items').get(() => items); + triggerEventStub = stub(); + dispatchStub = stub(); + + instance._store.dispatch = dispatchStub; + instance.passedElement.triggerEvent = triggerEventStub; + }); + + afterEach(() => { + choicesStub.reset(); + itemsStub.reset(); + instance._store.dispatch.reset(); + instance.passedElement.triggerEvent.reset(); + }); + + describe('remove a selected choice from the store', () => { + beforeEach(() => { + output = instance.removeChoice('Test 1'); + }); + + it('returns this', () => { + expect(output).to.deep.equal(instance); + }); + + it('removes an active item in store', () => { + expect(instance._store.dispatch).callCount(1); + expect(instance.passedElement.triggerEvent).callCount(1); + }); + }); + + describe('remove a non-selected choice from the store', () => { + beforeEach(() => { + output = instance.removeChoice('Test 2'); + }); + + it('returns this', () => { + expect(output).to.deep.equal(instance); + }); + + it('removes a choice in store', () => { + expect(instance._store.dispatch).callCount(1); + expect(instance.passedElement.triggerEvent).callCount(0); + }); + }); + + describe('remove an non-existent choice from the store', () => { + beforeEach(() => { + output = instance.removeChoice('xxxx'); + }); + + it('returns this', () => { + expect(output).to.deep.equal(instance); + }); + + it('removes no choices from store', () => { + expect(instance._store.dispatch).callCount(0); + expect(instance.passedElement.triggerEvent).callCount(0); + }); + }); + }); + describe('removeHighlightedItems', () => { let highlightedActiveItemsStub; let removeItemStub; @@ -1536,10 +1652,7 @@ describe('choices', () => { ]; beforeEach(() => { - highlightedActiveItemsStub = stub( - instance._store, - 'highlightedActiveItems', - ).get(() => items); + highlightedActiveItemsStub = stub(instance._store, 'highlightedActiveItems').get(() => items); removeItemStub = stub(); triggerChangeStub = stub(); @@ -1559,7 +1672,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('removes each highlighted item in store', () => { @@ -1573,7 +1686,7 @@ describe('choices', () => { }); it('returns this', () => { - expect(output).to.eql(instance); + expect(output).to.deep.equal(instance); }); it('triggers event with item value', () => { @@ -1591,28 +1704,29 @@ describe('choices', () => { let containerOuterRemoveLoadingStateStub; const value = 'value'; const label = 'label'; - const choices: Choice[] = [ + const choices: InputChoice[] = [ { - id: 1, value: '1', label: 'Test 1', selected: false, disabled: false, }, { - id: 2, value: '2', label: 'Test 2', selected: false, disabled: true, }, ]; - const groups: Group[] = [ + const groups: InputGroup[] = [ { ...choices[0], choices, }, - choices[1], + { + ...choices[1], + choices: [], + }, ]; beforeEach(() => { @@ -1624,8 +1738,7 @@ describe('choices', () => { instance.clearChoices = clearChoicesStub; instance._addGroup = addGroupStub; instance._addChoice = addChoiceStub; - instance.containerOuter.removeLoadingState = - containerOuterRemoveLoadingStateStub; + instance.containerOuter.removeLoadingState = containerOuterRemoveLoadingStateStub; }); afterEach(() => { @@ -1641,9 +1754,7 @@ describe('choices', () => { }); it('throws', () => { - expect(() => - instance.setChoices(choices, value, label, false), - ).to.throw(TypeError, /input/i); + expect(() => instance.setChoices(choices, value, label, false)).to.throw(TypeError, /input/i); }); }); @@ -1654,9 +1765,7 @@ describe('choices', () => { }); it('throws', () => { - expect(() => - instance.setChoices(choices, null, 'label', false), - ).to.throw(TypeError, /value/i); + expect(() => instance.setChoices(choices, null, 'label', false)).to.throw(TypeError, /value/i); }); }); }); @@ -1674,28 +1783,29 @@ describe('choices', () => { describe('passing choices with children choices', () => { it('adds groups', () => { instance.setChoices(groups, value, label, false); - expect(addGroupStub.callCount).to.equal(1); - expect(addGroupStub.firstCall.args[0]).to.eql({ - group: groups[0], - id: groups[0].id, - valueKey: value, - labelKey: label, + expect(addGroupStub.callCount).to.equal(2); + expect(addGroupStub.firstCall.args[0]).to.contain({ + label: groups[0].label, }); }); }); + const coerceBool = (arg: unknown, defaultValue: boolean = true) => + typeof arg === 'undefined' ? defaultValue : !!arg; + describe('passing choices without children choices', () => { it('adds passed choices', () => { instance.setChoices(choices, value, label, false); expect(addChoiceStub.callCount).to.equal(2); addChoiceStub.getCalls().forEach((call, index) => { - expect(call.args[0]).to.eql({ + expect(call.args[0]).to.deep.contain({ value: choices[index][value], label: choices[index][label], - isSelected: !!choices[index].selected, - isDisabled: !!choices[index].disabled, + active: coerceBool(choices[index].active), + selected: coerceBool(choices[index].selected, false), + disabled: coerceBool(choices[index].disabled, false), customProperties: choices[index].customProperties, - placeholder: !!choices[index].placeholder, + placeholder: coerceBool(choices[index].placeholder, false), }); }); }); @@ -1736,16 +1846,14 @@ describe('choices', () => { describe('events', () => { describe('search', () => { - const choices: Choice[] = [ + const choices: InputChoice[] = [ { - id: 1, value: '1', label: 'Test 1', selected: false, disabled: false, }, { - id: 2, value: '2', label: 'Test 2', selected: false, @@ -1765,67 +1873,114 @@ describe('choices', () => { }); }); - it('details are passed', (done) => { - const query = - 'This is a query & a "test" with characters that should not be sanitised.'; - - instance.input.value = query; - instance.input.focus(); - instance.passedElement.element.addEventListener( - 'search', - (event) => { - expect(event.detail).to.eql({ - value: query, - resultCount: 0, - }); - done(); - }, - { once: true }, - ); + describe('fuse', () => { + beforeEach(() => { + process.env.CHOICES_SEARCH_FUSE = 'full'; + instance._searcher = new SearchByFuse(instance.config); + }); + it('details are passed', () => + new Promise((done) => { + const query = 'This is a query & a "test" with characters that should not be sanitised.'; - instance._onKeyUp({ target: null, keyCode: null }); - }); + instance.input.value = query; + instance.input.focus(); + instance.passedElement.element.addEventListener( + 'search', + (event) => { + expect(event.detail).to.contains({ + value: query, + resultCount: 0, + }); + done(true); + }, + { once: true }, + ); - it('uses Fuse options', (done) => { - instance.input.value = 'test'; - instance.input.focus(); - instance.passedElement.element.addEventListener( - 'search', - (event) => { - expect(event.detail.resultCount).to.eql(2); + instance._onKeyUp({ target: null, keyCode: null }); + instance._onInput({ target: null }); + })); + it('uses Fuse options', () => + new Promise((done) => { instance.config.fuseOptions.isCaseSensitive = true; instance.config.fuseOptions.minMatchCharLength = 4; + instance._searcher = new SearchByFuse(instance.config); + + instance.input.value = 'test'; + instance.input.focus(); instance.passedElement.element.addEventListener( 'search', - (eventCaseSensitive) => { - expect(eventCaseSensitive.detail.resultCount).to.eql(0); - done(); + (event) => { + expect(event.detail.resultCount).to.eql(0); + done(true); }, { once: true }, ); instance._onKeyUp({ target: null, keyCode: null }); - }, - { once: true }, - ); + instance._onInput({ target: null }); + })); + + it('is fired with a searchFloor of 0', () => + new Promise((done) => { + instance.config.searchFloor = 0; + instance.input.value = 'qwerty'; + instance.input.focus(); + instance.passedElement.element.addEventListener('search', (event) => { + expect(event.detail).to.contains({ + value: instance.input.value, + resultCount: 0, + }); + done(true); + }); - instance._onKeyUp({ target: null, keyCode: null }); + instance._onKeyUp({ target: null, keyCode: null }); + instance._onInput({ target: null }); + })); }); - it('is fired with a searchFloor of 0', (done) => { - instance.config.searchFloor = 0; - instance.input.value = ''; - instance.input.focus(); - instance.passedElement.element.addEventListener('search', (event) => { - expect(event.detail).to.eql({ - value: instance.input.value, - resultCount: 0, - }); - done(); + describe('prefix-filter', () => { + beforeEach(() => { + instance._searcher = new SearchByPrefixFilter(instance.config); }); + it('details are passed', () => + new Promise((done) => { + const query = 'This is a query & a "test" with characters that should not be sanitised.'; + + instance.input.value = query; + instance.input.focus(); + instance.passedElement.element.addEventListener( + 'search', + (event) => { + expect(event.detail).to.contains({ + value: query, + resultCount: 0, + }); + done(true); + }, + { once: true }, + ); - instance._onKeyUp({ target: null, keyCode: null }); + instance._onKeyUp({ target: null, keyCode: null }); + instance._onInput({ target: null }); + })); + + it('is fired with a searchFloor of 0', () => + new Promise((done) => { + instance.config.searchFloor = 0; + instance.input.value = 'qwerty'; + instance.input.focus(); + instance.passedElement.element.addEventListener('search', (event) => { + expect(event.detail).to.contains({ + value: instance.input.value, + resultCount: 0, + }); + done(true); + }); + + instance._onKeyUp({ target: null, keyCode: null }); + instance._onInput({ target: null }); + })); }); }); }); @@ -1833,13 +1988,19 @@ describe('choices', () => { describe('private methods', () => { describe('_createGroupsFragment', () => { let _createChoicesFragmentStub; - const choices: Choice[] = [ + const choices: ChoiceFull[] = [ { id: 1, selected: true, groupId: 1, value: 'Choice 1', label: 'Choice 1', + disabled: false, + active: false, + placeholder: false, + highlighted: false, + score: 0, + rank: 0, }, { id: 2, @@ -1847,6 +2008,12 @@ describe('choices', () => { groupId: 2, value: 'Choice 2', label: 'Choice 2', + disabled: false, + active: false, + placeholder: false, + highlighted: false, + score: 0, + rank: 0, }, { id: 3, @@ -1854,21 +2021,29 @@ describe('choices', () => { groupId: 1, value: 'Choice 3', label: 'Choice 3', + disabled: false, + active: false, + placeholder: false, + highlighted: false, + score: 0, + rank: 0, }, ]; - const groups: Group[] = [ + const groups: GroupFull[] = [ { id: 2, - value: 'Group 2', + label: 'Group 2', active: true, disabled: false, + choices: [], }, { id: 1, - value: 'Group 1', + label: 'Group 1', active: true, disabled: false, + choices: [], }, ]; @@ -1893,10 +2068,8 @@ describe('choices', () => { elementToWrapFragment.appendChild(output); expect(output).to.be.instanceOf(DocumentFragment); - expect(elementToWrapFragment.children[0]).to.eql(childElement); - expect( - elementToWrapFragment.querySelectorAll('[data-group]').length, - ).to.equal(2); + expect(elementToWrapFragment.children[0]).to.deep.equal(childElement); + expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2); }); }); @@ -1907,9 +2080,7 @@ describe('choices', () => { elementToWrapFragment.appendChild(output); expect(output).to.be.instanceOf(DocumentFragment); - expect( - elementToWrapFragment.querySelectorAll('[data-group]').length, - ).to.equal(2); + expect(elementToWrapFragment.querySelectorAll('[data-group]').length).to.equal(2); }); }); @@ -1961,31 +2132,18 @@ describe('choices', () => { expect(_createChoicesFragmentStub.called).to.equal(false); instance._createGroupsFragment(groups, choices); expect(_createChoicesFragmentStub.called).to.equal(true); - expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([ - { - id: 1, - selected: true, - groupId: 1, - value: 'Choice 1', - label: 'Choice 1', - }, - { - id: 3, - selected: false, - groupId: 1, - value: 'Choice 3', - label: 'Choice 3', - }, - ]); - expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([ - { - id: 2, - selected: false, - groupId: 2, - value: 'Choice 2', - label: 'Choice 2', - }, - ]); + expect(_createChoicesFragmentStub.firstCall.args[0][0]).to.contains({ + selected: true, + groupId: 1, + value: 'Choice 1', + label: 'Choice 1', + }); + expect(_createChoicesFragmentStub.firstCall.args[0][1]).to.contains({ + selected: false, + groupId: 1, + value: 'Choice 3', + label: 'Choice 3', + }); }); }); @@ -2000,31 +2158,24 @@ describe('choices', () => { expect(_createChoicesFragmentStub.called).to.equal(false); instance._createGroupsFragment(groups, choices); expect(_createChoicesFragmentStub.called).to.equal(true); - expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([ - { - id: 1, - selected: true, - groupId: 1, - value: 'Choice 1', - label: 'Choice 1', - }, - { - id: 3, - selected: false, - groupId: 1, - value: 'Choice 3', - label: 'Choice 3', - }, - ]); - expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([ - { - id: 2, - selected: false, - groupId: 2, - value: 'Choice 2', - label: 'Choice 2', - }, - ]); + expect(_createChoicesFragmentStub.firstCall.args[0][0]).to.deep.contains({ + selected: true, + groupId: 1, + value: 'Choice 1', + label: 'Choice 1', + }); + expect(_createChoicesFragmentStub.firstCall.args[0][1]).to.deep.contains({ + selected: false, + groupId: 1, + value: 'Choice 3', + label: 'Choice 3', + }); + expect(_createChoicesFragmentStub.secondCall.args[0][0]).to.deep.contains({ + selected: false, + groupId: 2, + value: 'Choice 2', + label: 'Choice 2', + }); }); }); @@ -2038,24 +2189,20 @@ describe('choices', () => { expect(_createChoicesFragmentStub.called).to.equal(false); instance._createGroupsFragment(groups, choices); expect(_createChoicesFragmentStub.called).to.equal(true); - expect(_createChoicesFragmentStub.firstCall.args[0]).to.eql([ - { - id: 3, - selected: false, - groupId: 1, - value: 'Choice 3', - label: 'Choice 3', - }, - ]); - expect(_createChoicesFragmentStub.secondCall.args[0]).to.eql([ - { - id: 2, - selected: false, - groupId: 2, - value: 'Choice 2', - label: 'Choice 2', - }, - ]); + expect(_createChoicesFragmentStub.firstCall.args[0][0]).to.deep.contains({ + id: 3, + selected: false, + groupId: 1, + value: 'Choice 3', + label: 'Choice 3', + }); + expect(_createChoicesFragmentStub.secondCall.args[0][0]).to.deep.contains({ + id: 2, + selected: false, + groupId: 2, + value: 'Choice 2', + label: 'Choice 2', + }); }); }); }); @@ -2098,47 +2245,12 @@ describe('choices', () => { instance._isSelectElement = false; instance.config.placeholder = true; instance.config.placeholderValue = placeholderValue; + instance._hasNonChoicePlaceholder = true; const value = instance._generatePlaceholderValue(); expect(value).to.equal(placeholderValue); }); }); - - describe('when the placeholderValue config option is not defined', () => { - describe('when the placeholder attribute is defined on the passed element', () => { - it('returns the value of the placeholder attribute', () => { - const placeholderValue = 'I am a placeholder'; - - instance._isSelectElement = false; - instance.config.placeholder = true; - instance.config.placeholderValue = undefined; - instance.passedElement.element = { - dataset: { - placeholder: placeholderValue, - }, - }; - - const value = instance._generatePlaceholderValue(); - expect(value).to.equal(placeholderValue); - }); - }); - - describe('when the placeholder attribute is not defined on the passed element', () => { - it('returns null', () => { - instance._isSelectElement = false; - instance.config.placeholder = true; - instance.config.placeholderValue = undefined; - instance.passedElement.element = { - dataset: { - placeholder: undefined, - }, - }; - - const value = instance._generatePlaceholderValue(); - expect(value).to.equal(null); - }); - }); - }); }); describe('when the placeholder config option is set to false', () => { @@ -2153,29 +2265,8 @@ describe('choices', () => { }); }); - describe('_getTemplate', () => { - describe('when passing a template key', () => { - it('returns the generated template for the given template key', () => { - const templateKey = 'test'; - const element = document.createElement('div'); - const customArg = { test: true }; - - instance._templates = { - [templateKey]: stub().returns(element), - }; - - output = instance._getTemplate(templateKey, customArg); - expect(output).to.deep.equal(element); - expect(instance._templates[templateKey]).to.have.been.calledOnceWith( - instance.config, - customArg, - ); - }); - }); - }); - describe('_onKeyDown', () => { - let activeItems; + let items; let hasItems; let hasActiveDropdown; let hasFocussedInput; @@ -2188,32 +2279,30 @@ describe('choices', () => { instance._onDirectionKey = stub(); instance._onDeleteKey = stub(); - ({ activeItems } = instance._store); - hasItems = instance.itemList.hasChildren(); + ({ items } = instance._store); + hasItems = instance.itemList.element.hasChildNodes(); hasActiveDropdown = instance.dropdown.isActive; hasFocussedInput = instance.input.isFocussed; }); describe('direction key', () => { const keyCodes = [ - KEY_CODES.UP_KEY, - KEY_CODES.DOWN_KEY, - KEY_CODES.PAGE_UP_KEY, - KEY_CODES.PAGE_DOWN_KEY, + [KeyCodeMap.UP_KEY, 'ArrowUp'], + [KeyCodeMap.DOWN_KEY, 'ArrowDown'], + [KeyCodeMap.PAGE_UP_KEY, 'PageUp'], + [KeyCodeMap.PAGE_DOWN_KEY, 'PageDown'], ]; - keyCodes.forEach((keyCode) => { + keyCodes.forEach(([keyCode, key]) => { it(`calls _onDirectionKey with the expected arguments`, () => { const event = { keyCode, + key, }; instance._onKeyDown(event); - expect(instance._onDirectionKey).to.have.been.calledWith( - event, - hasActiveDropdown, - ); + expect(instance._onDirectionKey).to.have.been.calledWith(event, hasActiveDropdown); }); }); }); @@ -2221,50 +2310,49 @@ describe('choices', () => { describe('select key', () => { it(`calls _onSelectKey with the expected arguments`, () => { const event = { - keyCode: KEY_CODES.A_KEY, + keyCode: KeyCodeMap.A_KEY, + key: 'A', }; instance._onKeyDown(event); - expect(instance._onSelectKey).to.have.been.calledWith( - event, - hasItems, - ); + expect(instance._onSelectKey).to.have.been.calledWith(event, hasItems); }); }); describe('enter key', () => { it(`calls _onEnterKey with the expected arguments`, () => { const event = { - keyCode: KEY_CODES.ENTER_KEY, + keyCode: KeyCodeMap.ENTER_KEY, + key: 'Enter', }; instance._onKeyDown(event); - expect(instance._onEnterKey).to.have.been.calledWith( - event, - activeItems, - hasActiveDropdown, - ); + expect(instance._onEnterKey).to.have.been.calledWith(event, hasActiveDropdown); }); }); describe('delete key', () => { - const keyCodes = [KEY_CODES.DELETE_KEY, KEY_CODES.BACK_KEY]; + // this is not an error; the constants are named the reverse of their assigned key names, according + // to their actual values, which appear to conform to the Windows VK mappings: + // 0x08 = 'Backspace', 0x2E = 'Delete' + // https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values#editing_keys + const keyCodes = [ + [KeyCodeMap.DELETE_KEY, 'Backspace'], + [KeyCodeMap.BACK_KEY, 'Delete'], + ]; - keyCodes.forEach((keyCode) => { + keyCodes.forEach(([keyCode, key]) => { it(`calls _onDeleteKey with the expected arguments`, () => { const event = { keyCode, + key, }; instance._onKeyDown(event); - expect(instance._onDeleteKey).to.have.been.calledWith( - event, - activeItems, - hasFocussedInput, - ); + expect(instance._onDeleteKey).to.have.been.calledWith(event, items, hasFocussedInput); }); }); }); @@ -2280,49 +2368,55 @@ describe('choices', () => { }); describe('when given an item to remove', () => { - const item = { + const item: ChoiceFull = { + highlighted: false, + active: false, + disabled: false, + placeholder: false, + selected: false, id: 1111, value: 'test value', label: 'test label', - choiceId: 2222, groupId: 3333, customProperties: {}, + score: 0, + rank: 0, }; it('dispatches a REMOVE_ITEM action to the store', () => { instance._removeItem(item); - expect(instance._store.dispatch).to.have.been.calledWith( - removeItem(item.id, item.choiceId), - ); + expect(instance._store.dispatch).to.have.been.calledWith(removeItem(item)); }); - it('triggers a REMOVE_ITEM event on the passed element', (done) => { - passedElement.addEventListener( - 'removeItem', - (event) => { - expect(event.detail).to.eql({ - id: item.id, - value: item.value, - label: item.label, - customProperties: item.customProperties, - groupValue: null, - }); - done(); - }, - false, - ); + it('triggers a REMOVE_ITEM event on the passed element', () => + new Promise((done) => { + passedElement.addEventListener( + 'removeItem', + (event) => { + expect(event.detail).to.contains({ + id: item.id, + value: item.value, + label: item.label, + customProperties: item.customProperties, + groupValue: undefined, + }); + done(true); + }, + false, + ); - instance._removeItem(item); - }); + instance._removeItem(item); + })); describe('when the item belongs to a group', () => { const group = { id: 1, - value: 'testing', + label: 'testing', }; const itemWithGroup = { ...item, + value: 'testing', groupId: group.id, }; @@ -2335,25 +2429,26 @@ describe('choices', () => { instance._store.getGroupById.reset(); }); - it("includes the group's value in the triggered event", (done) => { - passedElement.addEventListener( - 'removeItem', - (event) => { - expect(event.detail).to.eql({ - id: itemWithGroup.id, - value: itemWithGroup.value, - label: itemWithGroup.label, - customProperties: itemWithGroup.customProperties, - groupValue: group.value, - }); - - done(); - }, - false, - ); + it("includes the group's value in the triggered event", () => + new Promise((done) => { + passedElement.addEventListener( + 'removeItem', + (event) => { + expect(event.detail).to.contains({ + id: itemWithGroup.id, + value: itemWithGroup.value, + label: itemWithGroup.label, + customProperties: itemWithGroup.customProperties, + groupValue: group.label, + }); + + done(true); + }, + false, + ); - instance._removeItem(itemWithGroup); - }); + instance._removeItem(itemWithGroup); + })); }); }); }); diff --git a/src/scripts/components/container.test.ts b/test/scripts/components/container.test.ts similarity index 62% rename from src/scripts/components/container.test.ts rename to test/scripts/components/container.test.ts index 790c773ff..11754281b 100644 --- a/src/scripts/components/container.test.ts +++ b/test/scripts/components/container.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { stub } from 'sinon'; -import { DEFAULT_CLASSNAMES } from '../defaults'; -import Container from './container'; +import { DEFAULT_CLASSNAMES } from '../../../src'; +import Container from '../../../src/scripts/components/container'; describe('components/container', () => { let instance; @@ -28,82 +28,16 @@ describe('components/container', () => { describe('constructor', () => { it('assigns choices element to class', () => { - expect(instance.element).to.eql(element); + expect(instance.element).to.equal(element); }); it('assigns classnames to class', () => { - expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES); - }); - }); - - describe('addEventListeners', () => { - let addEventListenerStub; - - beforeEach(() => { - addEventListenerStub = stub(instance.element, 'addEventListener'); - }); - - afterEach(() => { - addEventListenerStub.restore(); - }); - - it('adds event listeners', () => { - instance.addEventListeners(); - expect(addEventListenerStub.callCount).to.equal(2); - expect(addEventListenerStub.getCall(0).args[0]).to.equal('focus'); - expect(addEventListenerStub.getCall(1).args[0]).to.equal('blur'); - }); - }); - - describe('removeEventListeners', () => { - let removeEventListenerStub; - - beforeEach(() => { - removeEventListenerStub = stub(instance.element, 'removeEventListener'); - }); - - afterEach(() => { - removeEventListenerStub.restore(); - }); - - it('removes event listeners', () => { - instance.removeEventListeners(); - expect(removeEventListenerStub.callCount).to.equal(2); - expect(removeEventListenerStub.getCall(0).args[0]).to.equal('focus'); - expect(removeEventListenerStub.getCall(1).args[0]).to.equal('blur'); - }); - }); - - describe('onFocus', () => { - it('sets isFocussed flag to true', () => { - expect(instance.isFocussed).to.equal(false); - instance._onFocus(); - expect(instance.isFocussed).to.equal(true); - }); - }); - - describe('onBlur', () => { - it('sets isFocussed flag to false', () => { - instance.isFocussed = true; - instance._onBlur(); - expect(instance.isFocussed).to.equal(false); + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); }); }); describe('shouldFlip', () => { - describe('not passing dropdownPos', () => { - it('returns false', () => { - expect(instance.shouldFlip()).to.equal(false); - }); - }); - describe('passing dropdownPos', () => { - describe('position config option set to "auto"', () => { - beforeEach(() => { - instance.position = 'auto'; - }); - }); - describe('position config option set to "top"', () => { beforeEach(() => { instance.position = 'top'; @@ -129,30 +63,19 @@ describe('components/container', () => { describe('setActiveDescendant', () => { it("sets element's aria-activedescendant attribute with passed descendant ID", () => { const activeDescendantID = '1234'; - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - null, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null); instance.setActiveDescendant(activeDescendantID); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - activeDescendantID, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID); }); }); describe('removeActiveDescendant', () => { it("remove elememnt's aria-activedescendant attribute", () => { const activeDescendantID = '1234'; - instance.element.setAttribute( - 'aria-activedescendant', - activeDescendantID, - ); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - activeDescendantID, - ); + instance.element.setAttribute('aria-activedescendant', activeDescendantID); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID); instance.removeActiveDescendant(); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - null, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null); }); }); @@ -162,9 +85,7 @@ describe('components/container', () => { }); it('adds open state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.openState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.openState)).to.equal(true); }); it('sets aria-expanded attribute to true', () => { @@ -189,9 +110,7 @@ describe('components/container', () => { }); it('adds adds flipped state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState)).to.equal(true); }); it('sets isFlipped flag to true', () => { @@ -206,9 +125,7 @@ describe('components/container', () => { }); it('adds open state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.openState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.openState)).to.equal(false); }); it('sets aria-expanded attribute to true', () => { @@ -226,9 +143,7 @@ describe('components/container', () => { }); it('removes adds flipped state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.flippedState)).to.equal(false); }); it('sets isFlipped flag to false', () => { @@ -237,47 +152,15 @@ describe('components/container', () => { }); }); - describe('focus', () => { - let focusStub; - - beforeEach(() => { - focusStub = stub(instance.element, 'focus'); - }); - - afterEach(() => { - focusStub.restore(); - }); - - describe('isFocussed flag being set to false', () => { - it('focuses element', () => { - instance.isFocussed = false; - instance.focus(); - expect(focusStub.called).to.equal(true); - }); - }); - - describe('isFocussed flag being set to true', () => { - it('does not focus element', () => { - instance.isFocussed = true; - instance.focus(); - expect(focusStub.called).to.equal(false); - }); - }); - }); - describe('addFocusState', () => { beforeEach(() => { instance.removeLoadingState(); }); it('adds focus state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(false); instance.addFocusState(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(true); }); }); @@ -287,13 +170,9 @@ describe('components/container', () => { }); it('removes focus state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(true); instance.removeFocusState(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.focusState)).to.equal(false); }); }); @@ -303,13 +182,9 @@ describe('components/container', () => { }); it('removes disabled state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(true); instance.enable(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(false); }); it('removes aria-disabled attribute', () => { @@ -341,13 +216,9 @@ describe('components/container', () => { }); it('removes disabled state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(false); instance.disable(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.disabledState)).to.equal(true); }); it('removes aria-disabled attribute', () => { @@ -389,9 +260,7 @@ describe('components/container', () => { it('wraps passed element inside element', () => { expect(instance.element.querySelector('div#wrap-test')).to.equal(null); instance.wrap(document.querySelector('div#wrap-test')); - expect(instance.element.querySelector('div#wrap-test')).to.equal( - elementToWrap, - ); + expect(instance.element.querySelector('div#wrap-test')).to.equal(elementToWrap); }); }); @@ -410,14 +279,10 @@ describe('components/container', () => { }); it('moves wrapped element outside of element', () => { - expect( - instance.element.querySelector('div#unwrap-test'), - ).to.be.instanceof(HTMLElement); + expect(instance.element.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement); instance.unwrap(elementToUnwrap); expect(instance.element.querySelector('div#unwrap-test')).to.equal(null); - expect(document.querySelector('div#unwrap-test')).to.be.instanceof( - HTMLElement, - ); + expect(document.querySelector('div#unwrap-test')).to.be.instanceof(HTMLElement); }); it('removes element from DOM', () => { @@ -433,13 +298,9 @@ describe('components/container', () => { }); it('adds loading state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(false); instance.addLoadingState(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(true); }); it('sets aria-busy attribute to true', () => { @@ -461,13 +322,9 @@ describe('components/container', () => { }); it('removes loading state class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState), - ).to.equal(true); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(true); instance.removeLoadingState(); - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState), - ).to.equal(false); + expect(instance.element.classList.contains(DEFAULT_CLASSNAMES.loadingState)).to.equal(false); }); it('removes aria-busy attribute', () => { diff --git a/src/scripts/components/dropdown.test.ts b/test/scripts/components/dropdown.test.ts similarity index 51% rename from src/scripts/components/dropdown.test.ts rename to test/scripts/components/dropdown.test.ts index 25af3b418..8c5ea5207 100644 --- a/src/scripts/components/dropdown.test.ts +++ b/test/scripts/components/dropdown.test.ts @@ -1,11 +1,12 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import { DEFAULT_CLASSNAMES } from '../defaults'; -import Dropdown from './dropdown'; +import { DEFAULT_CLASSNAMES } from '../../../src'; +import Dropdown from '../../../src/scripts/components/dropdown'; +import { getClassNames } from '../../../src/scripts/lib/utils'; describe('components/dropdown', () => { - let instance; - let choicesElement; + let instance: Dropdown | null; + let choicesElement: HTMLDivElement; beforeEach(() => { choicesElement = document.createElement('div'); @@ -24,20 +25,33 @@ describe('components/dropdown', () => { describe('constructor', () => { it('assigns choices element to instance', () => { - expect(instance.element).to.eql(choicesElement); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element).to.equal(choicesElement); }); it('assigns classnames to instance', () => { - expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); }); }); describe('distanceFromTopWindow', () => { - let top; + let top: number; let dimensions; let getBoundingClientRectStub; beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + top = 100; dimensions = { bottom: 121, @@ -48,9 +62,7 @@ describe('components/dropdown', () => { width: 0, }; - getBoundingClientRectStub = sinon - .stub(instance.element, 'getBoundingClientRect') - .returns(dimensions); + getBoundingClientRectStub = sinon.stub(instance.element, 'getBoundingClientRect').returns(dimensions); }); afterEach(() => { @@ -58,56 +70,63 @@ describe('components/dropdown', () => { }); it('determines how far the top of our element is from the top of the viewport', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } const expectedResponse = dimensions.bottom; const actualResponse = instance.distanceFromTopWindow; expect(actualResponse).to.equal(expectedResponse); }); }); - describe('getChild', () => { - let childElement; - const childClass = 'test-element'; - - beforeEach(() => { - childElement = document.createElement('span'); - childElement.classList.add(childClass); - instance.element.appendChild(childElement); - }); - - it('returns child element', () => { - const expectedResponse = childElement; - const actualResponse = instance.getChild(`.${childClass}`); - expect(expectedResponse).to.eql(actualResponse); - }); - }); - describe('show', () => { let actualResponse; beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } actualResponse = instance.show(); }); afterEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.hide(); }); it('adds active class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState), - ).to.equal(true); + getClassNames(DEFAULT_CLASSNAMES.activeState).forEach((c) => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element.classList.contains(c)).to.equal(true); + }); }); it('sets expanded attribute', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.getAttribute('aria-expanded')).to.equal('true'); }); it('sets isActive instance flag', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.isActive).to.equal(true); }); it('returns instance', () => { - expect(actualResponse).to.eql(instance); + expect(actualResponse).to.deep.equal(instance); }); }); @@ -115,29 +134,49 @@ describe('components/dropdown', () => { let actualResponse; beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } actualResponse = instance.hide(); }); afterEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.show(); }); it('adds active class', () => { - expect( - instance.element.classList.contains(DEFAULT_CLASSNAMES.activeState), - ).to.equal(false); + getClassNames(DEFAULT_CLASSNAMES.activeState).forEach((c) => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element.classList.contains(c)).to.equal(false); + }); }); it('sets expanded attribute', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.getAttribute('aria-expanded')).to.equal('false'); }); it('sets isActive instance flag', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.isActive).to.equal(false); }); it('returns instance', () => { - expect(actualResponse).to.eql(instance); + expect(actualResponse).to.deep.equal(instance); }); }); }); diff --git a/src/scripts/components/input.test.ts b/test/scripts/components/input.test.ts similarity index 89% rename from src/scripts/components/input.test.ts rename to test/scripts/components/input.test.ts index 777f080a6..86424afc7 100644 --- a/src/scripts/components/input.test.ts +++ b/test/scripts/components/input.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { stub } from 'sinon'; -import { DEFAULT_CLASSNAMES } from '../defaults'; -import Input from './input'; +import { DEFAULT_CLASSNAMES } from '../../../src'; +import Input from '../../../src/scripts/components/input'; describe('components/input', () => { let instance; @@ -24,11 +24,11 @@ describe('components/input', () => { describe('constructor', () => { it('assigns choices element to class', () => { - expect(instance.element).to.eql(choicesElement); + expect(instance.element).to.equal(choicesElement); }); it('assigns classnames to class', () => { - expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES); + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); }); }); @@ -46,10 +46,7 @@ describe('components/input', () => { it('adds event listeners', () => { instance.addEventListeners(); expect(['input', 'paste', 'focus', 'blur']).to.have.members( - Array.from( - { length: addEventListenerStub.callCount }, - (_, i) => addEventListenerStub.getCall(i).args[0], - ), + Array.from({ length: addEventListenerStub.callCount }, (_, i) => addEventListenerStub.getCall(i).args[0]), ); }); }); @@ -260,14 +257,10 @@ describe('components/input', () => { it('returns instance', () => { const response = instance.clear(); - expect(response).to.eql(instance); + expect(response).to.deep.equal(instance); }); }); - /** - * Blocked by lack of ch support in JSDOM - * @see {@link https://github.com/jsdom/cssstyle/pull/107} - * describe('setWidth', () => { it('sets the width of the element based on input value and placeholder', () => { instance.placeholder = 'This is a placeholder'; @@ -278,7 +271,6 @@ describe('components/input', () => { expect(instance.element.style.minWidth).to.equal('22ch'); }); }); - */ describe('placeholder setter', () => { it('sets value of element to passed placeholder', () => { @@ -310,43 +302,24 @@ describe('components/input', () => { instance.element.value = value; expect(instance.value).to.equal(value); }); - - it('strips HTML from value', () => { - const value = ''; - instance.element.value = value; - expect(instance.value).to.equal( - '<script>somethingMalicious();</script>', - ); - }); }); describe('setActiveDescendant', () => { it("sets element's aria-activedescendant attribute with passed descendant ID", () => { const activeDescendantID = '1234'; - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - null, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null); instance.setActiveDescendant(activeDescendantID); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - activeDescendantID, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID); }); }); describe('removeActiveDescendant', () => { it("remove elememnt's aria-activedescendant attribute", () => { const activeDescendantID = '1234'; - instance.element.setAttribute( - 'aria-activedescendant', - activeDescendantID, - ); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - activeDescendantID, - ); + instance.element.setAttribute('aria-activedescendant', activeDescendantID); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(activeDescendantID); instance.removeActiveDescendant(); - expect(instance.element.getAttribute('aria-activedescendant')).to.equal( - null, - ); + expect(instance.element.getAttribute('aria-activedescendant')).to.equal(null); }); }); }); diff --git a/src/scripts/components/list.test.ts b/test/scripts/components/list.test.ts similarity index 60% rename from src/scripts/components/list.test.ts rename to test/scripts/components/list.test.ts index c7b1b0ff4..edd54ef96 100644 --- a/src/scripts/components/list.test.ts +++ b/test/scripts/components/list.test.ts @@ -1,9 +1,10 @@ import { expect } from 'chai'; -import List from './list'; +import { getClassNames } from '../../../src/scripts/lib/utils'; +import List from '../../../src/scripts/components/list'; describe('components/list', () => { - let instance; - let choicesElement; + let instance: List | null; + let choicesElement: HTMLDivElement; beforeEach(() => { choicesElement = document.createElement('div'); @@ -19,16 +20,28 @@ describe('components/list', () => { describe('constructor', () => { it('assigns choices element to class', () => { - expect(instance.element).to.eql(choicesElement); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element).to.equal(choicesElement); }); it('sets the height of the element', () => { - expect(instance.height).to.eql(choicesElement.scrollTop); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.height).to.deep.equal(choicesElement.scrollTop); }); }); describe('clear', () => { it("clears element's inner HTML", () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } const innerHTML = 'test'; instance.element.innerHTML = innerHTML; expect(instance.element.innerHTML).to.equal(innerHTML); @@ -39,48 +52,41 @@ describe('components/list', () => { describe('append', () => { it('appends passed node to element', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } const elementToAppend = document.createElement('span'); const childClass = 'test-element'; - elementToAppend.classList.add(childClass); + elementToAppend.classList.add(...getClassNames(childClass)); expect(instance.element.querySelector(`.${childClass}`)).to.equal(null); - instance.append(elementToAppend); - expect(instance.element.querySelector(`.${childClass}`)).to.equal( - elementToAppend, - ); - }); - }); - - describe('getChild', () => { - let childElement; - const childClass = 'test-element'; - - beforeEach(() => { - childElement = document.createElement('span'); - childElement.classList.add(childClass); - instance.element.appendChild(childElement); - }); - - it('returns child element', () => { - const expectedResponse = childElement; - const actualResponse = instance.getChild(`.${childClass}`); - expect(expectedResponse).to.eql(actualResponse); + instance.element.append(elementToAppend); + expect(instance.element.querySelector(`.${childClass}`)).to.equal(elementToAppend); }); }); describe('hasChildren', () => { describe('when list has children', () => { it('returns true', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } const childElement = document.createElement('span'); instance.element.appendChild(childElement); - const response = instance.hasChildren(); + const response = instance.element.hasChildNodes(); expect(response).to.equal(true); }); }); describe('when list does not have children', () => { it('returns false', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.element.innerHTML = ''; - const response = instance.hasChildren(); + const response = instance.element.hasChildNodes(); expect(response).to.equal(false); }); }); @@ -88,6 +94,10 @@ describe('components/list', () => { describe('scrollToTop', () => { it("sets the position's scroll position to 0", () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.element.scrollTop = 10; instance.scrollToTop(); expect(instance.element.scrollTop).to.equal(0); diff --git a/src/scripts/components/wrapped-element.test.ts b/test/scripts/components/wrapped-element.test.ts similarity index 55% rename from src/scripts/components/wrapped-element.test.ts rename to test/scripts/components/wrapped-element.test.ts index 7d3e57642..26cef0a8e 100644 --- a/src/scripts/components/wrapped-element.test.ts +++ b/test/scripts/components/wrapped-element.test.ts @@ -1,10 +1,11 @@ import { expect } from 'chai'; -import { DEFAULT_CLASSNAMES } from '../defaults'; -import WrappedElement from './wrapped-element'; +import { getClassNames } from '../../../src/scripts/lib/utils'; +import { DEFAULT_CLASSNAMES, EventMap, EventType } from '../../../src'; +import WrappedElement from '../../../src/scripts/components/wrapped-element'; describe('components/wrappedElement', () => { - let instance; - let element; + let instance: WrappedElement | null; + let element: HTMLSelectElement; beforeEach(() => { element = document.createElement('select'); @@ -21,39 +22,46 @@ describe('components/wrappedElement', () => { describe('constructor', () => { it('assigns choices element to class', () => { - expect(instance.element).to.eql(element); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element).to.equal(element); }); it('assigns classnames to class', () => { - expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); }); it('sets isDisabled flag to false', () => { - expect(instance.isDisabled).to.eql(false); - }); - - describe('passing an element that is not an instance of HTMLInputElement or HTMLSelectElement', () => { - it('throws a TypeError', () => { - element = document.createElement('div'); - expect( - () => - new WrappedElement({ - element, - classNames: DEFAULT_CLASSNAMES, - }), - ).to.throw(TypeError, 'Invalid element passed'); - }); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.isDisabled).to.deep.equal(false); }); }); describe('value getter', () => { it('returns element value', () => { - expect(instance.value).to.eql(element.value); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.value).to.deep.equal(element.value); }); }); describe('isActive getter', () => { it('returns whether the "data-choice" attribute is set to "active"', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.element.dataset.choice = 'active'; expect(instance.isActive).to.equal(true); @@ -64,74 +72,110 @@ describe('components/wrappedElement', () => { describe('dir getter', () => { it('returns the direction of the element', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.dir).to.equal(instance.element.dir); }); }); describe('conceal', () => { - let originalStyling; + let originalStyling: string; beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } originalStyling = 'color:red'; instance.element.setAttribute('style', originalStyling); }); it('hides element', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.conceal(); expect(instance.element.tabIndex).to.equal(-1); + + const classesToCheck = getClassNames(instance.classNames.input); expect( - instance.element.classList.contains(instance.classNames.input), + Array.from(instance.element.classList).some((className) => classesToCheck.indexOf(className) !== -1), ).to.equal(true); expect(instance.element.hidden).to.be.true; expect(instance.element.getAttribute('data-choice')).to.equal('active'); - expect(instance.element.getAttribute('data-choice-orig-style')).to.equal( - originalStyling, - ); + expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(originalStyling); }); }); describe('reveal', () => { - let originalStyling; + let originalStyling: string; beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } originalStyling = 'color:red'; instance.element.setAttribute('data-choice-orig-style', originalStyling); }); it('shows element', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.reveal(); expect(instance.element.tabIndex).to.equal(0); + + const classesToCheck = getClassNames(instance.classNames.input); expect( - instance.element.classList.contains(instance.classNames.input), + Array.from(instance.element.classList).some((className) => classesToCheck.indexOf(className) !== -1), ).to.equal(false); expect(instance.element.hidden).to.be.false; expect(instance.element.getAttribute('style')).to.equal(originalStyling); expect(instance.element.getAttribute('aria-hidden')).to.equal(null); expect(instance.element.getAttribute('data-choice')).to.equal(null); - expect(instance.element.getAttribute('data-choice-orig-style')).to.equal( - null, - ); + expect(instance.element.getAttribute('data-choice-orig-style')).to.equal(null); }); }); describe('enable', () => { beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.disable(); }); it('removes disabled attribute', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.hasAttribute('disabled')).to.equal(true); instance.enable(); expect(instance.element.hasAttribute('disabled')).to.equal(false); }); it('sets elements disabled state to false', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.disabled).to.equal(true); instance.enable(); expect(instance.element.disabled).to.equal(false); }); it('sets isDisabled flag to false', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.isDisabled).to.equal(true); instance.enable(); expect(instance.isDisabled).to.equal(false); @@ -140,22 +184,38 @@ describe('components/wrappedElement', () => { describe('disable', () => { beforeEach(() => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } instance.enable(); }); it('sets disabled attribute (to blank string)', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.hasAttribute('disabled')).to.equal(false); instance.disable(); expect(instance.element.getAttribute('disabled')).to.equal(''); }); it('sets elements disabled state to true', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.element.disabled).to.equal(false); instance.disable(); expect(instance.element.disabled).to.equal(true); }); it('sets isDisabled flag to true', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(instance.isDisabled).to.equal(false); instance.disable(); expect(instance.isDisabled).to.equal(true); @@ -163,17 +223,22 @@ describe('components/wrappedElement', () => { }); describe('triggerEvent', () => { - it('fires event on element using passed eventType and data', (done) => { - const data = { - test: true, - }; - - instance.element.addEventListener('testEvent', ({ detail }) => { - expect(detail).to.eql(data); - done(); - }); - - instance.triggerEvent('testEvent', data); - }); + it('fires event on element using passed eventType and data', () => + new Promise((done) => { + const data: EventMap[EventType.change]['detail'] = { + value: '', + }; + expect(instance).to.not.be.null; + if (!instance) { + return; + } + + instance.element.addEventListener(EventType.change, ({ detail }: CustomEvent) => { + expect(detail).to.deep.equal(data); + done(true); + }); + + instance.triggerEvent(EventType.change, data); + })); }); }); diff --git a/src/scripts/components/wrapped-input.test.ts b/test/scripts/components/wrapped-input.test.ts similarity index 65% rename from src/scripts/components/wrapped-input.test.ts rename to test/scripts/components/wrapped-input.test.ts index 9aee451d8..1d47224a4 100644 --- a/src/scripts/components/wrapped-input.test.ts +++ b/test/scripts/components/wrapped-input.test.ts @@ -1,20 +1,18 @@ import { expect } from 'chai'; import { stub } from 'sinon'; -import { DEFAULT_CLASSNAMES } from '../defaults'; -import WrappedElement from './wrapped-element'; -import WrappedInput from './wrapped-input'; +import { DEFAULT_CLASSNAMES } from '../../../src'; +import WrappedElement from '../../../src/scripts/components/wrapped-element'; +import WrappedInput from '../../../src/scripts/components/wrapped-input'; describe('components/wrappedInput', () => { - let instance; - let element; - const delimiter = '-'; + let instance: WrappedInput | null; + let element: HTMLInputElement; beforeEach(() => { element = document.createElement('input'); instance = new WrappedInput({ element, classNames: DEFAULT_CLASSNAMES, - delimiter, }); }); @@ -25,11 +23,19 @@ describe('components/wrappedInput', () => { describe('constructor', () => { it('assigns choices element to class', () => { - expect(instance.element).to.eql(element); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element).to.equal(element); }); it('assigns classnames to class', () => { - expect(instance.classNames).to.eql(DEFAULT_CLASSNAMES); + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); }); }); @@ -39,7 +45,7 @@ describe('components/wrappedInput', () => { methods.forEach((method) => { describe(method, () => { beforeEach(() => { - stub(WrappedElement.prototype, method as keyof WrappedElement); + stub(WrappedElement.prototype, method as keyof WrappedElement); }); afterEach(() => { @@ -47,6 +53,10 @@ describe('components/wrappedInput', () => { }); it(`calls super.${method}`, () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } expect(WrappedElement.prototype[method].called).to.equal(false); instance[method](); expect(WrappedElement.prototype[method].called).to.equal(true); @@ -57,6 +67,10 @@ describe('components/wrappedInput', () => { describe('value setter', () => { it('sets the value of the input to the given value', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } const newValue = 'Value 1, Value 2, Value 3'; expect(instance.element.value).to.equal(''); instance.value = newValue; diff --git a/test/scripts/components/wrapped-select.test.ts b/test/scripts/components/wrapped-select.test.ts new file mode 100644 index 000000000..5536e53e8 --- /dev/null +++ b/test/scripts/components/wrapped-select.test.ts @@ -0,0 +1,130 @@ +import { expect } from 'chai'; +import { stub, spy } from 'sinon'; +import WrappedElement from '../../../src/scripts/components/wrapped-element'; +import WrappedSelect from '../../../src/scripts/components/wrapped-select'; +import Templates from '../../../src/scripts/templates'; +import { DEFAULT_CLASSNAMES } from '../../../src'; + +describe('components/wrappedSelect', () => { + let instance: WrappedSelect | null; + let element: HTMLSelectElement; + + beforeEach(() => { + element = document.createElement('select'); + element.id = 'target'; + for (let i = 0; i <= 4; i++) { + const option = document.createElement('option'); + + if (i === 0) { + option.value = ''; + option.innerHTML = 'Placeholder label'; + } else { + option.value = `Value ${i}`; + option.innerHTML = `Label ${i}`; + } + + if (i === 1) { + option.setAttribute('placeholder', ''); + } + + element.appendChild(option); + } + document.body.appendChild(element); + + instance = new WrappedSelect({ + element: document.getElementById('target') as HTMLSelectElement, + classNames: DEFAULT_CLASSNAMES, + template: spy(Templates.option), + extractPlaceholder: true, + }); + }); + + afterEach(() => { + document.body.innerHTML = ''; + instance = null; + }); + + describe('constructor', () => { + it('assigns choices element to class', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element).to.equal(element); + }); + + it('assigns classnames to class', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.classNames).to.deep.equal(DEFAULT_CLASSNAMES); + }); + }); + + describe('inherited methods', () => { + const methods: string[] = ['conceal', 'reveal', 'enable', 'disable']; + + methods.forEach((method) => { + beforeEach(() => { + stub(WrappedElement.prototype, method as keyof WrappedElement); + }); + + afterEach(() => { + WrappedElement.prototype[method].restore(); + }); + + describe(method, () => { + it(`calls super.${method}`, () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(WrappedElement.prototype[method].called).to.equal(false); + instance[method](); + expect(WrappedElement.prototype[method].called).to.equal(true); + }); + }); + }); + }); + + describe('placeholderOption getter', () => { + it('returns option element with empty value attribute', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.placeholderOption).to.be.instanceOf(HTMLOptionElement); + if (instance.placeholderOption) { + expect(instance.placeholderOption.value).to.equal(''); + } + }); + + it('returns option element with placeholder attribute as fallback', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + expect(instance.element.firstChild).to.not.be.null; + if (instance.element.firstChild) { + instance.element.removeChild(instance.element.firstChild); + } + + expect(instance.placeholderOption).to.be.instanceOf(HTMLOptionElement); + if (instance.placeholderOption) { + expect(instance.placeholderOption.value).to.equal('Value 1'); + } + }); + }); + + describe('options getter', () => { + it('returns all option elements', () => { + expect(instance).to.not.be.null; + if (!instance) { + return; + } + const optionsAsChoices = instance.optionsAsChoices(); + expect(optionsAsChoices).to.be.an('array'); + }); + }); +}); diff --git a/src/scripts/lib/utils.test.ts b/test/scripts/lib/utils.test.ts similarity index 69% rename from src/scripts/lib/utils.test.ts rename to test/scripts/lib/utils.test.ts index 022a2cf4f..0a0733cba 100644 --- a/src/scripts/lib/utils.test.ts +++ b/test/scripts/lib/utils.test.ts @@ -6,37 +6,16 @@ import { cloneObject, diff, dispatchEvent, - existsInArray, - generateChars, generateId, - getRandomNumber, - getType, - isType, sanitise, + parseCustomProperties, sortByAlpha, sortByScore, - parseCustomProperties, -} from './utils'; + sortByRank, +} from '../../../src/scripts/lib/utils'; +import { EventType } from '../../../src'; describe('utils', () => { - describe('getRandomNumber', () => { - it('returns random number between range', () => { - for (let index = 0; index < 10; index++) { - const output = getRandomNumber(1, 10); - expect(output).to.be.a('number'); - expect(output).to.be.within(1, 10); - } - }); - }); - - describe('generateChars', () => { - it('generates a string of random chars with given length', () => { - const output = generateChars(10); - expect(output).to.be.a('string'); - expect(output).to.have.length(10); - }); - }); - describe('generateId', () => { describe('when given element has id value', () => { it('generates a unique prefixed id based on given elements id', () => { @@ -78,27 +57,6 @@ describe('utils', () => { }); }); - describe('getType', () => { - it('returns type of given object', () => { - expect(getType({})).to.equal('Object'); - expect(getType(1)).to.equal('Number'); - expect(getType(true)).to.equal('Boolean'); - expect(getType([])).to.equal('Array'); - expect(getType(() => {})).to.equal('Function'); - expect(getType(new Error())).to.equal('Error'); - expect(getType(/''/g)).to.equal('RegExp'); - expect(getType(new String())).to.equal('String'); // eslint-disable-line - expect(getType('')).to.equal('String'); - }); - }); - - describe('isType', () => { - it('checks with given object type equals given type', () => { - expect(isType('Object', {})).to.equal(true); - expect(isType('String', {})).to.equal(false); - }); - }); - describe('sanitise', () => { describe('when passing a parameter that is not a string', () => { it('returns the passed argument', () => { @@ -114,9 +72,7 @@ describe('utils', () => { it('strips HTML from value', () => { const value = ''; const output = sanitise(value); - expect(output).to.equal( - '<script>somethingMalicious();</script>', - ); + expect(output).to.equal('<script>somethingMalicious();</script>'); }); }); }); @@ -133,7 +89,7 @@ describe('utils', () => { const output = values.sort(sortByAlpha); - expect(output).to.eql([ + expect(output).to.deep.equal([ { value: 'Arctic Monkeys' }, { value: 'Oasis' }, { value: 'Tame Impala' }, @@ -151,7 +107,7 @@ describe('utils', () => { const output = values.sort(sortByAlpha); - expect(output).to.eql([ + expect(output).to.deep.equal([ { value: '0', label: 'Arctic Monkeys' }, { value: '0', label: 'Oasis' }, { value: '0', label: 'Tame Impala' }, @@ -164,21 +120,23 @@ describe('utils', () => { describe('sortByScore', () => { describe('sorting an array', () => { it('sorts by score ascending', () => { - const values = [ - { score: 10 }, - { score: 3001 }, - { score: 124 }, - { score: 400 }, - ]; + const values = [{ score: 10 }, { score: 3001 }, { score: 124 }, { score: 400 }]; const output = values.sort(sortByScore); - expect(output).to.eql([ - { score: 10 }, - { score: 124 }, - { score: 400 }, - { score: 3001 }, - ]); + expect(output).to.deep.equal([{ score: 10 }, { score: 124 }, { score: 400 }, { score: 3001 }]); + }); + }); + }); + + describe('sortByRank', () => { + describe('sorting an array', () => { + it('sorts by rank ascending', () => { + const values = [{ rank: 10 }, { rank: 3001 }, { rank: 124 }, { rank: 400 }]; + + const output = values.sort(sortByRank); + + expect(output).to.deep.equal([{ rank: 10 }, { rank: 124 }, { rank: 400 }, { rank: 3001 }]); }); }); }); @@ -188,7 +146,7 @@ describe('utils', () => { const fakeElement = { dispatchEvent: stub(), }; - const eventType = 'addItem'; + const eventType = EventType.addItem; const customArgs = { testing: true, }; @@ -204,20 +162,6 @@ describe('utils', () => { }); }); - describe('existsInArray', () => { - it('determines whether a value exists within given array', () => { - const values = [ - { value: 'The Strokes' }, - { value: 'Arctic Monkeys' }, - { value: 'Oasis' }, - { value: 'Tame Impala' }, - ]; - - expect(existsInArray(values, 'Oasis', 'value')).to.equal(true); - expect(existsInArray(values, 'The Beatles', 'value')).to.equal(false); - }); - }); - describe('cloneObject', () => { it('deeply clones a given object', () => { const object = { @@ -238,7 +182,7 @@ describe('utils', () => { const output = cloneObject(object); expect(output).to.not.equal(object); - expect(output).to.eql(object); + expect(output).to.deep.equal(object); }); }); @@ -265,7 +209,7 @@ describe('utils', () => { const result = { description: 'foo', bar: 'foo' }; const value = parseCustomProperties(customProperties); - expect(value).to.eql(result); + expect(value).to.deep.equal(result); }); }); describe('when custom properties are undefined', () => { @@ -274,7 +218,7 @@ describe('utils', () => { const result = {}; const value = parseCustomProperties(customProperties); - expect(value).to.eql(result); + expect(value).to.deep.equal(result); }); }); }); diff --git a/test/scripts/reducers/choices.test.ts b/test/scripts/reducers/choices.test.ts new file mode 100644 index 000000000..1c16055f1 --- /dev/null +++ b/test/scripts/reducers/choices.test.ts @@ -0,0 +1,186 @@ +import { expect } from 'chai'; +import choices from '../../../src/scripts/reducers/choices'; +import { cloneObject } from '../../../src/scripts/lib/utils'; +import { ChoiceFull } from '../../../src/scripts/interfaces/choice-full'; +import { ActionType } from '../../../src'; +import { StateUpdate } from '../../../src/scripts/interfaces/store'; + +describe('reducers/choices', () => { + describe('when choices do not exist', () => { + describe('ADD_CHOICE', () => { + const choice: ChoiceFull = { + highlighted: false, + value: 'test', + label: 'test', + id: 1, + elementId: '1', + groupId: 1, + active: false, + disabled: false, + placeholder: true, + selected: false, + customProperties: { + test: true, + }, + score: 0, + rank: 0, + }; + + describe('passing expected values', () => { + it('adds choice', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [choice], + }; + + const actualResponse = choices([], { + type: ActionType.ADD_CHOICE, + choice: cloneObject(choice), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + + describe('fallback values', () => { + describe('passing no placeholder value', () => { + it('adds choice with placeholder set to false', () => { + const item = Object.assign(cloneObject(choice), { + placeholder: false, + }); + const expectedResponse: StateUpdate = { + update: true, + state: [item], + }; + + const actualResponse = choices([], { + type: ActionType.ADD_CHOICE, + choice: cloneObject(item), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); + }); + }); + + describe('when choices exist', () => { + let state: ChoiceFull[]; + + beforeEach(() => { + state = [ + { + id: 1, + elementId: 'choices-test-1', + groupId: 0, + value: 'Choice 1', + label: 'Choice 1', + disabled: false, + selected: false, + active: false, + score: 9999, + rank: 9999, + customProperties: {}, + placeholder: false, + highlighted: false, + }, + { + id: 2, + elementId: 'choices-test-2', + groupId: 0, + value: 'Choice 2', + label: 'Choice 2', + disabled: false, + selected: true, + active: false, + score: 9999, + rank: 9999, + customProperties: {}, + placeholder: false, + highlighted: false, + }, + ]; + }); + + describe('FILTER_CHOICES', () => { + it('sets active flag based on whether choice is in passed results', () => { + const id = 1; + const score = 10; + const rank = 10; + const active = true; + + const expectedResponse = { + ...state[0], + active, + score, + rank, + } as ChoiceFull; + + const actualResponse = choices(cloneObject(state), { + type: ActionType.FILTER_CHOICES, + results: [ + { + item: { id } as ChoiceFull, + score, + rank, + }, + ], + }).state.find((choice) => choice.id === id); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + + describe('ACTIVATE_CHOICES', () => { + it('sets active flag to passed value', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [ + { + ...state[0], + active: true, + }, + { + ...state[1], + active: true, + }, + ], + }; + + const actualResponse = choices(cloneObject(state), { + type: ActionType.ACTIVATE_CHOICES, + active: true, + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + + describe('ADD_ITEM', () => { + describe('when action has a choice id', () => { + it('disables choice corresponding with id', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [ + { + ...state[0], + }, + { + ...state[1], + selected: true, + }, + ], + }; + + const actualResponse = choices(cloneObject(state), { + type: ActionType.ADD_ITEM, + item: cloneObject(state[1]), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); + }); +}); diff --git a/test/scripts/reducers/groups.test.ts b/test/scripts/reducers/groups.test.ts new file mode 100644 index 000000000..0577541e7 --- /dev/null +++ b/test/scripts/reducers/groups.test.ts @@ -0,0 +1,34 @@ +import { expect } from 'chai'; +import groups from '../../../src/scripts/reducers/groups'; +import { cloneObject } from '../../../src/scripts/lib/utils'; +import { GroupFull } from '../../../src/scripts/interfaces/group-full'; +import { ActionType } from '../../../src'; +import { StateUpdate } from '../../../src/scripts/interfaces/store'; + +describe('reducers/groups', () => { + describe('when groups do not exist', () => { + describe('ADD_GROUP', () => { + it('adds group', () => { + const group: GroupFull = { + active: true, + disabled: false, + id: 1, + label: 'Group one', + choices: [], + }; + + const expectedResponse: StateUpdate = { + update: true, + state: [group], + }; + + const actualResponse = groups([], { + type: ActionType.ADD_GROUP, + group: cloneObject(group), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); +}); diff --git a/test/scripts/reducers/items.test.ts b/test/scripts/reducers/items.test.ts new file mode 100644 index 000000000..82e147258 --- /dev/null +++ b/test/scripts/reducers/items.test.ts @@ -0,0 +1,173 @@ +import { expect } from 'chai'; +import items from '../../../src/scripts/reducers/items'; +import { RemoveItemAction } from '../../../src/scripts/actions/items'; +import { cloneObject } from '../../../src/scripts/lib/utils'; +import { ChoiceFull } from '../../../src/scripts/interfaces/choice-full'; +import { ActionType } from '../../../src'; +import { StateUpdate } from '../../../src/scripts/interfaces/store'; + +describe('reducers/items', () => { + describe('when items do not exist', () => { + describe('ADD_ITEM', () => { + const choice: ChoiceFull = { + value: 'Item one', + label: 'Item one', + id: 1234, + groupId: 1, + score: 0, + rank: 0, + customProperties: { + property: 'value', + }, + placeholder: true, + active: true, + disabled: false, + selected: true, + highlighted: false, + }; + + describe('passing expected values', () => { + let actualResponse: ChoiceFull[]; + + beforeEach(() => { + actualResponse = items([], { + type: ActionType.ADD_ITEM, + item: cloneObject(choice), + }).state; + }); + + it('adds item', () => { + const expectedResponse = [choice]; + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + + it('unhighlights all highlighted items', () => { + actualResponse.forEach((item) => { + expect(item.highlighted).to.equal(false); + }); + }); + }); + + describe('fallback values', () => { + describe('passing no placeholder value', () => { + const item = Object.assign(cloneObject(choice), { + placeholder: false, + }); + it('adds item with placeholder set to false', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [item], + }; + + const actualResponse = items([], { + type: ActionType.ADD_ITEM, + item: cloneObject(item), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); + }); + }); + + describe('when items exist', () => { + let state: ChoiceFull[]; + + beforeEach(() => { + state = [ + { + id: 1, + groupId: 0, + score: 0, + rank: 0, + value: 'Item one', + label: 'Item one', + active: false, + highlighted: false, + customProperties: {}, + placeholder: false, + disabled: false, + selected: false, + }, + { + id: 2, + groupId: 0, + score: 0, + rank: 0, + value: 'Item one', + label: 'Item one', + active: true, + highlighted: false, + customProperties: {}, + placeholder: false, + disabled: false, + selected: false, + }, + ]; + }); + + describe('REMOVE_ITEM', () => { + it('sets an item to be inactive based on passed ID', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [ + { + ...state[0], + }, + ], + }; + + const actualResponse = items(cloneObject(state), { + type: ActionType.REMOVE_ITEM, + item: cloneObject(state[1]), + } as RemoveItemAction); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + + describe('REMOVE_CHOICE', () => { + it('the item is removed', () => { + const choice = state[0]; + const expectedResponse: StateUpdate = { + update: true, + state: state.filter((s) => s.id !== choice.id), + }; + + const actualResponse = items(cloneObject(state), { + type: ActionType.REMOVE_CHOICE, + choice: cloneObject(choice), + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + + describe('HIGHLIGHT_ITEM', () => { + it('sets an item to be inactive based on passed ID', () => { + const expectedResponse: StateUpdate = { + update: true, + state: [ + { + ...state[0], + }, + { + ...state[1], + highlighted: true, + }, + ], + }; + + const actualResponse = items(cloneObject(state), { + type: ActionType.HIGHLIGHT_ITEM, + item: cloneObject(state[1]), + highlighted: true, + }); + + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); +}); diff --git a/test/scripts/search/index.test.ts b/test/scripts/search/index.test.ts new file mode 100644 index 000000000..de5eb0439 --- /dev/null +++ b/test/scripts/search/index.test.ts @@ -0,0 +1,123 @@ +import { expect } from 'chai'; +import { beforeEach } from 'vitest'; +import { DEFAULT_CONFIG } from '../../../src'; +import { cloneObject } from '../../../src/scripts/lib/utils'; +import { SearchByFuse } from '../../../src/scripts/search/fuse'; +import { SearchByPrefixFilter } from '../../../src/scripts/search/prefix-filter'; + +export interface SearchableShape { + label: string; + value: string; +} + +describe('search', () => { + const options = DEFAULT_CONFIG; + const haystack: SearchableShape[] = []; + Array.from(Array(10).keys()).forEach((i) => + haystack.push({ + label: `label ${i}`, + value: `value ${i}`, + }), + ); + + describe('fuse-full', () => { + let searcher: SearchByFuse; + + beforeEach(() => { + process.env.CHOICES_SEARCH_FUSE = 'full'; + searcher = new SearchByFuse(options); + searcher.index(haystack); + }); + it('empty result', () => { + const results = searcher.search(''); + expect(results.length).eq(0); + }); + it('label prefix', () => { + const results = searcher.search('label'); + expect(results.length).eq(haystack.length); + }); + it('label suffix', () => { + const results = searcher.search(`${haystack.length - 1}`); + expect(results.length).eq(1); + }); + + it('search order', () => { + const results = searcher.search('label'); + + expect(results.length).eq(haystack.length); + haystack.forEach((value, index) => { + expect(results[index].item.value).eq(value.value); + }); + }); + + it('search order - custom sortFn', () => { + const opts = cloneObject(options); + opts.fuseOptions.sortFn = (a, b) => { + if (a.score === b.score) { + return a.idx < b.idx ? 1 : -1; + } + + return a.score < b.score ? 1 : -1; + }; + + searcher = new SearchByFuse(opts); + searcher.index(haystack); + const results = searcher.search('label'); + + expect(results.length).eq(haystack.length); + haystack.reverse().forEach((value, index) => { + expect(results[index].item.value).eq(value.value); + }); + }); + }); + + describe('fuse-basic', () => { + let searcher: SearchByFuse; + beforeEach(() => { + process.env.CHOICES_SEARCH_FUSE = 'basic'; + searcher = new SearchByFuse(options); + searcher.index(haystack); + }); + it('empty result', () => { + const results = searcher.search(''); + expect(results.length).eq(0); + }); + it('label prefix', () => { + const results = searcher.search('label'); + expect(results.length).eq(haystack.length); + }); + it('label suffix', () => { + const results = searcher.search(`${haystack.length - 1}`); + expect(results.length).eq(1); + }); + it('search order', () => { + const results = searcher.search('label'); + + expect(results.length).eq(haystack.length); + haystack.forEach((value, index) => { + expect(results[index].item.value).eq(value.value); + }); + }); + }); + + describe('prefix-filter', () => { + let searcher: SearchByPrefixFilter; + beforeEach(() => { + process.env.CHOICES_SEARCH_FUSE = undefined; + searcher = new SearchByPrefixFilter(options); + searcher.index(haystack); + }); + it('empty result', () => { + const results = searcher.search(''); + expect(results.length).eq(0); + }); + it('label prefix', () => { + const results = searcher.search('label'); + expect(results.length).eq(haystack.length); + }); + it('label suffix', () => { + const results = searcher.search(`${haystack.length - 1}`); + expect(results.length).eq(0); + }); + }); +}); diff --git a/test/scripts/store/store.test.ts b/test/scripts/store/store.test.ts new file mode 100644 index 000000000..766207dd3 --- /dev/null +++ b/test/scripts/store/store.test.ts @@ -0,0 +1,311 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { beforeEach } from 'vitest'; +import Store from '../../../src/scripts/store/store'; +import { ActionType, State } from '../../../src'; +import { cloneObject } from '../../../src/scripts/lib/utils'; +import { AnyAction, StoreListener } from '../../../src/scripts/interfaces/store'; + +describe('reducers/store', () => { + let instance: Store; + let subscribeStub: sinon.SinonStub<[listener: StoreListener], void>; + let dispatchStub: sinon.SinonStub<[action: AnyAction], void>; + let getStateStub: sinon.SinonStub; + let emptyState: State; + let state: State; + + beforeEach(() => { + instance = new Store(); + subscribeStub = sinon.stub(instance, 'subscribe'); + dispatchStub = sinon.stub(instance, 'dispatch'); + getStateStub = sinon.stub(instance, 'state'); + emptyState = instance.defaultState; + state = { + items: [ + { + id: 1, + groupId: 0, + value: 'Item one', + label: 'Item one', + active: false, + highlighted: false, + customProperties: {}, + placeholder: false, + disabled: false, + selected: false, + score: 0, + rank: 0, + }, + { + id: 2, + groupId: 0, + value: 'Item two', + label: 'Item two', + active: true, + highlighted: false, + customProperties: {}, + placeholder: false, + disabled: false, + selected: false, + score: 0, + rank: 0, + }, + { + id: 3, + groupId: 0, + value: 'Item three', + label: 'Item three', + active: true, + highlighted: true, + customProperties: {}, + placeholder: false, + disabled: false, + selected: false, + score: 0, + rank: 0, + }, + ], + choices: [ + { + id: 1, + elementId: 'choices-test-1', + groupId: 0, + value: 'Choice 1', + label: 'Choice 1', + disabled: false, + selected: false, + active: true, + score: 9999, + rank: 9999, + customProperties: {}, + placeholder: false, + highlighted: false, + }, + { + id: 2, + elementId: 'choices-test-2', + groupId: 0, + value: 'Choice 2', + label: 'Choice 2', + disabled: false, + selected: true, + active: false, + score: 9999, + rank: 9998, + customProperties: {}, + placeholder: false, + highlighted: false, + }, + ], + groups: [ + { + id: 1, + label: 'Group one', + active: true, + disabled: false, + choices: [], + }, + { + id: 2, + label: 'Group two', + active: true, + disabled: false, + choices: [], + }, + ], + }; + }); + + afterEach(() => { + subscribeStub.restore(); + dispatchStub.restore(); + getStateStub.restore(); + }); + + describe('constructor', () => { + it('creates redux-like store', () => { + expect(instance).to.contain.keys(['_state', '_listeners', '_txn']); + }); + }); + + describe('subscribe', () => { + it('wraps redux-like subscribe method', () => { + const onChange = (): void => {}; + expect(subscribeStub.callCount).to.equal(0); + instance.subscribe(onChange); + expect(subscribeStub.callCount).to.equal(1); + expect(subscribeStub.firstCall.args[0]).to.equal(onChange); + }); + }); + + describe('dispatch', () => { + it('wraps redux-like dispatch method', () => { + const action: AnyAction = { type: ActionType.CLEAR_CHOICES }; + expect(dispatchStub.callCount).to.equal(0); + instance.dispatch(action); + expect(dispatchStub.callCount).to.equal(1); + expect(dispatchStub.firstCall.args[0]).to.equal(action); + }); + }); + + describe('state getter', () => { + it('returns state', () => { + getStateStub.value(cloneObject(emptyState)); + + expect(instance.state).to.deep.equal(emptyState); + }); + }); + + describe('txn', () => { + let listenerStub: sinon.SinonStub; + + beforeEach(() => { + subscribeStub.restore(); + dispatchStub.restore(); + getStateStub.restore(); + + instance._state = cloneObject(state); + listenerStub = sinon.stub(); + instance.subscribe(listenerStub); + }); + + it('coalesce listener events', () => { + const emptyChoicesState = cloneObject(state); + emptyChoicesState.choices = []; + emptyChoicesState.groups = []; + + instance.withTxn(() => { + const action: AnyAction = { type: ActionType.CLEAR_CHOICES }; + instance.dispatch(action); + instance.dispatch(action); + }); + + expect(listenerStub.callCount).eq(1); + expect(instance.state).to.deep.equal(emptyChoicesState); + }); + + it('coalesce listener events with reset', () => { + instance.withTxn(() => { + const action: AnyAction = { type: ActionType.CLEAR_CHOICES }; + instance.dispatch(action); + instance.dispatch(action); + instance.reset(); + }); + + expect(listenerStub.callCount).eq(1); + expect(instance.state).to.deep.equal(emptyState); + }); + }); + + describe('without txn', () => { + let listenerStub: sinon.SinonStub; + + beforeEach(() => { + subscribeStub.restore(); + dispatchStub.restore(); + getStateStub.restore(); + + instance._state = cloneObject(state); + listenerStub = sinon.stub(); + instance.subscribe(listenerStub); + }); + + it('multiple listener events', () => { + const emptyChoicesState = cloneObject(state); + emptyChoicesState.choices = []; + emptyChoicesState.groups = []; + + const action: AnyAction = { type: ActionType.CLEAR_CHOICES }; + instance.dispatch(action); + instance.dispatch(action); + + expect(listenerStub.callCount).eq(2); + expect(instance.state).to.deep.equal(emptyChoicesState); + }); + + it('multiple listener events with reset', () => { + const action: AnyAction = { type: ActionType.CLEAR_CHOICES }; + instance.dispatch(action); + instance.dispatch(action); + instance.reset(); + + expect(listenerStub.callCount).eq(3); + expect(instance.state).to.deep.equal(emptyState); + }); + }); + + describe('store selectors', () => { + beforeEach(() => { + getStateStub.value(cloneObject(state)); + }); + + describe('items getter', () => { + it('returns items', () => { + const expectedResponse = state.items; + expect(instance.items).to.deep.equal(expectedResponse); + }); + }); + + describe('highlightedActiveItems getter', () => { + it('returns items that are active and highlighted', () => { + const expectedResponse = state.items.filter((item) => item.highlighted && item.active); + expect(instance.highlightedActiveItems).to.deep.equal(expectedResponse); + }); + }); + + describe('choices getter', () => { + it('returns choices', () => { + const expectedResponse = state.choices; + expect(instance.choices).to.deep.equal(expectedResponse); + }); + }); + + describe('activeChoices getter', () => { + it('returns choices that are active', () => { + const expectedResponse = state.choices.filter((choice) => choice.active); + expect(instance.activeChoices).to.deep.equal(expectedResponse); + }); + }); + + describe('searchableChoices getter', () => { + it('returns choices that are not placeholders and are selectable', () => { + const expectedResponse = state.choices.filter((choice) => !choice.disabled && !choice.placeholder); + expect(instance.searchableChoices).to.deep.equal(expectedResponse); + }); + }); + + describe('getChoiceById', () => { + describe('passing id', () => { + it('returns active choice by passed id', () => { + const id: number = 1; + const expectedResponse = state.choices.find((choice) => choice.id === id); + const actualResponse = instance.getChoiceById(id); + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); + + describe('groups getter', () => { + it('returns groups', () => { + const expectedResponse = state.groups; + expect(instance.groups).to.deep.equal(expectedResponse); + }); + }); + + describe('activeGroups getter', () => { + it('returns active groups', () => { + const expectedResponse = state.groups.filter((group) => group.active); + expect(instance.activeGroups).to.deep.equal(expectedResponse); + }); + }); + + describe('getGroupById', () => { + it('returns group by id', () => { + const id = 1; + const expectedResponse = state.groups.find((group) => group.id === id); + const actualResponse = instance.getGroupById(id); + expect(actualResponse).to.deep.equal(expectedResponse); + }); + }); + }); +}); diff --git a/src/scripts/templates.test.ts b/test/scripts/templates.test.ts similarity index 72% rename from src/scripts/templates.test.ts rename to test/scripts/templates.test.ts index c4af46976..b560b0a9e 100644 --- a/src/scripts/templates.test.ts +++ b/test/scripts/templates.test.ts @@ -1,9 +1,9 @@ import { expect } from 'chai'; -import templates from './templates'; -import { strToEl } from './lib/utils'; -import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG } from './defaults'; -import { Options } from './interfaces/options'; -import { ClassNames } from './interfaces/class-names'; +// eslint-disable-next-line import/no-named-default +import { default as _templates } from '../../src/scripts/templates'; +import { strToEl, getClassNames } from '../../src/scripts/lib/utils'; +import { DEFAULT_CLASSNAMES, DEFAULT_CONFIG, Options, ClassNames } from '../../src'; +import { NoticeTypes, Templates as TemplatesInterface } from '../../src/scripts/interfaces/templates'; /** * @param {HTMLElement} element1 @@ -12,22 +12,15 @@ import { ClassNames } from './interfaces/class-names'; function expectEqualElements(element1, element2): void { expect(element1.tagName).to.equal(element2.tagName); expect(element1.attributes.length).to.equal(element2.attributes.length); - expect(Object.keys(element1.dataset)).to.have.members( - Object.keys(element2.dataset), - ); + expect(Object.keys(element1.dataset)).to.have.members(Object.keys(element2.dataset)); expect(element1.classList).to.include(element2.classList); // compare attributes values for (const attribute of Object.values(element1.attributes)) { - expect(element1.getAttribute(attribute)).to.equal( - element2.getAttribute(attribute), - ); + expect(element1.getAttribute(attribute)).to.equal(element2.getAttribute(attribute)); } } -function createOptionsWithPartialClasses( - classNames: Partial, - options: Partial = {}, -): Options { +function createOptionsWithPartialClasses(classNames: Partial, options: Partial = {}): Options { return { ...DEFAULT_CONFIG, ...options, @@ -38,6 +31,22 @@ function createOptionsWithPartialClasses( }; } +function shimTemplates(element: string): TemplatesInterface { + const fauxChoices = { + _docRoot: document.createElement('body'), + passedElement: { + element: document.createElement(element), + }, + }; + + const templating = {}; + Object.keys(_templates).forEach((name) => { + templating[name] = _templates[name].bind(fauxChoices); + }); + + return templating as TemplatesInterface; +} + describe('templates', () => { describe('containerOuter', () => { const options = createOptionsWithPartialClasses({ @@ -46,6 +55,7 @@ describe('templates', () => { const direction = 'rtl'; describe('select element', () => { + const templates = shimTemplates('select'); describe('search enabled', () => { it('returns expected html', () => { const isSelectElement = true; @@ -56,7 +66,7 @@ describe('templates', () => { const expectedOutput = strToEl(`
{ const expectedOutput = strToEl(`
{ const expectedOutput = strToEl(`
{ }); describe('non select element', () => { + const templates = shimTemplates('input'); it('returns expected html', () => { const isSelectElement = false; const isSelectOneElement = false; @@ -191,10 +202,8 @@ describe('templates', () => { const expectedOutput = strToEl(` @@ -215,12 +224,13 @@ describe('templates', () => { }); describe('containerInner', () => { + const templates = shimTemplates('select'); it('returns expected html', () => { const innerOptions = createOptionsWithPartialClasses({ containerInner: 'class-1', }); const expectedOutput = strToEl( - `
`, + `
`, ); const actualOutput = templates.containerInner(innerOptions); @@ -229,6 +239,7 @@ describe('templates', () => { }); describe('itemList', () => { + const templates = shimTemplates('select'); const itemOptions = createOptionsWithPartialClasses({ list: 'class-1', listSingle: 'class-2', @@ -238,7 +249,7 @@ describe('templates', () => { describe('select one element', () => { it('returns expected html', () => { const expectedOutput = strToEl( - `
`, + `
`, ); const actualOutput = templates.itemList(itemOptions, true); @@ -249,7 +260,7 @@ describe('templates', () => { describe('non select one element', () => { it('returns expected html', () => { const expectedOutput = strToEl( - `
`, + `
`, ); const actualOutput = templates.itemList(itemOptions, false); @@ -259,13 +270,14 @@ describe('templates', () => { }); describe('placeholder', () => { + const templates = shimTemplates('select'); it('returns expected html', () => { const placeholderOptions = createOptionsWithPartialClasses({ placeholder: 'class-1', }); const value = 'test'; const expectedOutput = strToEl(` -
${value}
`); +
${value}
`); const actualOutput = templates.placeholder(placeholderOptions, value); expectEqualElements(actualOutput, expectedOutput); @@ -278,10 +290,11 @@ describe('templates', () => { }); describe('select one element', () => { + const templates = shimTemplates('select'); it('returns expected html', () => { const expectedOutput = strToEl(`
@@ -293,10 +306,11 @@ describe('templates', () => { }); describe('non select one element', () => { + const templates = shimTemplates('input'); it('returns expected html', () => { const expectedOutput = strToEl(`
@@ -310,6 +324,7 @@ describe('templates', () => { }); describe('choiceGroup', () => { + const templates = shimTemplates('select'); const groupOptions = createOptionsWithPartialClasses({ group: 'class-1', groupHeading: 'class-2', @@ -330,13 +345,13 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
-
${data.value}
+
${data.value}
`); const actualOutput = templates.choiceGroup(groupOptions, data); @@ -356,14 +371,14 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
-
${data.value}
+
${data.value}
`); const actualOutput = templates.choiceGroup(groupOptions, data); @@ -374,6 +389,7 @@ describe('templates', () => { }); describe('choice', () => { + const templates = shimTemplates('select'); const choiceOptions = createOptionsWithPartialClasses({ item: 'class-1', itemChoice: 'class-2', @@ -390,7 +406,7 @@ describe('templates', () => { beforeEach(() => { data = { id: 1, - groupId: -1, + groupId: 0, disabled: false, elementId: 'test', label: 'test', @@ -403,7 +419,9 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
{ ${data.label}
`); - const actualOutput = templates.choice( - choiceOptions, - data, - itemSelectText, - ); + const actualOutput = templates.choice(choiceOptions, data, itemSelectText); expectEqualElements(actualOutput, expectedOutput); }); @@ -436,7 +450,9 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
{ ${data.label}
`); - const actualOutput = templates.choice( - choiceOptions, - data, - itemSelectText, - ); + const actualOutput = templates.choice(choiceOptions, data, itemSelectText); expectEqualElements(actualOutput, expectedOutput); }); @@ -470,7 +482,11 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
{ ${data.label}
`); - const actualOutput = templates.choice( - choiceOptions, - data, - itemSelectText, - ); + const actualOutput = templates.choice(choiceOptions, data, itemSelectText); expectEqualElements(actualOutput, expectedOutput); }); @@ -503,7 +515,11 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
{ ${data.label}
`); - const actualOutput = templates.choice( - choiceOptions, - data, - itemSelectText, - ); + const actualOutput = templates.choice(choiceOptions, data, itemSelectText); expectEqualElements(actualOutput, expectedOutput); }); @@ -536,11 +548,14 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(`
{ ${data.label}
`); - const actualOutput = templates.choice( - choiceOptions, - data, - itemSelectText, - ); + const actualOutput = templates.choice(choiceOptions, data, itemSelectText); expectEqualElements(actualOutput, expectedOutput); }); @@ -560,6 +571,7 @@ describe('templates', () => { }); describe('input', () => { + const templates = shimTemplates('input'); const inputOptions = createOptionsWithPartialClasses({ input: 'class-1', inputCloned: 'class-2', @@ -574,8 +586,7 @@ describe('templates', () => { const expectedOutput = strToEl(` { }); describe('dropdown', () => { + const templates = shimTemplates('select'); const dropdownOptions = createOptionsWithPartialClasses({ list: 'class-1', listDropdown: 'class-2', @@ -596,7 +608,7 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl( - ``, + ``, ); const actualOutput = templates.dropdown(dropdownOptions); @@ -605,6 +617,7 @@ describe('templates', () => { }); describe('notice', () => { + const templates = shimTemplates('select'); const noticeOptions = createOptionsWithPartialClasses({ item: 'class-1', itemChoice: 'class-2', @@ -616,11 +629,11 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl(` -
+
${label}
`); - const actualOutput = templates.notice(noticeOptions, label); + const actualOutput = templates.notice(noticeOptions, label, ''); expectEqualElements(actualOutput, expectedOutput); }); @@ -628,16 +641,13 @@ describe('templates', () => { describe('passing a notice type', () => { describe('no results', () => { it('adds no results classname', () => { + const { item, itemChoice, notice, noResults } = noticeOptions.classNames; const expectedOutput = strToEl(` -
+
${label}
`); - const actualOutput = templates.notice( - noticeOptions, - label, - 'no-results', - ); + const actualOutput = templates.notice(noticeOptions, label, NoticeTypes.noResults); expectEqualElements(actualOutput, expectedOutput); }); @@ -645,16 +655,13 @@ describe('templates', () => { describe('no choices', () => { it('adds no choices classname', () => { + const { item, itemChoice, notice, noChoices } = noticeOptions.classNames; const expectedOutput = strToEl(` -
+
${label}
`); - const actualOutput = templates.notice( - noticeOptions, - label, - 'no-choices', - ); + const actualOutput = templates.notice(noticeOptions, label, NoticeTypes.noChoices); expectEqualElements(actualOutput, expectedOutput); }); @@ -663,6 +670,7 @@ describe('templates', () => { }); describe('option', () => { + const templates = shimTemplates('select'); let data; beforeEach(() => { @@ -676,9 +684,7 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl( - ``, + ``, ); const actualOutput = templates.option(data); @@ -689,7 +695,7 @@ describe('templates', () => { beforeEach(() => { data = { ...data, - active: true, + selected: true, }; }); diff --git a/test/setupFiles/window-matchMedia.ts b/test/setupFiles/window-matchMedia.ts new file mode 100644 index 000000000..c60d7cf96 --- /dev/null +++ b/test/setupFiles/window-matchMedia.ts @@ -0,0 +1,17 @@ +import { beforeAll } from 'vitest'; + +beforeAll(() => { + Object.defineProperty(globalThis.window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), // deprecated + removeListener: vi.fn(), // deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), + }); +}); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 000000000..95360152e --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "es6", + "lib": ["es2017", "dom"], + "target": "es5", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "strict": false, + "noImplicitAny": false, + "allowJs": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictNullChecks": true, + "newLine": "lf", + "declaration": false, + "declarationMap": false, + "types": ["@types/node", "vitest/globals", "vitest/jsdom"], + }, + "include": [".", "../src"], + "exclude": ["**/node_modules", "**/public"] +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index baa7c82a3..000000000 --- a/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "/", - "outDir": "./dist/", - "allowJs": true, - "declaration": true, - "declarationDir": "./public/types/", - "declarationMap": true, - "sourceMap": true, - "module": "commonjs", - "moduleResolution": "node", - "esModuleInterop": true, - "target": "es5", - "lib": ["es2017", "dom"], - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitAny": false, - "strictNullChecks": true, - "types": ["cypress"], - "newLine": "lf" - }, - "include": ["./src/**/*", "./cypress/**/*"] -} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 000000000..9d6ba3e6e --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./test/setupFiles/window-matchMedia.ts'], + include: ['test/**/*.{test,spec}.?(c|m)[jt]s?(x)'], + }, + esbuild: { + target: 'es2017', + }, +}); diff --git a/webpack.config.base.js b/webpack.config.base.js deleted file mode 100644 index ac35494d5..000000000 --- a/webpack.config.base.js +++ /dev/null @@ -1,46 +0,0 @@ -const ESLintPlugin = require('eslint-webpack-plugin'); -const path = require('path'); - -const include = path.resolve(__dirname, './src/scripts'); -const exclude = /node_modules/; - -/** - * @type {import('webpack').Configuration} - */ -module.exports = { - entry: ['./src/index'], - module: { - rules: [ - { - loader: 'babel-loader', - test: /\.ts?$/, - include, - exclude, - options: { - babelrc: true, - }, - }, - { - loader: 'ts-loader', - test: /\.ts?$/, - include, - exclude - } - ], - }, - resolve: { - extensions: [".ts", ".js"] - }, - plugins: [new ESLintPlugin({ - context: include, - files: '**/*.js', - exclude: 'node_modules', - quiet: true - })], - output: { - library: 'Choices', - libraryTarget: 'window', - libraryExport: 'default', - globalObject: 'window', - }, -}; diff --git a/webpack.config.dev.js b/webpack.config.dev.js deleted file mode 100644 index b4a101ec8..000000000 --- a/webpack.config.dev.js +++ /dev/null @@ -1,24 +0,0 @@ -const path = require('path'); -const { HotModuleReplacementPlugin } = require('webpack'); -const deepMerge = require('deepmerge'); -const baseConfig = require('./webpack.config.base'); - -module.exports = deepMerge( - baseConfig, - /** @type {import('webpack').Configuration} */ ({ - mode: 'development', - output: { - path: path.resolve(__dirname, './public'), - filename: 'choices.min.js', - publicPath: 'http://localhost:3001/assets/scripts/', - }, - devtool: 'source-map', - entry: ['webpack/hot/dev-server', 'webpack-hot-middleware/client'], - plugins: [new HotModuleReplacementPlugin()], - }), - { - arrayMerge(target, source) { - return [...source, ...target]; - }, - }, -); diff --git a/webpack.config.prod.js b/webpack.config.prod.js deleted file mode 100644 index a88a8dd2c..000000000 --- a/webpack.config.prod.js +++ /dev/null @@ -1,47 +0,0 @@ -const path = require('path'); -const deepMerge = require('deepmerge'); -const { BannerPlugin } = require('webpack'); - -const baseConfig = require('./webpack.config.base'); -const { name, version, author, homepage } = require('./package.json'); - -const arrayMerge = (target, source) => [...source, ...target]; - -const prodConfig = deepMerge( - baseConfig, - { - mode: 'production', - output: { - path: path.join(__dirname, '/public/assets/scripts'), - publicPath: '/public/assets/scripts/', - }, - plugins: [ - new BannerPlugin( - `${name} v${version} | © ${new Date().getFullYear()} ${author} | ${homepage}`, - ), - ], - }, - { - arrayMerge, - }, -); - -module.exports = [ - deepMerge( - prodConfig, - { - output: { filename: 'choices.js', libraryTarget: 'umd' }, - optimization: { minimize: false }, - }, - { - arrayMerge, - }, - ), - deepMerge( - prodConfig, - { output: { filename: 'choices.min.js' } }, - { - arrayMerge, - }, - ), -];