1
0
mirror of https://github.com/flarum/core.git synced 2025-08-18 06:11:23 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
luceos
aea72957fd Apply fixes from StyleCI
[ci skip] [skip ci]
2021-06-28 09:37:49 +00:00
Daniel Klabbers
838d9c5106 Allow easier extensibility of page document
This allows extensions to:

- mutate the json api document sent with php documents
- override/interact with the Document

Right now extensions need to replace the complete Frontend class
in order to interact with the document. Let's abstract into ioc
so that advanced devs can interact with it.
2021-06-28 11:36:03 +02:00
595 changed files with 20487 additions and 18644 deletions

1
.gitattributes vendored
View File

@@ -13,6 +13,5 @@ tests export-ignore
js/dist/* -diff js/dist/* -diff
js/dist/* linguist-generated js/dist/* linguist-generated
js/dist-typings/* linguist-generated js/dist-typings/* linguist-generated
js/yarn.lock -diff
* text=auto eol=lf * text=auto eol=lf

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: flarum
open_collective: flarum

24
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,24 @@
<!--
IMPORTANT: We applaud pull requests, they excite us every single time. As we have an obligation to maintain a healthy code standard and quality, we take sufficient time for reviews. Please do create a separate pull request per change/issue/feature; we will ask you to split bundled pull requests.
-->
**Fixes #0000**
**Changes proposed in this pull request:**
<!-- fill this out, mention the pages and/or components which have been impacted -->
**Reviewers should focus on:**
<!-- fill this out, ask for feedback on specific changes you are unsure about -->
**Screenshot**
<!-- include an image of the most relevant user-facing change, if any -->
**Confirmed**
- [ ] Frontend changes: tested on a local Flarum installation.
- [ ] Backend changes: tests are green (run `composer test`).
**Required changes:**
- [ ] Related documentation PR: (Remove if irrelevant)
- [ ] Related core extension PRs: (Remove if irrelevant)

13
.github/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,13 @@
# Security Policy
## Versions
Due to the nature of our project - being open source - we have decided to patch only the latest major release (currently v1.x) for security vulnerabilities.
## How to disclose
Please use [huntr.dev](https://huntr.dev/) for security issues that affect our project. If you believe you have found a vulnerability, please disclose it via [this form](https://huntr.dev/bounties/disclose/?target=https://github.com/flarum/core).
This will enable us to **review** the vulnerability, **fix** it promptly, and **reward** you for your efforts.
If you have any questions about the process, feel free to reach out to security@huntr.dev or security@flarum.org.

View File

@@ -1,15 +0,0 @@
name: Core PHP
on: [workflow_dispatch, push, pull_request]
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
# This will break your current script.
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
jobs:
run:
uses: flarum/.github/.github/workflows/REUSABLE_backend.yml@main
with:
enable_backend_testing: true
backend_directory: .

33
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: JavaScript
on:
push:
branches:
- master
jobs:
build:
name: JS / Build
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Restore npm cache
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('js/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# Our action will install npm, cd into `./js`, run `npm run build` and
# `npm run build-typings`, then commit and upload any changes
- name: Build production JS
uses: flarum/action-build@2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
build_script: build
package_manager: npm
typings_script: build-typings

View File

@@ -1,21 +0,0 @@
name: Core JS
on: [workflow_dispatch, push, pull_request]
# The reusable workflow definitions will be moved to the `flarum/framework` repo soon.
# This will break your current script.
# When this happens, run `flarum-cli audit infra --fix` to update your infrastructure.
jobs:
run:
uses: flarum/.github/.github/workflows/REUSABLE_frontend.yml@main
with:
enable_bundlewatch: true
enable_prettier: true
enable_typescript: true
frontend_directory: ./js
main_git_branch: master
secrets:
bundlewatch_github_token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}

28
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Lint
on:
workflow_dispatch:
push:
paths:
- 'js/src/**'
pull_request:
paths:
- 'js/src/**'
jobs:
prettier:
name: JS / Prettier
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: "14"
- name: Check JS formatting
run: npx prettier --check src
working-directory: ./js

45
.github/workflows/pr_size_change.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Bundle size checker
on:
workflow_dispatch:
push:
paths:
- "js/**"
pull_request:
paths:
- "js/**"
jobs:
bundlewatch:
runs-on: ubuntu-latest
name: Bundlewatch
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: "14"
- name: Use npm v7
run: sudo npm install -g npm@7.x.x
- name: Install JS dependencies
# We need to use `npm install` here. If we don't, the workflow will fail.
run: npm install
working-directory: ./js
- name: Build production assets
run: npm run build
working-directory: ./js
- name: Check bundle size change
run: node_modules/.bin/bundlewatch --config .bundlewatch.config.json
working-directory: ./js
env:
BUNDLEWATCH_GITHUB_TOKEN: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}

79
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: [7.3, 7.4, '8.0']
service: ['mysql:5.7', mariadb]
prefix: ['', flarum_]
include:
- service: 'mysql:5.7'
db: MySQL
- service: mariadb
db: MariaDB
- prefix: flarum_
prefixStr: (prefix)
exclude:
- php: 7.3
service: 'mysql:5.7'
prefix: flarum_
- php: 7.3
service: mariadb
prefix: flarum_
- php: 8.0
service: 'mysql:5.7'
prefix: flarum_
- php: 8.0
service: mariadb
prefix: flarum_
services:
mysql:
image: ${{ matrix.service }}
ports:
- 13306:3306
name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}'
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@0b9d33cd0782337377999751fc10ea079fdd7104 # pin@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug
extensions: curl, dom, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip
tools: phpunit, composer:v2
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
# which isn't supported prior to PHP7.4
# When we drop support for PHP7.3, we should remove this from the setup.
- name: Create MySQL Database
run: |
sudo systemctl start mysql
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
- name: Install Composer dependencies
run: composer install
- name: Setup Composer tests
run: composer test:setup
env:
DB_PORT: 13306
DB_PASSWORD: root
DB_PREFIX: ${{ matrix.prefix }}
- name: Run Composer tests
run: composer test
env:
COMPOSER_PROCESS_TIMEOUT: 600

3
.gitignore vendored
View File

@@ -1,7 +1,7 @@
/vendor /vendor
composer.lock composer.lock
composer.phar composer.phar
node_modules
.DS_Store .DS_Store
Thumbs.db Thumbs.db
tests/.phpunit.result.cache tests/.phpunit.result.cache
@@ -9,4 +9,3 @@ tests/.phpunit.result.cache
.vagrant .vagrant
.idea/* .idea/*
.vscode .vscode
js/coverage-ts

View File

@@ -1,181 +1,5 @@
# Changelog # Changelog
## [1.2.1](https://github.com/flarum/core/compare/v1.2.0...v1.2.1)
### Fixed
- Don't escape single quotes in discussion title meta tags (60600f4d2b8f0c5dac94c329041427a0a08fad42)
## [1.2.0](https://github.com/flarum/core/compare/v1.1.1...v1.2.0)
### Added
- View `README` documentation in extension pages (https://github.com/flarum/core/pull/3094).
- Declare & Use CSS Custom Properties (https://github.com/flarum/core/pull/3146).
- Lazy draw dropdowns to improve performance (https://github.com/flarum/core/pull/2925).
- Default Settings Extender (https://github.com/flarum/core/pull/3127).
- Add `textarea` setting type to admin pages (https://github.com/flarum/core/pull/3141).
- Allow registering settings as `Less` config vars through Settings Extender (https://github.com/flarum/core/pull/3011).
- Allow replacing of blade template namespaces via extender (https://github.com/flarum/core/pull/3167).
- Update to Webpack 5 (https://github.com/flarum/core/pull/3135).
- Introduce `Less` custom function extender with a `is-extension-enabled` function (https://github.com/flarum/core/pull/3190).
- Support for `few` in ICU Message syntax (https://github.com/flarum/core/pull/3122).
- ES6 local support for number formatting (https://github.com/flarum/core/pull/3099).
- Added dedicated endpoint for retrieving single groups (https://github.com/flarum/core/pull/3084).
- Callback `loadWhere` relation eager loading extender (https://github.com/flarum/core/pull/3116).
- Extensible document title driver implementation (https://github.com/flarum/core/pull/3109).
- Type checks, typescript coverage GH action (https://github.com/flarum/core/pull/3136).
- Add color indicator in appearance admin page instead of validating colors (https://github.com/flarum/core/pull/3140).
- Add typing files for our translator libraries (https://github.com/flarum/core/pull/3175).
- `StatusWidget` tools extensibility (https://github.com/flarum/core/pull/3189).
- Allow switching the `ImageManager` driver (https://github.com/flarum/core/pull/3195).
- Events for notification read/all read actions (https://github.com/flarum/core/pull/3203).
### Changed
- Testing with php8.1 (https://github.com/flarum/core/pull/3102).
- Migrate fully to Yarn (https://github.com/flarum/core/pull/3155).
- Handle post rendering errors to avoid crashes (https://github.com/flarum/core/pull/3061).
- Added basic filtering, sorting, and pagination to groups endpoint (https://github.com/flarum/core/pull/3084).
- Pass IP address to API Client pipeline (https://github.com/flarum/core/pull/3124).
- Rename Extension Page "Uninstall" to "Purge" (https://github.com/flarum/core/pull/3123).
- [A11Y] Improve accessibility for discussion reply count on post stream (https://github.com/flarum/core/pull/3090).
- Improved post loading support (https://github.com/flarum/core/pull/3100).
- Rewrite SubtreeRetainer into Typescript (https://github.com/flarum/core/pull/3137).
- Rewrite ModalManager and state to Typescript (https://github.com/flarum/core/pull/3007).
- Rewrite frontend application files to Typescript (https://github.com/flarum/core/pull/3006).
- Allow extensions to modify the minimum search length in the Search component (https://github.com/flarum/core/pull/3130).
- Allow use of any tag in `listItems` helper (https://github.com/flarum/core/pull/3147).
- Replace `for ... in` with `Array.reduce` (https://github.com/flarum/core/pull/3149).
- Page title format is now implemented through translations (https://github.com/flarum/core/pull/3077, https://github.com/flarum/core/pull/3228)
- Add `aria-label` attribute to the navigation drawer button (https://github.com/flarum/core/pull/3157).
- Convert extend util to TypeScript (https://github.com/flarum/core/pull/2928).
- Better typings for DiscussionListState (https://github.com/flarum/core/pull/3132).
- Rewrite ItemList, update `ItemList` typings (https://github.com/flarum/core/pull/3005).
- Add priority order to discussion page controls (https://github.com/flarum/core/pull/3165).
- Use `@php` in Blade templates (https://github.com/flarum/core/pull/3172).
- Convert some common classes/utils to TS (https://github.com/flarum/core/pull/2929).
- Convert routes to Typescript (https://github.com/flarum/core/pull/3177).
- Move admin `colorItems` to an `ItemList` (https://github.com/flarum/core/pull/3186).
- Centralize pagination/canonical meta URL generation in Document (https://github.com/flarum/core/pull/3077).
- Use revision versioner to allow custom asset versioning (https://github.com/flarum/core/pull/3183).
- Split up application error handling (https://github.com/flarum/core/pull/3184).
- Make SlugManager available to blade template (https://github.com/flarum/core/pull/3194).
- Convert models to TS (https://github.com/flarum/core/pull/3174).
- Allow loading relations in other discussion endpoints (https://github.com/flarum/core/pull/3191).
- Improve selected text stylization (https://github.com/flarum/core/pull/2961).
- Extract notification `primaryControl` items to an ItemList (https://github.com/flarum/core/pull/3204).
- Frontend code housekeeping (#3214, #3213).
- Only retain scroll position if coming from discussion (https://github.com/flarum/core/pull/3229).
- Use `aria-live` regions to focus screenreader attention on alerts as they appear (https://github.com/flarum/core/pull/3237).
- Prevent unwarranted `a11y` warnings on custom Button subclasses (https://github.com/flarum/core/pull/3238).
### Fixed
- Missing locale text in the user editing modal (https://github.com/flarum/core/pull/3093).
- Dashes in table prefix prevent installation (https://github.com/flarum/core/pull/3089).
- Missing autocomplete attributes to input fields (https://github.com/flarum/core/pull/3088).
- Missing route parameters throwing an error (https://github.com/flarum/core/pull/3118).
- Mail settings select component never used (https://github.com/flarum/core/pull/3120).
- White avatar image throws javascript errors on the profile page (https://github.com/flarum/core/pull/3119).
- Unformatted avatar upload validation errors (https://github.com/flarum/core/pull/2946).
- Webkit input clear button shows up with the custom one (https://github.com/flarum/core/pull/3128).
- Media query breakpoints conflict with Windows display scaling (https://github.com/flarum/core/pull/3139).
- `typeof this` not recognized by some IDEs (https://github.com/flarum/core/pull/3142).
- `Model.save()` cannot save `null` `hasOne` relationship (https://github.com/flarum/core/pull/3131).
- Edit post `until reply` policy broken on PHP 8 (https://github.com/flarum/core/pull/3145).
- Inaccurate `Component.component` argument typings (https://github.com/flarum/core/pull/3148).
- Scrolling notification list infinitely repeats (https://github.com/flarum/core/pull/3159).
- Argument for INFO constant was assigned to `maxfiles` argument incorrectly (bfd81a83cfd0fa8125395a147ff0c9ce622f38e3).
- `Activated` event is sent every time an email is confirmed instead of just once (https://github.com/flarum/core/pull/3163).
- [A11Y] Modal close button missing accessible label (https://github.com/flarum/core/pull/3161).
- [A11Y] Auth modal inputs missing accessible labels (https://github.com/flarum/core/pull/3207).
- [A11Y] Triggering click on drawer button can cause layered backdrops (https://github.com/flarum/core/pull/3018).
- [A11Y] Focus can leave open nav drawer on mobile (https://github.com/flarum/core/pull/3018).
- [A11Y] Post action items not showing when focus is within the post (https://github.com/flarum/core/pull/3173).
- [A11Y] Missing accessible label for alert dismiss button (https://github.com/flarum/core/pull/3237).
- Error accessing the forum after saving a setting with more than 65k characters (https://github.com/flarum/core/pull/3162).
- Cannot restart queue from within (https://github.com/flarum/core/pull/3166).
- `Post--by-actor` not showing when comparing user instances (https://github.com/flarum/core/pull/3170).
- Incorrect typings for Modal `hide()` method (https://github.com/flarum/core/pull/3180).
- Avatar Upload throws errors with correct mimetype and incorrect extension (https://github.com/flarum/core/pull/3181).
- Clicking the dropdown button on a post opens all dropdowns in `Post-actions` (https://github.com/flarum/core/pull/3185).
- `getPlainContent()` causes external content to be fetched (https://github.com/flarum/core/pull/3193).
- `listItems` not accepting all `Mithril.Children` (https://github.com/flarum/core/pull/3176).
- Notifications mark as read option updates all notifications including the read ones (https://github.com/flarum/core/pull/3202).
- Post meta permalink not properly generated (https://github.com/flarum/core/pull/3216).
- Broken contribution link in README (https://github.com/flarum/core/pull/3211).
- `WelcomeHero` is displayed when content is empty (https://github.com/flarum/core/pull/3219).
- `last_activity_at, last_seen_at` updated on all API requests (https://github.com/flarum/core/pull/3231).
- `RememberMe` access token updated twice in API requests (https://github.com/flarum/core/pull/3233).
- Error in `funding` item in `composer.json` bricks the frontend (https://github.com/flarum/core/pull/3239).
- Escaped quotes in window title (https://github.com/flarum/core/pull/3264)
- `schedule:list` command fails due to missing timezone configuration.
### Deprecated
- Unused `evented` utility (https://github.com/flarum/core/pull/3125).
## [1.1.1](https://github.com/flarum/core/compare/v1.1.0...v1.1.1)
### Fixed
- Performance issue with very large communities.
## [1.1.0](https://github.com/flarum/core/compare/v1.0.4...v1.1.0)
### Added
- Info command now displays MySQL version, queue driver, mail driver (https://github.com/flarum/core/pull/2991)
- Use organization Prettier config (https://github.com/flarum/core/pull/2967)
- Support for global typings in extensions (https://github.com/flarum/core/pull/2992)
- Typings for class component state attribute (https://github.com/flarum/core/pull/2995)
- Custom colorising with CSS custom properties (https://github.com/flarum/core/pull/3001)
- Theme Extender to allow overriding LESS files (https://github.com/flarum/core/pull/3008)
- Update lastSeenAt when authenticating via API (https://github.com/flarum/core/pull/3058)
- NoJs Admin View (https://github.com/flarum/core/pull/3059)
- Preload FontAwesome, JS and CSS, and add `preload` extender (https://github.com/flarum/core/pull/3057)
### Changed
- Move Day.js plugin types import to global typings (https://github.com/flarum/core/pull/2954)
- Avoid resolving excluded middleware on each middleware items
- Allow extra attrs provided to `<Select>` to be passed through to the DOM element (https://github.com/flarum/core/pull/2959)
- Limit height of code blocks (https://github.com/flarum/core/pull/3012)
- Update normalize.css from v3.0.2 to v8.0.1 (https://github.com/flarum/core/pull/3015)
- Permission Grid: stick the headers to handle a lot of tags (https://github.com/flarum/core/pull/2887)
- Use `ItemList` for `DiscussionPage` content (https://github.com/flarum/core/pull/3004)
- Move email confirmation to POST request (https://github.com/flarum/core/pull/3038)
- Minor CSS code cleanup (https://github.com/flarum/core/pull/3026)
- Replace username with display name in more places (https://github.com/flarum/core/pull/3040)
- Rewrite Button to Typescript (https://github.com/flarum/core/pull/2984)
- Rewrite AdminPage abstract component into Typescript (https://github.com/flarum/core/pull/2996)
- Allow adding page parameters to PaginatedListState (https://github.com/flarum/core/pull/2935)
- Pass filter params to getApiDocument (https://github.com/flarum/core/pull/3037)
- Use author filter instead of gambit to get a user's discussions (https://github.com/flarum/core/pull/3068)
- [A11Y] Accessibility improvements for the Search component (https://github.com/flarum/core/pull/3017)
- Add determinsm to extension order resolution (https://github.com/flarum/core/pull/3076)
- Add cache control headers to the admin area (https://github.com/flarum/core/pull/3097)
### Fixed
- HLJS 11 new styles resulting in double padding (https://github.com/flarum/core/pull/2909)
- Internal API client attempting to load an uninstantiated session
- Empty post footer taking visual space (https://github.com/flarum/core/pull/2926)
- Unrecognized component class custom attribute typings (https://github.com/flarum/core/pull/2962)
- User edit groups permission not visually depending on view hidden groups permission (https://github.com/flarum/core/pull/2880)
- Event post excerpt preview triggers error (https://github.com/flarum/core/pull/2964)
- Missing settings defaults for display name driver and User slug driver (https://github.com/flarum/core/pull/2971)
- [A11Y] Icons not hidden from screenreaders (https://github.com/flarum/core/pull/3027)
- [A11Y] Checkboxes not focusable (https://github.com/flarum/core/pull/3014)
- Uploading ICO favicons resulting in server errors (https://github.com/flarum/core/pull/2949)
- Missing proper validation for large avatar upload payload (https://github.com/flarum/core/pull/3042)
- [A11Y] Missing focus rings in control elements (https://github.com/flarum/core/pull/3016)
- Unsanitised integer query parameters (https://github.com/flarum/core/pull/3064)
###### Code Contributors
@lhsazevedo, @Ornanovitch, @pierres, @the-turk, @iPurpl3x
###### Issue Reporters
@uamv, @dannyuk1982, @BurnNoticeSpy, @haarp, @peopleinside, @matteocontrini
## [1.0.4](https://github.com/flarum/core/compare/v1.0.3...v1.0.4)
### Fixed
- Upgrade to v1.0 resets the "view" permission on all tags (https://github.com/flarum/core/pull/2941)
## [1.0.3](https://github.com/flarum/core/compare/v1.0.2...v1.0.3) ## [1.0.3](https://github.com/flarum/core/compare/v1.0.2...v1.0.3)
### Changed ### Changed

View File

View File

@@ -9,6 +9,7 @@
<a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a> <a href="https://github.styleci.io/repos/28257573"><img src="https://github.styleci.io/repos/28257573/shield?style=flat" alt="StyleCI"></a>
</p> </p>
## About Flarum ## About Flarum
**[Flarum](https://flarum.org/) is a delightfully simple discussion platform for your website.** It's fast and easy to use, with all the features you need to run a successful community. It is designed to be: **[Flarum](https://flarum.org/) is a delightfully simple discussion platform for your website.** It's fast and easy to use, with all the features you need to run a successful community. It is designed to be:
@@ -19,15 +20,13 @@
* **Powerful and extensible.** Customize, extend, and integrate Flarum to suit your community. Flarums architecture is amazingly flexible, with a powerful Extension API. * **Powerful and extensible.** Customize, extend, and integrate Flarum to suit your community. Flarums architecture is amazingly flexible, with a powerful Extension API.
![Screenshot of a Flarum instance, showing multiple discussions and tags.](https://flarum.org/assets/img/home-screenshot.png)
## Installation ## Installation
This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum). For support, refer to the [documentation](https://docs.flarum.org/), and ask questions on [Flarum Discuss](https://discuss.flarum.org/) (our community forum) or [Discord server](https://flarum.org/discord/). This repository contains Flarum's core code. If you want to set up a forum, visit the [Flarum skeleton repository](https://github.com/flarum/flarum).
## Contributing ## Contributing
Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://docs.flarum.org/contributing)** to learn how you can help. Thank you for considering contributing to Flarum! Please read the **[Contributing guide](https://flarum.org/docs/contributing.html)** to learn how you can help.
## Security Vulnerabilities ## Security Vulnerabilities

View File

@@ -59,7 +59,7 @@
"illuminate/support": "^8.0", "illuminate/support": "^8.0",
"illuminate/validation": "^8.0", "illuminate/validation": "^8.0",
"illuminate/view": "^8.0", "illuminate/view": "^8.0",
"intervention/image": "2.5.* || ^2.6.1", "intervention/image": "^2.5.0",
"laminas/laminas-diactoros": "^2.4.1", "laminas/laminas-diactoros": "^2.4.1",
"laminas/laminas-httphandlerrunner": "^1.2.0", "laminas/laminas-httphandlerrunner": "^1.2.0",
"laminas/laminas-stratigility": "^3.2.2", "laminas/laminas-stratigility": "^3.2.2",
@@ -86,7 +86,7 @@
"wikimedia/less.php": "^3.0" "wikimedia/less.php": "^3.0"
}, },
"require-dev": { "require-dev": {
"flarum/testing": "^1.0.0" "flarum/testing": "1.0@dev"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -107,28 +107,6 @@
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.x-dev" "dev-master": "1.x-dev"
},
"flarum-cli": {
"excludeScaffolding": [
"LICENSE.md",
"js/tsconfig.json",
"js/webpack.config.js"
],
"modules": {
"backendTesting": true,
"js": true,
"gitConf": true,
"githubActions": true,
"prettier": true,
"typescript": true,
"bundlewatch": true,
"editorConfig": true,
"styleci": true,
"admin": true,
"forum": true,
"jsCommon": true,
"css": true
}
} }
}, },
"scripts": { "scripts": {

9
js/.gitignore vendored
View File

@@ -1,9 +0,0 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
node_modules

6
js/.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"printWidth": 150,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-3.1.1.cjs

39
js/@types/global/index.d.ts vendored Normal file
View File

@@ -0,0 +1,39 @@
// Mithril
import Mithril from 'mithril';
// Other third-party libs
import * as _dayjs from 'dayjs';
import * as _$ from 'jquery';
// Globals from flarum/core
import Application from '../../src/common/Application';
import type { TooltipJQueryFunction } from '../tooltips';
/**
* flarum/core exposes several extensions globally:
*
* - jQuery for convenient DOM manipulation
* - Mithril for VDOM and components
* - dayjs for date/time operations
*
* Since these are already part of the global namespace, extensions won't need
* to (and should not) bundle these themselves.
*/
declare global {
// $ is already defined by `@types/jquery`
const m: Mithril.Static;
const dayjs: typeof _dayjs;
// Extend JQuery with our custom functions, defined with $.fn
interface JQuery {
tooltip: TooltipJQueryFunction;
}
}
/**
* All global variables owned by flarum/core.
*/
declare global {
const app: Application;
}

View File

@@ -1,2 +1,2 @@
export * from './src/common'; export * from './src/common';
export * from './src/admin'; export * from './src/admin';

View File

@@ -1,138 +0,0 @@
declare type Writable<T> = { -readonly [P in keyof T]: T[P] };
declare type DeepWritable<T> = { -readonly [P in keyof T]: DeepWritable<T[P]> };
declare type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };
/**
* UTILITY TYPES
*/
/**
* Type that returns an array of all keys of a provided object that are of
* of the provided type, or a subtype of the type.
*/
declare type KeysOfType<Type extends object, Match> = {
[Key in keyof Type]-?: Type[Key] extends Match ? Key : never;
};
/**
* Type that matches one of the keys of an object that is of the provided
* type, or a subtype of it.
*/
declare type KeyOfType<Type extends object, Match> = KeysOfType<Type, Match>[keyof Type];
type Component<A> = import('mithril').Component<A>;
declare type ComponentClass<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = {
new (...args: any[]): Component<Attrs>;
prototype: C;
};
/**
* Unfortunately, TypeScript only supports strings and classes for JSX tags.
* Therefore, our type definition should only allow for those two types.
*
* @see https://github.com/microsoft/TypeScript/issues/14789#issuecomment-412247771
*/
declare type VnodeElementTag<Attrs = Record<string, unknown>, C extends Component<Attrs> = Component<Attrs>> = string | ComponentClass<Attrs, C>;
/**
* @deprecated Please import `app` from a namespace instead of using it as a global variable.
*
* @example App in forum JS
* ```
* import app from 'flarum/forum/app';
* ```
*
* @example App in admin JS
* ```
* import app from 'flarum/admin/app';
* ```
*
* @example App in common JS
* ```
* import app from 'flarum/common/app';
* ```
*/
declare const app: never;
declare const m: import('mithril').Static;
declare const dayjs: typeof import('dayjs');
/**
* From https://github.com/lokesh/color-thief/issues/188
*/
declare module 'color-thief-browser' {
type Color = [number, number, number];
export default class ColorThief {
getColor: (img: HTMLImageElement | null) => Color;
getPalette: (img: HTMLImageElement | null) => Color[];
}
}
type ESModule = { __esModule: true; [key: string]: unknown };
/**
* The global `flarum` variable.
*
* Contains the compiled ES Modules for all Flarum extensions and core.
*
* @example <caption>Check if `flarum-tags` is present</captions>
* if ('flarum-tags' in flarum.extensions) {
* // Tags is installed and enabled!
* }
*/
interface FlarumObject {
/**
* Contains the compiled ES Module for Flarum's core.
*
* You shouldn't need to access this directly for any reason.
*/
core: Readonly<ESModule>;
/**
* Contains the compiled ES Modules for all Flarum extensions.
*
* @example <caption>Check if `flarum-tags` is present</captions>
* if ('flarum-tags' in flarum.extensions) {
* // Tags is installed and enabled!
* }
*/
extensions: Readonly<Record<string, ESModule>>;
}
declare const flarum: FlarumObject;
// Extend JQuery with our custom functions, defined with $.fn
interface JQuery {
/**
* Flarum's tooltip JQuery plugin.
*
* Do not use this directly. Instead use the `<Tooltip>` component that
* is exported from `flarum/common/components/Tooltip`.
*
* This will be removed in a future version of Flarum.
*
* @deprecated
*/
tooltip: import('./tooltips/index').TooltipJQueryFunction;
}
/**
* For more info, see: https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking
*
* In a nutshell, we need to add `ElementAttributesProperty` to tell Typescript
* what property on component classes to look at for attribute typings. For our
* Component class, this would be `attrs` (e.g. `this.attrs...`)
*/
interface JSX {
ElementAttributesProperty: {
attrs: Record<string, unknown>;
};
}
interface Event {
/**
* Whether this event should trigger a Mithril redraw.
*/
redraw: boolean;
}

View File

@@ -0,0 +1,39 @@
// Mithril
import Mithril from 'mithril';
// Other third-party libs
import * as _dayjs from 'dayjs';
import * as _$ from 'jquery';
// Globals from flarum/core
import Application from '../../src/common/Application';
import type { TooltipJQueryFunction } from '../tooltips';
/**
* flarum/core exposes several extensions globally:
*
* - jQuery for convenient DOM manipulation
* - Mithril for VDOM and components
* - dayjs for date/time operations
*
* Since these are already part of the global namespace, extensions won't need
* to (and should not) bundle these themselves.
*/
declare global {
// $ is already defined by `@types/jquery`
const m: Mithril.Static;
const dayjs: typeof _dayjs;
// Extend JQuery with our custom functions, defined with $.fn
interface JQuery {
tooltip: TooltipJQueryFunction;
}
}
/**
* All global variables owned by flarum/core.
*/
declare global {
const app: Application;
}

View File

@@ -1,30 +1,3 @@
import Application from '../common/Application';
import ExtensionData from './utils/ExtensionData';
export declare type Extension = {
id: string;
version: string;
description?: string;
icon?: {
name: string;
};
links: {
authors?: {
name?: string;
link?: string;
}[];
discuss?: string;
documentation?: string;
support?: string;
website?: string;
donate?: string;
source?: string;
};
extra: {
'flarum-extension': {
title: string;
};
};
};
export default class AdminApplication extends Application { export default class AdminApplication extends Application {
extensionData: ExtensionData; extensionData: ExtensionData;
extensionCategories: { extensionCategories: {
@@ -35,27 +8,10 @@ export default class AdminApplication extends Application {
history: { history: {
canGoBack: () => boolean; canGoBack: () => boolean;
getPrevious: () => void; getPrevious: () => void;
backUrl: () => string; backUrl: () => any;
back: () => void; back: () => void;
}; };
/** getRequiredPermissions(permission: any): string[];
* Settings are serialized to the admin dashboard as strings.
* Additional encoding/decoding is possible, but must take
* place on the client side.
*
* @inheritdoc
*/
data: Application['data'] & {
extensions: Record<string, Extension>;
settings: Record<string, string>;
modelStatistics: Record<string, {
total: number;
}>;
};
constructor();
/**
* @inheritdoc
*/
mount(): void;
getRequiredPermissions(permission: string): string[];
} }
import Application from "../common/Application";
import ExtensionData from "./utils/ExtensionData";

View File

@@ -1,11 +1,11 @@
declare var _default: { declare var _default: {
extend: any; extend: typeof import("../common/extend");
Session: typeof import("../common/Session").default; Session: typeof import("../common/Session").default;
Store: typeof import("../common/Store").default; Store: typeof import("../common/Store").default;
'utils/BasicEditorDriver': typeof import("../common/utils/BasicEditorDriver").default; 'utils/BasicEditorDriver': typeof import("../common/utils/BasicEditorDriver").default;
'utils/evented': { 'utils/evented': {
handlers: Record<string, unknown>; handlers: Object;
getHandlers(event: string): Function[]; getHandlers(event: string): any[];
trigger(event: string, ...args: any[]): void; trigger(event: string, ...args: any[]): void;
on(event: string, handler: Function): void; on(event: string, handler: Function): void;
one(event: string, handler: Function): void; one(event: string, handler: Function): void;
@@ -32,15 +32,17 @@ declare var _default: {
'utils/subclassOf': typeof import("../common/utils/subclassOf").default; 'utils/subclassOf': typeof import("../common/utils/subclassOf").default;
'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default; 'utils/setRouteWithForcedRefresh': typeof import("../common/utils/setRouteWithForcedRefresh").default;
'utils/patchMithril': typeof import("../common/utils/patchMithril").default; 'utils/patchMithril': typeof import("../common/utils/patchMithril").default;
'utils/proxifyCompat': typeof import("../common/utils/proxifyCompat").default; 'utils/proxifyCompat': (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
'utils/classList': (...classes: import("clsx").ClassValue[]) => string; 'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
'utils/extractText': typeof import("../common/utils/extractText").default; 'utils/extractText': typeof import("../common/utils/extractText").default;
'utils/formatNumber': typeof import("../common/utils/formatNumber").default; 'utils/formatNumber': typeof import("../common/utils/formatNumber").default;
'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default; 'utils/mapRoutes': typeof import("../common/utils/mapRoutes").default;
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void; 'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce"); 'utils/throttleDebounce': typeof import("../common/utils/throttleDebounce");
'utils/isObject': typeof import("../common/utils/isObject").default;
'utils/focusTrap': typeof import("../common/utils/focusTrap");
'models/Notification': typeof import("../common/models/Notification").default; 'models/Notification': typeof import("../common/models/Notification").default;
'models/User': typeof import("../common/models/User").default; 'models/User': typeof import("../common/models/User").default;
'models/Post': typeof import("../common/models/Post").default; 'models/Post': typeof import("../common/models/Post").default;
@@ -67,7 +69,6 @@ declare var _default: {
'components/Link': typeof import("../common/components/Link").default; 'components/Link': typeof import("../common/components/Link").default;
'components/LinkButton': typeof import("../common/components/LinkButton").default; 'components/LinkButton': typeof import("../common/components/LinkButton").default;
'components/Checkbox': typeof import("../common/components/Checkbox").default; 'components/Checkbox': typeof import("../common/components/Checkbox").default;
'components/ColorPreviewInput': typeof import("../common/components/ColorPreviewInput").default;
'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default; 'components/SelectDropdown': typeof import("../common/components/SelectDropdown").default;
'components/ModalManager': typeof import("../common/components/ModalManager").default; 'components/ModalManager': typeof import("../common/components/ModalManager").default;
'components/Button': typeof import("../common/components/Button").default; 'components/Button': typeof import("../common/components/Button").default;
@@ -95,7 +96,6 @@ declare var _default: {
'utils/ExtensionData': typeof ExtensionData; 'utils/ExtensionData': typeof ExtensionData;
'utils/isExtensionEnabled': typeof isExtensionEnabled; 'utils/isExtensionEnabled': typeof isExtensionEnabled;
'utils/getCategorizedExtensions': typeof getCategorizedExtensions; 'utils/getCategorizedExtensions': typeof getCategorizedExtensions;
'utils/generateElementId': typeof generateElementId;
'components/SettingDropdown': typeof SettingDropdown; 'components/SettingDropdown': typeof SettingDropdown;
'components/EditCustomFooterModal': typeof EditCustomFooterModal; 'components/EditCustomFooterModal': typeof EditCustomFooterModal;
'components/SessionDropdown': typeof SessionDropdown; 'components/SessionDropdown': typeof SessionDropdown;
@@ -132,7 +132,6 @@ import saveSettings from "./utils/saveSettings";
import ExtensionData from "./utils/ExtensionData"; import ExtensionData from "./utils/ExtensionData";
import isExtensionEnabled from "./utils/isExtensionEnabled"; import isExtensionEnabled from "./utils/isExtensionEnabled";
import getCategorizedExtensions from "./utils/getCategorizedExtensions"; import getCategorizedExtensions from "./utils/getCategorizedExtensions";
import generateElementId from "./utils/generateElementId";
import SettingDropdown from "./components/SettingDropdown"; import SettingDropdown from "./components/SettingDropdown";
import EditCustomFooterModal from "./components/EditCustomFooterModal"; import EditCustomFooterModal from "./components/EditCustomFooterModal";
import SessionDropdown from "./components/SessionDropdown"; import SessionDropdown from "./components/SessionDropdown";

View File

@@ -1,4 +1,4 @@
export default class AdminHeader extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class AdminHeader extends Component<import("../../common/Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../../common/Component"; import Component from "../../common/Component";

View File

@@ -1,14 +1,14 @@
export default class AdminNav extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class AdminNav extends Component<import("../../common/Component").ComponentAttrs> {
constructor(); constructor();
query: Stream<string> | undefined; query: Stream<string> | undefined;
scrollToActive(): void; scrollToActive(): void;
/** /**
* Build an item list of main links to show in the admin navigation. * Build an item list of main links to show in the admin navigation.
* *
* @return {ItemList<import('mithril').Children>} * @return {ItemList}
*/ */
items(): ItemList<import('mithril').Children>; items(): ItemList;
extensionItems(): ItemList<any>; extensionItems(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import Stream from "../../common/utils/Stream"; import Stream from "../../common/utils/Stream";

View File

@@ -1,109 +1,17 @@
import type Mithril from 'mithril'; export default class AdminPage extends Page {
import Page, { IPageAttrs } from '../../common/components/Page'; settings: {} | undefined;
import Stream from '../../common/utils/Stream'; loading: boolean | undefined;
export interface AdminHeaderOptions { content(): string;
title: Mithril.Children; submitButton(): JSX.Element;
description: Mithril.Children; header(): JSX.Element;
icon: string; headerInfo(): {
/** className: string;
* Will be used as the class for the AdminPage. icon: string;
* title: string;
* Will also be appended with `-header` and set as the class for the `AdminHeader` component. description: string;
*/
className: string;
}
/**
* A type that matches any valid value for the `type` attribute on an HTML `<input>` element.
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-type
*
* Note: this will be exported from a different location in the future.
*
* @see https://github.com/flarum/core/issues/3039
*/
export declare type HTMLInputTypes = 'button' | 'checkbox' | 'color' | 'date' | 'datetime-local' | 'email' | 'file' | 'hidden' | 'image' | 'month' | 'number' | 'password' | 'radio' | 'range' | 'reset' | 'search' | 'submit' | 'tel' | 'text' | 'time' | 'url' | 'week';
interface CommonSettingsItemOptions extends Mithril.Attributes {
setting: string;
label: Mithril.Children;
help?: Mithril.Children;
className?: string;
}
/**
* Valid options for the setting component builder to generate an HTML input element.
*/
export interface HTMLInputSettingsComponentOptions extends CommonSettingsItemOptions {
/**
* Any valid HTML input `type` value.
*/
type: HTMLInputTypes;
}
declare const BooleanSettingTypes: readonly ["bool", "checkbox", "switch", "boolean"];
declare const SelectSettingTypes: readonly ["select", "dropdown", "selectdropdown"];
declare const TextareaSettingTypes: readonly ["textarea"];
declare const ColorPreviewSettingType = "color-preview";
/**
* Valid options for the setting component builder to generate a Switch.
*/
export interface SwitchSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof BooleanSettingTypes[number];
}
/**
* Valid options for the setting component builder to generate a Select dropdown.
*/
export interface SelectSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof SelectSettingTypes[number];
/**
* Map of values to their labels
*/
options: {
[value: string]: Mithril.Children;
}; };
default: string;
}
/**
* Valid options for the setting component builder to generate a Textarea.
*/
export interface TextareaSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof TextareaSettingTypes[number];
}
/**
* Valid options for the setting component builder to generate a ColorPreviewInput.
*/
export interface ColorPreviewSettingComponentOptions extends CommonSettingsItemOptions {
type: typeof ColorPreviewSettingType;
}
/**
* All valid options for the setting component builder.
*/
export declare type SettingsComponentOptions = HTMLInputSettingsComponentOptions | SwitchSettingComponentOptions | SelectSettingComponentOptions | TextareaSettingComponentOptions | ColorPreviewSettingComponentOptions;
/**
* Valid attrs that can be returned by the `headerInfo` function
*/
export declare type AdminHeaderAttrs = AdminHeaderOptions & Partial<Omit<Mithril.Attributes, 'class'>>;
export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAttrs> extends Page<CustomAttrs> {
settings: Record<string, Stream<string>>;
loading: boolean;
view(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/** /**
* Returns the content of the AdminPage. * buildSettingComponent takes a settings object and turns it into a component.
*/
abstract content(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the submit button for this AdminPage.
*
* Calls `this.saveSettings` when the button is clicked.
*/
submitButton(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the Header component for this AdminPage.
*/
header(vnode: Mithril.Vnode<CustomAttrs, this>): Mithril.Children;
/**
* Returns the options passed to the AdminHeader component.
*/
headerInfo(): AdminHeaderAttrs;
/**
* `buildSettingComponent` takes a settings object and turns it into a component.
* Depending on the type of input, you can set the type to 'bool', 'select', or * Depending on the type of input, you can set the type to 'bool', 'select', or
* any standard <input> type. Any values inside the 'extra' object will be added * any standard <input> type. Any values inside the 'extra' object will be added
* to the component as an attribute. * to the component as an attribute.
@@ -134,34 +42,14 @@ export default abstract class AdminPage<CustomAttrs extends IPageAttrs = IPageAt
* default: 'option1', * default: 'option1',
* } * }
* *
* @example * @param setting
* * @returns {JSX.Element}
* () => {
* return <p>My cool component</p>;
* }
*/
buildSettingComponent(entry: ((this: this) => Mithril.Children) | SettingsComponentOptions): Mithril.Children;
/**
* Called when `saveSettings` completes successfully.
*/ */
buildSettingComponent(entry: any): JSX.Element;
onsaved(): void; onsaved(): void;
/** setting(key: any, fallback?: string): any;
* Returns a function that fetches the setting from the `app` global. dirty(): {};
*/
setting(key: string, fallback?: string): Stream<string>;
/**
* Returns a map of settings keys to values which includes only those which have been modified but not yet saved.
*/
dirty(): Record<string, string>;
/**
* Returns the number of settings that have been modified.
*/
isChanged(): number; isChanged(): number;
/** saveSettings(e: any): Promise<void>;
* Saves the modified settings to the database.
*/
saveSettings(e: SubmitEvent & {
redraw: boolean;
}): Promise<void>;
} }
export {}; import Page from "../../common/components/Page";

View File

@@ -1,6 +1,3 @@
export default class AppearancePage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class AppearancePage extends AdminPage {
constructor();
colorItems(): ItemList<any>;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList";

View File

@@ -1,5 +1,4 @@
export default class BasicsPage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class BasicsPage extends AdminPage {
constructor();
localeOptions: {} | undefined; localeOptions: {} | undefined;
displayNameOptions: {} | undefined; displayNameOptions: {} | undefined;
slugDriverOptions: {} | undefined; slugDriverOptions: {} | undefined;
@@ -7,12 +6,10 @@ export default class BasicsPage extends AdminPage<import("../../common/component
* Build a list of options for the default homepage. Each option must be an * Build a list of options for the default homepage. Each option must be an
* object with `path` and `label` properties. * object with `path` and `label` properties.
* *
* @return {ItemList<{ path: string, label: import('mithril').Children }>} * @return {ItemList}
* @public
*/ */
homePageItems(): ItemList<{ public homePageItems(): ItemList;
path: string;
label: import('mithril').Children;
}>;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,6 +1,5 @@
export default class DashboardPage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class DashboardPage extends AdminPage {
constructor(); availableWidgets(): ItemList;
availableWidgets(): ItemList<any>;
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,16 +1,16 @@
export default class DashboardWidget extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class DashboardWidget extends Component<import("../../common/Component").ComponentAttrs> {
constructor(); constructor();
/** /**
* Get the class name to apply to the widget. * Get the class name to apply to the widget.
* *
* @return {string} * @return {String}
*/ */
className(): string; className(): string;
/** /**
* Get the content of the widget. * Get the content of the widget.
* *
* @return {import('mithril').Children} * @return {VirtualElement}
*/ */
content(): import('mithril').Children; content(): any;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";

View File

@@ -2,15 +2,14 @@
* The `EditGroupModal` component shows a modal dialog which allows the user * The `EditGroupModal` component shows a modal dialog which allows the user
* to create or edit a group. * to create or edit a group.
*/ */
export default class EditGroupModal extends Modal<import("../../common/components/Modal").IInternalModalAttrs> { export default class EditGroupModal extends Modal {
constructor();
group: any; group: any;
nameSingular: Stream<any> | undefined; nameSingular: Stream<any> | undefined;
namePlural: Stream<any> | undefined; namePlural: Stream<any> | undefined;
icon: Stream<any> | undefined; icon: Stream<any> | undefined;
color: Stream<any> | undefined; color: Stream<any> | undefined;
isHidden: Stream<any> | undefined; isHidden: Stream<any> | undefined;
fields(): ItemList<any>; fields(): ItemList;
submitData(): { submitData(): {
nameSingular: any; nameSingular: any;
namePlural: any; namePlural: any;

View File

@@ -1,5 +1,5 @@
export default class ExtensionLinkButton extends LinkButton { export default class ExtensionLinkButton extends LinkButton {
statusItems(name: any): ItemList<any>; statusItems(name: any): ItemList;
} }
import LinkButton from "../../common/components/LinkButton"; import LinkButton from "../../common/components/LinkButton";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,15 +1,6 @@
import ItemList from '../../common/utils/ItemList'; export default class ExtensionPage extends AdminPage {
import AdminPage from './AdminPage'; extension: any;
import RequestError from '../../common/utils/RequestError'; changingState: boolean | undefined;
import { Extension } from '../AdminApplication';
import { IPageAttrs } from '../../common/components/Page';
import type Mithril from 'mithril';
export interface ExtensionPageAttrs extends IPageAttrs {
id: string;
}
export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> extends AdminPage<Attrs> {
extension: Extension;
changingState: boolean;
infoFields: { infoFields: {
discuss: string; discuss: string;
documentation: string; documentation: string;
@@ -17,16 +8,14 @@ export default class ExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionP
website: string; website: string;
donate: string; donate: string;
source: string; source: string;
}; } | undefined;
oninit(vnode: Mithril.Vnode<Attrs, this>): void;
className(): string; className(): string;
view(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element | null; sections(): ItemList;
header(): JSX.Element[]; topItems(): ItemList;
sections(vnode: Mithril.VnodeDOM<Attrs, this>): ItemList<unknown>; infoItems(): ItemList;
content(vnode: Mithril.VnodeDOM<Attrs, this>): JSX.Element;
topItems(): ItemList<Mithril.Children>;
infoItems(): ItemList<Mithril.Children>;
toggle(): void; toggle(): void;
isEnabled(): any; isEnabled(): any;
onerror(e: RequestError): void; onerror(e: any): void;
} }
import AdminPage from "./AdminPage";
import ItemList from "../../common/utils/ItemList";

View File

@@ -1,19 +1,4 @@
import PermissionGrid, { PermissionGridEntry } from './PermissionGrid'; export default class ExtensionPermissionGrid extends PermissionGrid {
import ItemList from '../../common/utils/ItemList'; extensionId: any;
import Mithril from 'mithril';
export interface IExtensionPermissionGridAttrs {
extensionId: string;
}
export default class ExtensionPermissionGrid<CustomAttrs extends IExtensionPermissionGridAttrs = IExtensionPermissionGridAttrs> extends PermissionGrid<CustomAttrs> {
protected extensionId: string;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
permissionItems(): ItemList<{
label: Mithril.Children;
children: PermissionGridEntry[];
}>;
viewItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
startItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
replyItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
moderateItems(): ItemList<import("./PermissionGrid").PermissionConfig>;
scopeControlItems(): ItemList<unknown>;
} }
import PermissionGrid from "./PermissionGrid";

View File

@@ -2,15 +2,15 @@
* The `HeaderPrimary` component displays primary header controls. On the * The `HeaderPrimary` component displays primary header controls. On the
* default skin, these are shown just to the right of the forum title. * default skin, these are shown just to the right of the forum title.
*/ */
export default class HeaderPrimary extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class HeaderPrimary extends Component<import("../../common/Component").ComponentAttrs> {
constructor(); constructor();
config(isInitialized: any, context: any): void; config(isInitialized: any, context: any): void;
/** /**
* Build an item list for the controls. * Build an item list for the controls.
* *
* @return {ItemList<import('mithril').Children>} * @return {ItemList}
*/ */
items(): ItemList<import('mithril').Children>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,14 +1,14 @@
/** /**
* The `HeaderSecondary` component displays secondary header controls. * The `HeaderSecondary` component displays secondary header controls.
*/ */
export default class HeaderSecondary extends Component<import("../../common/Component").ComponentAttrs, undefined> { export default class HeaderSecondary extends Component<import("../../common/Component").ComponentAttrs> {
constructor(); constructor();
/** /**
* Build an item list for the controls. * Build an item list for the controls.
* *
* @return {ItemList<import('mithril').Children>} * @return {ItemList}
*/ */
items(): ItemList<import('mithril').Children>; items(): ItemList;
} }
import Component from "../../common/Component"; import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,14 +1,3 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> export default class LoadingModal extends Modal {
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
export interface ILoadingModalAttrs extends IInternalModalAttrs {
}
export default class LoadingModal<ModalAttrs extends ILoadingModalAttrs = ILoadingModalAttrs> extends Modal<ModalAttrs> {
/**
* @inheritdoc
*/
static readonly isDismissible: boolean;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): string;
onsubmit(e: Event): void;
} }
import Modal from "../../common/components/Modal";

View File

@@ -1,5 +1,4 @@
export default class MailPage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class MailPage extends AdminPage {
constructor();
sendingTest: boolean | undefined; sendingTest: boolean | undefined;
refresh(): void; refresh(): void;
status: { status: {

View File

@@ -1,36 +1,12 @@
import Component, { ComponentAttrs } from '../../common/Component'; export default class PermissionGrid extends Component<import("../../common/Component").ComponentAttrs> {
import ItemList from '../../common/utils/ItemList'; constructor();
import type Mithril from 'mithril'; permissionItems(): ItemList;
export interface PermissionConfig { viewItems(): ItemList;
permission: string; startItems(): ItemList;
icon: string; replyItems(): ItemList;
label: Mithril.Children; moderateItems(): ItemList;
allowGuest?: boolean; scopeItems(): ItemList;
} scopeControlItems(): ItemList;
export interface PermissionSetting {
setting: () => Mithril.Children;
icon: string;
label: Mithril.Children;
}
export declare type PermissionGridEntry = PermissionConfig | PermissionSetting;
export declare type PermissionType = 'view' | 'start' | 'reply' | 'moderate';
export interface ScopeItem {
label: Mithril.Children;
render: (permission: PermissionGridEntry) => Mithril.Children;
onremove?: () => void;
}
export interface IPermissionGridAttrs extends ComponentAttrs {
}
export default class PermissionGrid<CustomAttrs extends IPermissionGridAttrs = IPermissionGridAttrs> extends Component<CustomAttrs> {
view(vnode: Mithril.Vnode<CustomAttrs, this>): JSX.Element;
permissionItems(): ItemList<{
label: Mithril.Children;
children: PermissionGridEntry[];
}>;
viewItems(): ItemList<PermissionGridEntry>;
startItems(): ItemList<PermissionGridEntry>;
replyItems(): ItemList<PermissionGridEntry>;
moderateItems(): ItemList<PermissionGridEntry>;
scopeItems(): ItemList<ScopeItem>;
scopeControlItems(): ItemList<unknown>;
} }
import Component from "../../common/Component";
import ItemList from "../../common/utils/ItemList";

View File

@@ -1,4 +1,3 @@
export default class PermissionsPage extends AdminPage<import("../../common/components/Page").IPageAttrs> { export default class PermissionsPage extends AdminPage {
constructor();
} }
import AdminPage from "./AdminPage"; import AdminPage from "./AdminPage";

View File

@@ -1,18 +0,0 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" />
import Modal, { IInternalModalAttrs } from '../../common/components/Modal';
import ExtensionReadme from '../models/ExtensionReadme';
import type Mithril from 'mithril';
import type { Extension } from '../AdminApplication';
export interface IReadmeModalAttrs extends IInternalModalAttrs {
extension: Extension;
}
export default class ReadmeModal<CustomAttrs extends IReadmeModalAttrs = IReadmeModalAttrs> extends Modal<CustomAttrs> {
protected name: string;
protected extName: string;
protected readme: ExtensionReadme;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): JSX.Element;
loadReadme(): Promise<void>;
}

View File

@@ -8,7 +8,7 @@ export default class SessionDropdown extends Dropdown {
* *
* @return {ItemList} * @return {ItemList}
*/ */
items(): ItemList<any>; items(): ItemList;
} }
import Dropdown from "../../common/components/Dropdown"; import Dropdown from "../../common/components/Dropdown";
import ItemList from "../../common/utils/ItemList"; import ItemList from "../../common/utils/ItemList";

View File

@@ -1,5 +1,4 @@
export default class SettingsModal extends Modal<import("../../common/components/Modal").IInternalModalAttrs> { export default class SettingsModal extends Modal {
constructor();
settings: {} | undefined; settings: {} | undefined;
form(): string; form(): string;
submitButton(): JSX.Element; submitButton(): JSX.Element;

View File

@@ -1,6 +1,5 @@
export default class StatusWidget extends DashboardWidget { export default class StatusWidget extends DashboardWidget {
items(): ItemList<any>; items(): ItemList;
toolsItems(): ItemList<any>;
handleClearCache(e: any): void; handleClearCache(e: any): void;
} }
import DashboardWidget from "./DashboardWidget"; import DashboardWidget from "./DashboardWidget";

View File

@@ -1,5 +1,4 @@
export default class UploadImageButton extends Button<import("../../common/components/Button").IButtonAttrs> { export default class UploadImageButton extends Button {
constructor();
loading: boolean; loading: boolean;
/** /**
* Prompt the user to upload an image. * Prompt the user to upload an image.
@@ -13,16 +12,16 @@ export default class UploadImageButton extends Button<import("../../common/compo
/** /**
* After a successful upload/removal, reload the page. * After a successful upload/removal, reload the page.
* *
* @param {object} response * @param {Object} response
* @protected * @protected
*/ */
protected success(response: object): void; protected success(response: Object): void;
/** /**
* If upload/removal fails, stop loading. * If upload/removal fails, stop loading.
* *
* @param {object} response * @param {Object} response
* @protected * @protected
*/ */
protected failure(response: object): void; protected failure(response: Object): void;
} }
import Button from "../../common/components/Button"; import Button from "../../common/components/Button";

View File

@@ -1,18 +1,6 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> /// <reference types="mithril" />
import type Mithril from 'mithril';
import type User from '../../common/models/User';
import ItemList from '../../common/utils/ItemList'; import ItemList from '../../common/utils/ItemList';
import AdminPage from './AdminPage'; import AdminPage from './AdminPage';
declare type ColumnData = {
/**
* Column title
*/
name: Mithril.Children;
/**
* Component(s) to show for this column.
*/
content: (user: User) => Mithril.Children;
};
/** /**
* Admin page which displays a paginated list of all users on the forum. * Admin page which displays a paginated list of all users on the forum.
*/ */
@@ -62,12 +50,12 @@ export default class UserListPage extends AdminPage {
* *
* See `UserListPage.tsx` for examples. * See `UserListPage.tsx` for examples.
*/ */
columns(): ItemList<ColumnData>; columns(): ItemList;
headerInfo(): { headerInfo(): {
className: string; className: string;
icon: string; icon: string;
title: import("@askvortsov/rich-icu-message-formatter").NestedStringArray; title: any;
description: import("@askvortsov/rich-icu-message-formatter").NestedStringArray; description: any;
}; };
/** /**
* Asynchronously fetch the next set of users to be rendered. * Asynchronously fetch the next set of users to be rendered.
@@ -82,4 +70,3 @@ export default class UserListPage extends AdminPage {
nextPage(): void; nextPage(): void;
previousPage(): void; previousPage(): void;
} }
export {};

View File

@@ -1,3 +1,5 @@
import app from './app'; import app from './app';
export { app }; export { app };
export declare const compat: Record<string, unknown>; export declare const compat: {
[key: string]: any;
};

View File

@@ -1,4 +0,0 @@
export default class ExtensionReadme extends Model {
content: () => any;
}
import Model from "../../common/Model";

View File

@@ -1,10 +1,9 @@
import DefaultResolver from '../../common/resolvers/DefaultResolver'; import DefaultResolver from '../../common/resolvers/DefaultResolver';
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
/** /**
* A custom route resolver for ExtensionPage that generates handles routes * A custom route resolver for ExtensionPage that generates handles routes
* to default extension pages or a page provided by an extension. * to default extension pages or a page provided by an extension.
*/ */
export default class ExtensionPageResolver<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs, RouteArgs extends Record<string, unknown> = {}> extends DefaultResolver<Attrs, ExtensionPage<Attrs>, RouteArgs> { export default class ExtensionPageResolver extends DefaultResolver {
static extension: string | null; static extension: string | null;
onmatch(args: Attrs & RouteArgs, requestedPath: string, route: string): new () => ExtensionPage<Attrs>; onmatch(args: any, requestedPath: any, route: any): any;
} }

View File

@@ -1,5 +1,6 @@
import AdminApplication from './AdminApplication';
/** /**
* The `routes` initializer defines the forum app's routes. * The `routes` initializer defines the forum app's routes.
*
* @param {App} app
*/ */
export default function (app: AdminApplication): void; export default function _default(app: any): void;

View File

@@ -1,46 +1,17 @@
import type Mithril from 'mithril';
import ItemList from '../../common/utils/ItemList';
import { SettingsComponentOptions } from '../components/AdminPage';
import ExtensionPage, { ExtensionPageAttrs } from '../components/ExtensionPage';
import { PermissionConfig, PermissionType } from '../components/PermissionGrid';
declare type SettingConfigInput = SettingsComponentOptions | (() => Mithril.Children);
declare type SettingConfigInternal = SettingsComponentOptions | ((() => Mithril.Children) & {
setting: string;
});
export declare type CustomExtensionPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs> = new () => ExtensionPage<Attrs>;
declare type ExtensionConfig = {
settings?: ItemList<SettingConfigInternal>;
permissions?: {
view?: ItemList<PermissionConfig>;
start?: ItemList<PermissionConfig>;
reply?: ItemList<PermissionConfig>;
moderate?: ItemList<PermissionConfig>;
};
page?: CustomExtensionPage;
};
declare type InnerDataNoActiveExtension = {
currentExtension: null;
data: {
[key: string]: ExtensionConfig | undefined;
};
};
declare type InnerDataActiveExtension = {
currentExtension: string;
data: {
[key: string]: ExtensionConfig;
};
};
export default class ExtensionData { export default class ExtensionData {
protected state: InnerDataActiveExtension | InnerDataNoActiveExtension; data: {};
currentExtension: any;
/** /**
* This function simply takes the extension id * This function simply takes the extension id
* *
* @example * @example
* app.extensionData.for('flarum-tags') * app.extensionData.load('flarum-tags')
* *
* flarum/flags -> flarum-flags | acme/extension -> acme-extension * flarum/flags -> flarum-flags | acme/extension -> acme-extension
*
* @param extension
*/ */
for(extension: string): this; for(extension: any): ExtensionData;
/** /**
* This function registers your settings with Flarum * This function registers your settings with Flarum
* *
@@ -53,8 +24,13 @@ export default class ExtensionData {
* type: 'text', // This will be inputted into the input tag for the setting (text/number/etc) * type: 'text', // This will be inputted into the input tag for the setting (text/number/etc)
* label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label') * label: app.translator.trans('flarum-flags.admin.settings.guidelines_url_label')
* }, 15) // priority is optional (ItemList) * }, 15) // priority is optional (ItemList)
*
*
* @param content
* @param priority
* @returns {ExtensionData}
*/ */
registerSetting(content: SettingConfigInput, priority?: number): this; registerSetting(content: any, priority?: number): ExtensionData;
/** /**
* This function registers your permission with Flarum * This function registers your permission with Flarum
* *
@@ -65,32 +41,58 @@ export default class ExtensionData {
* label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'), * label: app.translator.trans('flarum-flags.admin.permissions.view_flags_label'),
* permission: 'discussion.viewFlags' * permission: 'discussion.viewFlags'
* }, 'moderate', 65) * }, 'moderate', 65)
*
* @param content
* @param permissionType
* @param priority
* @returns {ExtensionData}
*/ */
registerPermission(content: PermissionConfig, permissionType: PermissionType, priority?: number): this; registerPermission(content: any, permissionType?: any, priority?: number): ExtensionData;
/** /**
* Replace the default extension page with a custom component. * Replace the default extension page with a custom component.
* This component would typically extend ExtensionPage * This component would typically extend ExtensionPage
*
* @param component
* @returns {ExtensionData}
*/ */
registerPage(component: CustomExtensionPage): this; registerPage(component: any): ExtensionData;
/** /**
* Get an extension's registered settings * Get an extension's registered settings
*
* @param extensionId
* @returns {boolean|*}
*/ */
getSettings(extensionId: string): SettingConfigInternal[] | undefined; getSettings(extensionId: any): boolean | any;
/** /**
*
* Get an ItemList of all extensions' registered permissions * Get an ItemList of all extensions' registered permissions
*
* @param extension
* @param type
* @returns {ItemList}
*/ */
getAllExtensionPermissions(type: PermissionType): ItemList<PermissionConfig>; getAllExtensionPermissions(type: any): ItemList;
/** /**
* Get a singular extension's registered permissions * Get a singular extension's registered permissions
*
* @param extension
* @param type
* @returns {boolean|*}
*/ */
getExtensionPermissions(extension: string, type: PermissionType): ItemList<PermissionConfig>; getExtensionPermissions(extension: any, type: any): boolean | any;
/** /**
* Checks whether a given extension has registered permissions. * Checks whether a given extension has registered permissions.
*
* @param extension
* @returns {boolean}
*/ */
extensionHasPermissions(extension: string): boolean; extensionHasPermissions(extension: any): boolean;
/** /**
* Returns an extension's custom page component if it exists. * Returns an extension's custom page component if it exists.
*
* @param extension
* @returns {boolean|*}
*/ */
getPage<Attrs extends ExtensionPageAttrs = ExtensionPageAttrs>(extension: string): CustomExtensionPage<Attrs> | undefined; getPage(extension: any): boolean | any;
} }
export {}; import ItemList from "../../common/utils/ItemList";

View File

@@ -1 +0,0 @@
export { nanoid as default } from 'nanoid';

View File

@@ -1,96 +1,3 @@
import ItemList from './utils/ItemList';
import Translator from './Translator';
import Store, { ApiPayload, ApiResponsePlural, ApiResponseSingle } from './Store';
import Session from './Session';
import Drawer from './utils/Drawer';
import RequestError, { InternalFlarumRequestOptions } from './utils/RequestError';
import Forum from './models/Forum';
import PageState from './states/PageState';
import ModalManagerState from './states/ModalManagerState';
import AlertManagerState from './states/AlertManagerState';
import type DefaultResolver from './resolvers/DefaultResolver';
import type Mithril from 'mithril';
import type Component from './Component';
import type { ComponentAttrs } from './Component';
import Model, { SavedModelData } from './Model';
export declare type FlarumScreens = 'phone' | 'tablet' | 'desktop' | 'desktop-hd';
export declare type FlarumGenericRoute = RouteItem<any, any, any>;
export interface FlarumRequestOptions<ResponseType> extends Omit<Mithril.RequestOptions<ResponseType>, 'extract'> {
errorHandler?: (error: RequestError) => void;
url: string;
/**
* Manipulate the response text before it is parsed into JSON.
*
* @deprecated Please use `modifyText` instead.
*/
extract?: (responseText: string) => string;
/**
* Manipulate the response text before it is parsed into JSON.
*
* This overrides any `extract` method provided.
*/
modifyText?: (responseText: string) => string;
}
/**
* A valid route definition.
*/
export declare type RouteItem<Attrs extends ComponentAttrs, Comp extends Component<Attrs & {
routeName: string;
}>, RouteArgs extends Record<string, unknown> = {}> = {
/**
* The path for your route.
*
* This might be a specific URL path (e.g.,`/myPage`), or it might
* contain a variable used by a resolver (e.g., `/myPage/:id`).
*
* @see https://docs.flarum.org/extend/frontend-pages.html#route-resolvers-advanced
*/
path: `/${string}`;
} & ({
/**
* The component to render when this route matches.
*/
component: new () => Comp;
/**
* A custom resolver class.
*
* This should be the class itself, and **not** an instance of the
* class.
*/
resolverClass?: new (component: new () => Comp, routeName: string) => DefaultResolver<Attrs, Comp, RouteArgs>;
} | {
/**
* An instance of a route resolver.
*/
resolver: RouteResolver<Attrs, Comp, RouteArgs>;
});
export interface RouteResolver<Attrs extends ComponentAttrs, Comp extends Component<Attrs & {
routeName: string;
}>, RouteArgs extends Record<string, unknown> = {}> {
/**
* A method which selects which component to render based on
* conditional logic.
*
* Returns the component class, and **not** a Vnode or JSX
* expression.
*
* @see https://mithril.js.org/route.html#routeresolveronmatch
*/
onmatch(this: this, args: RouteArgs, requestedPath: string, route: string): {
new (): Comp;
};
/**
* A function which renders the provided component.
*
* If not specified, the route will default to rendering the
* component on its own, inside of a fragment.
*
* Returns a Mithril Vnode or other children.
*
* @see https://mithril.js.org/route.html#routeresolverrender
*/
render?(this: this, vnode: Mithril.Vnode<Attrs, Comp>): Mithril.Children;
}
/** /**
* The `App` class provides a container for an application, as well as various * The `App` class provides a container for an application, as well as various
* utilities for the rest of the app to use. * utilities for the rest of the app to use.
@@ -98,8 +5,11 @@ export interface RouteResolver<Attrs extends ComponentAttrs, Comp extends Compon
export default class Application { export default class Application {
/** /**
* The forum model for this application. * The forum model for this application.
*
* @type {Forum}
* @public
*/ */
forum: Forum; public forum: Forum;
/** /**
* A map of routes, keyed by a unique route name. Each route is an object * A map of routes, keyed by a unique route name. Each route is an object
* containing the following properties: * containing the following properties:
@@ -108,42 +18,71 @@ export default class Application {
* - `component` The Mithril component to render when this route is active. * - `component` The Mithril component to render when this route is active.
* *
* @example * @example
* app.routes.discussion = { path: '/d/:id', component: DiscussionPage }; * app.routes.discussion = {path: '/d/:id', component: DiscussionPage.component()};
*
* @type {Object}
* @public
*/ */
routes: Record<string, FlarumGenericRoute>; public routes: Object;
/** /**
* An ordered list of initializers to bootstrap the application. * An ordered list of initializers to bootstrap the application.
*
* @type {ItemList}
* @public
*/ */
initializers: ItemList<(app: this) => void>; public initializers: ItemList;
/** /**
* The app's session. * The app's session.
* *
* Stores info about the current user. * @type {Session}
* @public
*/ */
session: Session; public session: Session;
/** /**
* The app's translator. * The app's translator.
*
* @type {Translator}
* @public
*/ */
translator: Translator; public translator: Translator;
/** /**
* The app's data store. * The app's data store.
*
* @type {Store}
* @public
*/ */
store: Store; public store: Store;
/** /**
* A local cache that can be used to store data at the application level, so * A local cache that can be used to store data at the application level, so
* that is persists between different routes. * that is persists between different routes.
*
* @type {Object}
* @public
*/ */
cache: Record<string, unknown>; public cache: Object;
/** /**
* Whether or not the app has been booted. * Whether or not the app has been booted.
*
* @type {Boolean}
* @public
*/ */
booted: boolean; public booted: boolean;
/**
* The key for an Alert that was shown as a result of an AJAX request error.
* If present, it will be dismissed on the next successful request.
*
* @type {int}
* @private
*/
private requestErrorAlert;
/** /**
* The page the app is currently on. * The page the app is currently on.
* *
* This object holds information about the type of page we are currently * This object holds information about the type of page we are currently
* visiting, and sometimes additional arbitrary page state that may be * visiting, and sometimes additional arbitrary page state that may be
* relevant to lower-level components. * relevant to lower-level components.
*
* @type {PageState}
*/ */
current: PageState; current: PageState;
/** /**
@@ -152,86 +91,84 @@ export default class Application {
* Once the application navigates to another page, the object previously * Once the application navigates to another page, the object previously
* assigned to this.current will be moved to this.previous, while this.current * assigned to this.current will be moved to this.previous, while this.current
* is re-initialized. * is re-initialized.
*
* @type {PageState}
*/ */
previous: PageState; previous: PageState;
/**
* An object that manages modal state.
*/
modal: ModalManagerState; modal: ModalManagerState;
/** /**
* An object that manages the state of active alerts. * An object that manages the state of active alerts.
*
* @type {AlertManagerState}
*/ */
alerts: AlertManagerState; alerts: AlertManagerState;
/** data: any;
* An object that manages the state of the navigation drawer. title: string;
*/ titleCount: number;
drawer: Drawer; initialRoute: any;
data: { load(payload: any): void;
apiDocument: ApiPayload | null;
locale: string;
locales: Record<string, string>;
resources: SavedModelData[];
session: {
userId: number;
csrfToken: string;
};
[key: string]: unknown;
};
private _title;
private _titleCount;
private set title(value);
get title(): string;
private set titleCount(value);
get titleCount(): number;
/**
* The key for an Alert that was shown as a result of an AJAX request error.
* If present, it will be dismissed on the next successful request.
*/
private requestErrorAlert;
initialRoute: string;
load(payload: Application['data']): void;
boot(): void; boot(): void;
bootExtensions(extensions: Record<string, { bootExtensions(extensions: any): void;
extend?: unknown[]; mount(basePath?: string): void;
}>): void; drawer: Drawer | undefined;
protected mount(basePath?: string): void;
/** /**
* Get the API response document that has been preloaded into the application. * Get the API response document that has been preloaded into the application.
*
* @return {Object|null}
* @public
*/ */
preloadedApiDocument<M extends Model>(): ApiResponseSingle<M> | null; public preloadedApiDocument(): Object | null;
preloadedApiDocument<Ms extends Model[]>(): ApiResponsePlural<Ms[number]> | null;
/** /**
* Determine the current screen mode, based on our media queries. * Determine the current screen mode, based on our media queries.
*/
screen(): FlarumScreens;
/**
* Set the `<title>` of the page.
* *
* @param title New page title * @returns {String} - one of "phone", "tablet", "desktop" or "desktop-hd"
*/ */
setTitle(title: string): void; screen(): string;
/** /**
* Set a number to display in the `<title>` of the page. * Set the <title> of the page.
* *
* @param count Number to display in title * @param {String} title
* @public
*/ */
setTitleCount(count: number): void; public setTitle(title: string): void;
/**
* Set a number to display in the <title> of the page.
*
* @param {Integer} count
*/
setTitleCount(count: any): void;
updateTitle(): void; updateTitle(): void;
protected transformRequestOptions<ResponseType>(flarumOptions: FlarumRequestOptions<ResponseType>): InternalFlarumRequestOptions<ResponseType>;
/** /**
* Make an AJAX request, handling any low-level errors that may occur. * Make an AJAX request, handling any low-level errors that may occur.
* *
* @see https://mithril.js.org/request.html * @see https://mithril.js.org/request.html
* @param {Object} options
* @return {Promise}
* @public
*/ */
request<ResponseType>(originalOptions: FlarumRequestOptions<ResponseType>): Promise<ResponseType>; public request(originalOptions: any): Promise<any>;
/** /**
* By default, show an error alert, and log the error to the console. * @param {RequestError} error
* @param {string[]} [formattedError]
* @private
*/ */
protected requestErrorCatch<ResponseType>(error: RequestError, customErrorHandler: FlarumRequestOptions<ResponseType>['errorHandler']): Promise<never>;
protected requestErrorDefaultHandler(e: unknown, isDebug: boolean, formattedErrors: string[]): void;
private showDebug; private showDebug;
/** /**
* Construct a URL to the route with the given name. * Construct a URL to the route with the given name.
*
* @param {String} name
* @param {Object} params
* @return {String}
* @public
*/ */
route(name: string, params?: Record<string, unknown>): string; public route(name: string, params?: Object): string;
} }
import Forum from "./models/Forum";
import ItemList from "./utils/ItemList";
import Session from "./Session";
import Translator from "./Translator";
import Store from "./Store";
import PageState from "./states/PageState";
import ModalManagerState from "./states/ModalManagerState";
import AlertManagerState from "./states/AlertManagerState";
import Drawer from "./utils/Drawer";

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
export interface ComponentAttrs extends Mithril.Attributes { export interface ComponentAttrs extends Mithril.Attributes {
} }
/** /**
@@ -28,7 +28,7 @@ export interface ComponentAttrs extends Mithril.Attributes {
* *
* @see https://mithril.js.org/components.html * @see https://mithril.js.org/components.html
*/ */
export default abstract class Component<Attrs extends ComponentAttrs = ComponentAttrs, State = undefined> implements Mithril.ClassComponent<Attrs> { export default abstract class Component<T extends ComponentAttrs = ComponentAttrs> implements Mithril.ClassComponent<T> {
/** /**
* The root DOM element for the component. * The root DOM element for the component.
*/ */
@@ -38,47 +38,35 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
* *
* @see https://mithril.js.org/components.html#passing-data-to-components * @see https://mithril.js.org/components.html#passing-data-to-components
*/ */
protected attrs: Attrs; protected attrs: T;
/**
* Class component state that is persisted between redraws.
*
* Updating this will **not** automatically trigger a redraw, unlike
* other frameworks.
*
* This is different to Vnode state, which is always an instance of your
* class component.
*
* This is `undefined` by default.
*/
protected state: State;
/** /**
* @inheritdoc * @inheritdoc
*/ */
abstract view(vnode: Mithril.Vnode<Attrs, this>): Mithril.Children; abstract view(vnode: Mithril.Vnode<T, this>): Mithril.Children;
/** /**
* @inheritdoc * @inheritdoc
*/ */
oninit(vnode: Mithril.Vnode<Attrs, this>): void; oninit(vnode: Mithril.Vnode<T, this>): void;
/** /**
* @inheritdoc * @inheritdoc
*/ */
oncreate(vnode: Mithril.VnodeDOM<Attrs, this>): void; oncreate(vnode: Mithril.VnodeDOM<T, this>): void;
/** /**
* @inheritdoc * @inheritdoc
*/ */
onbeforeupdate(vnode: Mithril.VnodeDOM<Attrs, this>): void; onbeforeupdate(vnode: Mithril.VnodeDOM<T, this>): void;
/** /**
* @inheritdoc * @inheritdoc
*/ */
onupdate(vnode: Mithril.VnodeDOM<Attrs, this>): void; onupdate(vnode: Mithril.VnodeDOM<T, this>): void;
/** /**
* @inheritdoc * @inheritdoc
*/ */
onbeforeremove(vnode: Mithril.VnodeDOM<Attrs, this>): void; onbeforeremove(vnode: Mithril.VnodeDOM<T, this>): void;
/** /**
* @inheritdoc * @inheritdoc
*/ */
onremove(vnode: Mithril.VnodeDOM<Attrs, this>): void; onremove(vnode: Mithril.VnodeDOM<T, this>): void;
/** /**
* Returns a jQuery object for this component's element. If you pass in a * Returns a jQuery object for this component's element. If you pass in a
* selector string, this method will return a jQuery object, using the current * selector string, this method will return a jQuery object, using the current
@@ -99,7 +87,7 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
* *
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children * @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
*/ */
static component<SAttrs extends ComponentAttrs = ComponentAttrs>(attrs?: SAttrs, children?: Mithril.Children): Mithril.Vnode; static component(attrs?: {}, children?: null): Mithril.Vnode;
/** /**
* Saves a reference to the vnode attrs after running them through initAttrs, * Saves a reference to the vnode attrs after running them through initAttrs,
* and checking for common issues. * and checking for common issues.

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
/** /**
* The `Fragment` class represents a chunk of DOM that is rendered once with Mithril and then takes * The `Fragment` class represents a chunk of DOM that is rendered once with Mithril and then takes
* over control of its own DOM and lifecycle. * over control of its own DOM and lifecycle.
@@ -28,11 +28,11 @@ export default abstract class Fragment {
* containing all of the `li` elements inside the DOM element of this * containing all of the `li` elements inside the DOM element of this
* fragment. * fragment.
* *
* @param [selector] a jQuery-compatible selector string * @param {String} [selector] a jQuery-compatible selector string
* @returns the jQuery object for the DOM node * @returns {jQuery} the jQuery object for the DOM node
* @final * @final
*/ */
$(selector?: string): JQuery; $(selector: any): JQuery<any>;
/** /**
* Get the renderable virtual DOM that represents the fragment's view. * Get the renderable virtual DOM that represents the fragment's view.
* *

View File

@@ -1,147 +1,149 @@
import { FlarumRequestOptions } from './Application';
import Store, { ApiPayloadSingle, ApiResponseSingle, MetaInformation } from './Store';
export interface ModelIdentifier {
type: string;
id: string;
}
export interface ModelAttributes {
[key: string]: unknown;
}
export interface ModelRelationships {
[relationship: string]: {
data: ModelIdentifier | ModelIdentifier[];
};
}
export interface UnsavedModelData {
type?: string;
attributes?: ModelAttributes;
relationships?: ModelRelationships;
}
export interface SavedModelData {
type: string;
id: string;
attributes?: ModelAttributes;
relationships?: ModelRelationships;
}
export declare type ModelData = UnsavedModelData | SavedModelData;
export interface SaveRelationships {
[relationship: string]: Model | Model[];
}
export interface SaveAttributes {
[key: string]: unknown;
relationships?: SaveRelationships;
}
/** /**
* The `Model` class represents a local data resource. It provides methods to * The `Model` class represents a local data resource. It provides methods to
* persist changes via the API. * persist changes via the API.
*
* @abstract
*/ */
export default abstract class Model { export default class Model {
/**
* The resource object from the API.
*/
data: ModelData;
/**
* The time at which the model's data was last updated. Watching the value
* of this property is a fast way to retain/cache a subtree if data hasn't
* changed.
*/
freshness: Date;
/**
* Whether or not the resource exists on the server.
*/
exists: boolean;
/**
* The data store that this resource should be persisted to.
*/
protected store: Store;
/**
* @param data A resource object from the API.
* @param store The data store that this model should be persisted to.
*/
constructor(data?: ModelData, store?: Store);
/**
* Get the model's ID.
*
* @final
*/
id(): string | undefined;
/**
* Get one of the model's attributes.
*
* @final
*/
attribute<T = unknown>(attribute: string): T;
/**
* Merge new data into this model locally.
*
* @param data A resource object to merge into this model
*/
pushData(data: ModelData | {
relationships?: SaveRelationships;
}): this;
/**
* Merge new attributes into this model locally.
*
* @param attributes The attributes to merge.
*/
pushAttributes(attributes: ModelAttributes): void;
/**
* Merge new attributes into this model, both locally and with persistence.
*
* @param attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved.
*/
save(attributes: SaveAttributes, options?: Omit<FlarumRequestOptions<ApiPayloadSingle>, 'url'> & {
meta?: MetaInformation;
}): Promise<ApiResponseSingle<this>>;
/**
* Send a request to delete the resource.
*
* @param body Data to send along with the DELETE request.
*/
delete(body?: FlarumRequestOptions<void>['body'], options?: Omit<FlarumRequestOptions<void>, 'url'>): Promise<void>;
/**
* Construct a path to the API endpoint for this resource.
*/
protected apiEndpoint(): string;
protected copyData(): ModelData;
protected rawRelationship<M extends Model>(relationship: string): undefined | ModelIdentifier;
protected rawRelationship<M extends Model[]>(relationship: string): undefined | ModelIdentifier[];
/** /**
* Generate a function which returns the value of the given attribute. * Generate a function which returns the value of the given attribute.
* *
* @param transform A function to transform the attribute value * @param {String} name
* @param {function} [transform] A function to transform the attribute value
* @return {*}
* @public
*/ */
static attribute<T>(name: string): () => T; public static attribute(name: string, transform?: Function | undefined): any;
static attribute<T, O = unknown>(name: string, transform: (attr: O) => T): () => T;
/** /**
* Generate a function which returns the value of the given has-one * Generate a function which returns the value of the given has-one
* relationship. * relationship.
* *
* @return false if no information about the * @param {String} name
* @return {Model|Boolean|undefined} false if no information about the
* relationship exists; undefined if the relationship exists but the model * relationship exists; undefined if the relationship exists but the model
* has not been loaded; or the model if it has been loaded. * has not been loaded; or the model if it has been loaded.
* @public
*/ */
static hasOne<M extends Model>(name: string): () => M | false; public static hasOne(name: string): Model | boolean | undefined;
static hasOne<M extends Model | null>(name: string): () => M | null | false;
/** /**
* Generate a function which returns the value of the given has-many * Generate a function which returns the value of the given has-many
* relationship. * relationship.
* *
* @return false if no information about the relationship * @param {String} name
* @return {Array|Boolean} false if no information about the relationship
* exists; an array if it does, containing models if they have been * exists; an array if it does, containing models if they have been
* loaded, and undefined for those that have not. * loaded, and undefined for those that have not.
* @public
*/ */
static hasMany<M extends Model>(name: string): () => (M | undefined)[] | false; public static hasMany(name: string): any[] | boolean;
/** /**
* Transform the given value into a Date object. * Transform the given value into a Date object.
*
* @param {String} value
* @return {Date|null}
* @public
*/ */
static transformDate(value: string): Date; public static transformDate(value: string): Date | null;
static transformDate(value: string | null): Date | null;
static transformDate(value: string | undefined): Date | undefined;
static transformDate(value: string | null | undefined): Date | null | undefined;
/** /**
* Get a resource identifier object for the given model. * Get a resource identifier object for the given model.
*
* @param {Model} model
* @return {Object}
* @protected
*/ */
protected static getIdentifier(model: Model): ModelIdentifier; protected static getIdentifier(model: Model): Object;
/**
* @param {Object} data A resource object from the API.
* @param {Store} store The data store that this model should be persisted to.
* @public
*/
constructor(data?: Object, store?: any);
/**
* The resource object from the API.
*
* @type {Object}
* @public
*/
public data: Object;
/**
* The time at which the model's data was last updated. Watching the value
* of this property is a fast way to retain/cache a subtree if data hasn't
* changed.
*
* @type {Date}
* @public
*/
public freshness: Date;
/**
* Whether or not the resource exists on the server.
*
* @type {Boolean}
* @public
*/
public exists: boolean;
/**
* The data store that this resource should be persisted to.
*
* @type {Store}
* @protected
*/
protected store: any;
/**
* Get the model's ID.
*
* @return {Integer}
* @public
* @final
*/
public id(): any;
/**
* Get one of the model's attributes.
*
* @param {String} attribute
* @return {*}
* @public
* @final
*/
public attribute(attribute: string): any;
/**
* Merge new data into this model locally.
*
* @param {Object} data A resource object to merge into this model
* @public
*/
public pushData(data: Object): void;
/**
* Merge new attributes into this model locally.
*
* @param {Object} attributes The attributes to merge.
* @public
*/
public pushAttributes(attributes: Object): void;
/**
* Merge new attributes into this model, both locally and with persistence.
*
* @param {Object} attributes The attributes to save. If a 'relationships' key
* exists, it will be extracted and relationships will also be saved.
* @param {Object} [options]
* @return {Promise}
* @public
*/
public save(attributes: Object, options?: Object | undefined): Promise<any>;
/**
* Send a request to delete the resource.
*
* @param {Object} body Data to send along with the DELETE request.
* @param {Object} [options]
* @return {Promise}
* @public
*/
public delete(body: Object, options?: Object | undefined): Promise<any>;
/**
* Construct a path to the API endpoint for this resource.
*
* @return {String}
* @protected
*/
protected apiEndpoint(): string;
copyData(): any;
} }

View File

@@ -1,33 +1,37 @@
import User from './models/User';
import { FlarumRequestOptions } from './Application';
export declare type LoginParams = {
/**
* The username/email
*/
identification: string;
password: string;
remember: boolean;
};
/** /**
* The `Session` class defines the current user session. It stores a reference * The `Session` class defines the current user session. It stores a reference
* to the current authenticated user, and provides methods to log in/out. * to the current authenticated user, and provides methods to log in/out.
*/ */
export default class Session { export default class Session {
constructor(user: any, csrfToken: any);
/** /**
* The current authenticated user. * The current authenticated user.
*
* @type {User|null}
* @public
*/ */
user: User | null; public user: any | null;
/** /**
* The CSRF token. * The CSRF token.
*
* @type {String|null}
* @public
*/ */
csrfToken: string; public csrfToken: string | null;
constructor(user: User | null, csrfToken: string);
/** /**
* Attempt to log in a user. * Attempt to log in a user.
*
* @param {String} identification The username/email.
* @param {String} password
* @param {Object} [options]
* @return {Promise}
* @public
*/ */
login(body: LoginParams, options?: Omit<FlarumRequestOptions<any>, 'url' | 'body' | 'method'>): Promise<any>; public login(body: any, options?: Object | undefined): Promise<any>;
/** /**
* Log the user out. * Log the user out.
*
* @public
*/ */
logout(): void; public logout(): void;
} }

View File

@@ -1,127 +1,97 @@
import { FlarumRequestOptions } from './Application';
import Model, { ModelData, SavedModelData } from './Model';
export interface MetaInformation {
[key: string]: any;
}
export interface ApiQueryParamsSingle {
fields?: string[];
include?: string;
bySlug?: boolean;
meta?: MetaInformation;
}
export interface ApiQueryParamsPlural {
fields?: string[];
include?: string;
filter?: {
q: string;
[key: string]: string;
};
page?: {
offset?: number;
number?: number;
limit?: number;
size?: number;
};
sort?: string;
meta?: MetaInformation;
}
export declare type ApiQueryParams = ApiQueryParamsPlural | ApiQueryParamsSingle;
export interface ApiPayloadSingle {
data: SavedModelData;
included?: SavedModelData[];
meta?: MetaInformation;
}
export interface ApiPayloadPlural {
data: SavedModelData[];
included?: SavedModelData[];
links?: {
first: string;
next?: string;
prev?: string;
};
meta?: MetaInformation;
}
export declare type ApiPayload = ApiPayloadSingle | ApiPayloadPlural;
export declare type ApiResponseSingle<M extends Model> = M & {
payload: ApiPayloadSingle;
};
export declare type ApiResponsePlural<M extends Model> = M[] & {
payload: ApiPayloadPlural;
};
export declare type ApiResponse<M extends Model> = ApiResponseSingle<M> | ApiResponsePlural<M>;
interface ApiQueryRequestOptions<ResponseType> extends Omit<FlarumRequestOptions<ResponseType>, 'url'> {
}
interface StoreData {
[type: string]: Partial<Record<string, Model>>;
}
export declare function payloadIsPlural(payload: ApiPayload): payload is ApiPayloadPlural;
/** /**
* The `Store` class defines a local data store, and provides methods to * The `Store` class defines a local data store, and provides methods to
* retrieve data from the API. * retrieve data from the API.
*/ */
export default class Store { export default class Store {
constructor(models: any);
/** /**
* The local data store. A tree of resource types to IDs, such that * The local data store. A tree of resource types to IDs, such that
* accessing data[type][id] will return the model for that type/ID. * accessing data[type][id] will return the model for that type/ID.
*
* @type {Object}
* @protected
*/ */
protected data: StoreData; protected data: Object;
/** /**
* The model registry. A map of resource types to the model class that * The model registry. A map of resource types to the model class that
* should be used to represent resources of that type. * should be used to represent resources of that type.
*
* @type {Object}
* @public
*/ */
models: Record<string, typeof Model>; public models: Object;
constructor(models: Record<string, typeof Model>);
/** /**
* Push resources contained within an API payload into the store. * Push resources contained within an API payload into the store.
* *
* @return The model(s) representing the resource(s) contained * @param {Object} payload
* @return {Model|Model[]} The model(s) representing the resource(s) contained
* within the 'data' key of the payload. * within the 'data' key of the payload.
* @public
*/ */
pushPayload<M extends Model>(payload: ApiPayloadSingle): ApiResponseSingle<M>; public pushPayload(payload: Object): any | any[];
pushPayload<Ms extends Model[]>(payload: ApiPayloadPlural): ApiResponsePlural<Ms[number]>;
/** /**
* Create a model to represent a resource object (or update an existing one), * Create a model to represent a resource object (or update an existing one),
* and push it into the store. * and push it into the store.
* *
* @param data The resource object * @param {Object} data The resource object
* @return The model, or null if no model class has been * @return {Model|null} The model, or null if no model class has been
* registered for this resource type. * registered for this resource type.
* @public
*/ */
pushObject<M extends Model>(data: SavedModelData): M | null; public pushObject(data: Object): any | null;
pushObject<M extends Model>(data: SavedModelData, allowUnregistered: false): M;
/** /**
* Make a request to the API to find record(s) of a specific type. * Make a request to the API to find record(s) of a specific type.
*
* @param {String} type The resource type.
* @param {Integer|Integer[]|Object} [id] The ID(s) of the model(s) to retrieve.
* Alternatively, if an object is passed, it will be handled as the
* `query` parameter.
* @param {Object} [query]
* @param {Object} [options]
* @return {Promise}
* @public
*/ */
find<M extends Model>(type: string, params: ApiQueryParamsSingle): Promise<ApiResponseSingle<M>>; public find(type: string, id?: any | any[] | Object, query?: Object | undefined, options?: Object | undefined): Promise<any>;
find<Ms extends Model[]>(type: string, params: ApiQueryParamsPlural): Promise<ApiResponsePlural<Ms[number]>>;
find<M extends Model>(type: string, id: string, params?: ApiQueryParamsSingle, options?: ApiQueryRequestOptions<ApiPayloadSingle>): Promise<ApiResponseSingle<M>>;
find<Ms extends Model[]>(type: string, ids: string[], params?: ApiQueryParamsPlural, options?: ApiQueryRequestOptions<ApiPayloadPlural>): Promise<ApiResponsePlural<Ms[number]>>;
/** /**
* Get a record from the store by ID. * Get a record from the store by ID.
*
* @param {String} type The resource type.
* @param {Integer} id The resource ID.
* @return {Model}
* @public
*/ */
getById<M extends Model>(type: string, id: string): M | undefined; public getById(type: string, id: any): any;
/** /**
* Get a record from the store by the value of a model attribute. * Get a record from the store by the value of a model attribute.
* *
* @param type The resource type. * @param {String} type The resource type.
* @param key The name of the method on the model. * @param {String} key The name of the method on the model.
* @param value The value of the model attribute. * @param {*} value The value of the model attribute.
* @return {Model}
* @public
*/ */
getBy<M extends Model, T = unknown>(type: string, key: keyof M, value: T): M | undefined; public getBy(type: string, key: string, value: any): any;
/** /**
* Get all loaded records of a specific type. * Get all loaded records of a specific type.
*
* @param {String} type
* @return {Model[]}
* @public
*/ */
all<M extends Model>(type: string): M[]; public all(type: string): any[];
/** /**
* Remove the given model from the store. * Remove the given model from the store.
*
* @param {Model} model
*/ */
remove(model: Model): void; remove(model: any): void;
/** /**
* Create a new record of the given type. * Create a new record of the given type.
* *
* @param type The resource type * @param {String} type The resource type
* @param data Any data to initialize the model with * @param {Object} [data] Any data to initialize the model with
* @return {Model}
* @public
*/ */
createRecord<M extends Model>(type: string, data?: ModelData): M; public createRecord(type: string, data?: Object | undefined): any;
} }
export {};

View File

@@ -1,6 +1,3 @@
/// <reference path="../../src/common/translator-icu-rich.d.ts" />
import { RichMessageFormatter } from '@askvortsov/rich-icu-message-formatter';
import { pluralTypeHandler, selectTypeHandler } from '@ultraq/icu-message-formatter';
declare type Translations = Record<string, string>; declare type Translations = Record<string, string>;
declare type TranslatorParameters = Record<string, unknown>; declare type TranslatorParameters = Record<string, unknown>;
export default class Translator { export default class Translator {
@@ -11,15 +8,15 @@ export default class Translator {
/** /**
* The underlying ICU MessageFormatter util. * The underlying ICU MessageFormatter util.
*/ */
protected formatter: RichMessageFormatter; protected formatter: any;
setLocale(locale: string): void; setLocale(locale: string): void;
addTranslations(translations: Translations): void; addTranslations(translations: Translations): void;
/** /**
* An extensible entrypoint for extenders to register type handlers for translations. * An extensible entrypoint for extenders to register type handlers for translations.
*/ */
protected formatterTypeHandlers(): { protected formatterTypeHandlers(): {
plural: typeof pluralTypeHandler; plural: any;
select: typeof selectTypeHandler; select: any;
}; };
/** /**
* A temporary system to preprocess parameters. * A temporary system to preprocess parameters.
@@ -29,6 +26,6 @@ export default class Translator {
* @internal * @internal
*/ */
protected preprocessParameters(parameters: TranslatorParameters): TranslatorParameters; protected preprocessParameters(parameters: TranslatorParameters): TranslatorParameters;
trans(id: string, parameters?: TranslatorParameters): import("@askvortsov/rich-icu-message-formatter").NestedStringArray; trans(id: string, parameters?: TranslatorParameters): any;
} }
export {}; export {};

View File

@@ -1,6 +0,0 @@
import type Application from './Application';
declare const _default: Application;
/**
* The instance of Application within the common namespace.
*/
export default _default;

View File

@@ -1,11 +1,11 @@
declare var _default: { declare var _default: {
extend: any; extend: typeof extend;
Session: typeof Session; Session: typeof Session;
Store: typeof Store; Store: typeof Store;
'utils/BasicEditorDriver': typeof BasicEditorDriver; 'utils/BasicEditorDriver': typeof BasicEditorDriver;
'utils/evented': { 'utils/evented': {
handlers: Record<string, unknown>; handlers: Object;
getHandlers(event: string): Function[]; getHandlers(event: string): any[];
trigger(event: string, ...args: any[]): void; trigger(event: string, ...args: any[]): void;
on(event: string, handler: Function): void; on(event: string, handler: Function): void;
one(event: string, handler: Function): void; one(event: string, handler: Function): void;
@@ -32,15 +32,17 @@ declare var _default: {
'utils/subclassOf': typeof subclassOf; 'utils/subclassOf': typeof subclassOf;
'utils/setRouteWithForcedRefresh': typeof setRouteWithForcedRefresh; 'utils/setRouteWithForcedRefresh': typeof setRouteWithForcedRefresh;
'utils/patchMithril': typeof patchMithril; 'utils/patchMithril': typeof patchMithril;
'utils/proxifyCompat': typeof proxifyCompat; 'utils/proxifyCompat': (compat: {
[key: string]: any;
}, namespace: string) => {
[key: string]: any;
};
'utils/classList': (...classes: import("clsx").ClassValue[]) => string; 'utils/classList': (...classes: import("clsx").ClassValue[]) => string;
'utils/extractText': typeof extractText; 'utils/extractText': typeof extractText;
'utils/formatNumber': typeof formatNumber; 'utils/formatNumber': typeof formatNumber;
'utils/mapRoutes': typeof mapRoutes; 'utils/mapRoutes': typeof mapRoutes;
'utils/withAttr': (key: string, cb: Function) => (this: Element) => void; 'utils/withAttr': (key: string, cb: Function) => (this: Element) => void;
'utils/throttleDebounce': typeof ThrottleDebounce; 'utils/throttleDebounce': typeof ThrottleDebounce;
'utils/isObject': typeof isObject;
'utils/focusTrap': typeof FocusTrap;
'models/Notification': typeof Notification; 'models/Notification': typeof Notification;
'models/User': typeof User; 'models/User': typeof User;
'models/Post': typeof Post; 'models/Post': typeof Post;
@@ -67,7 +69,6 @@ declare var _default: {
'components/Link': typeof Link; 'components/Link': typeof Link;
'components/LinkButton': typeof LinkButton; 'components/LinkButton': typeof LinkButton;
'components/Checkbox': typeof Checkbox; 'components/Checkbox': typeof Checkbox;
'components/ColorPreviewInput': typeof ColorPreviewInput;
'components/SelectDropdown': typeof SelectDropdown; 'components/SelectDropdown': typeof SelectDropdown;
'components/ModalManager': typeof ModalManager; 'components/ModalManager': typeof ModalManager;
'components/Button': typeof Button; 'components/Button': typeof Button;
@@ -92,6 +93,7 @@ declare var _default: {
'states/PaginatedListState': typeof PaginatedListState; 'states/PaginatedListState': typeof PaginatedListState;
}; };
export default _default; export default _default;
import * as extend from "./extend";
import Session from "./Session"; import Session from "./Session";
import Store from "./Store"; import Store from "./Store";
import BasicEditorDriver from "./utils/BasicEditorDriver"; import BasicEditorDriver from "./utils/BasicEditorDriver";
@@ -116,13 +118,10 @@ import Stream from "./utils/Stream";
import subclassOf from "./utils/subclassOf"; import subclassOf from "./utils/subclassOf";
import setRouteWithForcedRefresh from "./utils/setRouteWithForcedRefresh"; import setRouteWithForcedRefresh from "./utils/setRouteWithForcedRefresh";
import patchMithril from "./utils/patchMithril"; import patchMithril from "./utils/patchMithril";
import proxifyCompat from "./utils/proxifyCompat";
import extractText from "./utils/extractText"; import extractText from "./utils/extractText";
import formatNumber from "./utils/formatNumber"; import formatNumber from "./utils/formatNumber";
import mapRoutes from "./utils/mapRoutes"; import mapRoutes from "./utils/mapRoutes";
import * as ThrottleDebounce from "./utils/throttleDebounce"; import * as ThrottleDebounce from "./utils/throttleDebounce";
import isObject from "./utils/isObject";
import * as FocusTrap from "./utils/focusTrap";
import Notification from "./models/Notification"; import Notification from "./models/Notification";
import User from "./models/User"; import User from "./models/User";
import Post from "./models/Post"; import Post from "./models/Post";
@@ -149,7 +148,6 @@ import Alert from "./components/Alert";
import Link from "./components/Link"; import Link from "./components/Link";
import LinkButton from "./components/LinkButton"; import LinkButton from "./components/LinkButton";
import Checkbox from "./components/Checkbox"; import Checkbox from "./components/Checkbox";
import ColorPreviewInput from "./components/ColorPreviewInput";
import SelectDropdown from "./components/SelectDropdown"; import SelectDropdown from "./components/SelectDropdown";
import ModalManager from "./components/ModalManager"; import ModalManager from "./components/ModalManager";
import Button from "./components/Button"; import Button from "./components/Button";

View File

@@ -1,5 +1,5 @@
import Component, { ComponentAttrs } from '../Component'; import Component, { ComponentAttrs } from '../Component';
import type Mithril from 'mithril'; import Mithril from 'mithril';
export interface AlertAttrs extends ComponentAttrs { export interface AlertAttrs extends ComponentAttrs {
/** The type of alert this is. Will be used to give the alert a class name of `Alert--{type}`. */ /** The type of alert this is. Will be used to give the alert a class name of `Alert--{type}`. */
type?: string; type?: string;
@@ -15,5 +15,5 @@ export interface AlertAttrs extends ComponentAttrs {
* some controls, and may be dismissible. * some controls, and may be dismissible.
*/ */
export default class Alert<T extends AlertAttrs = AlertAttrs> extends Component<T> { export default class Alert<T extends AlertAttrs = AlertAttrs> extends Component<T> {
view(vnode: Mithril.VnodeDOM<T, this>): JSX.Element; view(vnode: Mithril.Vnode): JSX.Element;
} }

View File

@@ -2,7 +2,8 @@
* The `AlertManager` component provides an area in which `Alert` components can * The `AlertManager` component provides an area in which `Alert` components can
* be shown and dismissed. * be shown and dismissed.
*/ */
export default class AlertManager extends Component<import("../Component").ComponentAttrs, undefined> { export default class AlertManager extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
state: any;
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -11,7 +11,7 @@
* *
* All other attrs will be assigned as attributes on the badge element. * All other attrs will be assigned as attributes on the badge element.
*/ */
export default class Badge extends Component<import("../Component").ComponentAttrs, undefined> { export default class Badge extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -1,69 +1,29 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from '../Component';
export interface IButtonAttrs extends ComponentAttrs {
/**
* Class(es) of an optional icon to be rendered within the button.
*
* If provided, the button will gain a `has-icon` class.
*/
icon?: string;
/**
* Disables button from user input.
*
* Default: `false`
*/
disabled?: boolean;
/**
* Show a loading spinner within the button.
*
* If `true`, also disables the button.
*
* Default: `false`
*/
loading?: boolean;
/**
* **DEPRECATED:** Please use the `aria-label` attribute instead. For tooltips, use
* the `<Tooltip>` component.
*
* Accessible text for the button. This should always be present if the button only
* contains an icon.
*
* The textual content of this attribute is passed to the DOM element as `aria-label`.
*
* @deprecated
*/
title?: string | Mithril.ChildArray;
/**
* Accessible text for the button. This should always be present if the button only
* contains an icon.
*
* The textual content of this attribute is passed to the DOM element as `aria-label`.
*/
'aria-label'?: string | Mithril.ChildArray;
/**
* Button type.
*
* Default: `"button"`
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type
*/
type?: string;
}
/** /**
* The `Button` component defines an element which, when clicked, performs an * The `Button` component defines an element which, when clicked, performs an
* action. * action.
* *
* Other attrs will be assigned as attributes on the `<button>` element. * ### Attrs
*
* - `icon` The name of the icon class. If specified, the button will be given a
* 'has-icon' class name.
* - `disabled` Whether or not the button is disabled. If truthy, the button
* will be given a 'disabled' class name, and any `onclick` handler will be
* removed.
* - `loading` Whether or not the button should be in a disabled loading state.
*
* All other attrs will be assigned as attributes on the button element.
* *
* Note that a Button has no default class names. This is because a Button can * Note that a Button has no default class names. This is because a Button can
* be used to represent any generic clickable control, like a menu item. Common * be used to represent any generic clickable control, like a menu item.
* styles can be applied by providing `className="Button"` to the Button component.
*/ */
export default class Button<CustomAttrs extends IButtonAttrs = IButtonAttrs> extends Component<CustomAttrs> { export default class Button extends Component<import("../Component").ComponentAttrs> {
view(vnode: Mithril.VnodeDOM<CustomAttrs, this>): JSX.Element; constructor();
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
/** /**
* Get the template for the button's content. * Get the template for the button's content.
*
* @return {*}
* @protected
*/ */
protected getButtonContent(children: Mithril.Children): Mithril.ChildArray; protected getButtonContent(children: any): any;
} }
import Component from "../Component";

View File

@@ -10,19 +10,19 @@
* - `onchange` A callback to run when the checkbox is checked/unchecked. * - `onchange` A callback to run when the checkbox is checked/unchecked.
* - `children` A text label to display next to the checkbox. * - `children` A text label to display next to the checkbox.
*/ */
export default class Checkbox extends Component<import("../Component").ComponentAttrs, undefined> { export default class Checkbox extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
/** /**
* Get the template for the checkbox's display (tick/cross icon). * Get the template for the checkbox's display (tick/cross icon).
* *
* @return {import('mithril').Children} * @return {*}
* @protected * @protected
*/ */
protected getDisplay(): import('mithril').Children; protected getDisplay(): any;
/** /**
* Run a callback when the state of the checkbox is changed. * Run a callback when the state of the checkbox is changed.
* *
* @param {boolean} checked * @param {Boolean} checked
* @protected * @protected
*/ */
protected onchange(checked: boolean): void; protected onchange(checked: boolean): void;

View File

@@ -1,6 +0,0 @@
import type Mithril from 'mithril';
import Component, { ComponentAttrs } from '../Component';
export default class ColorPreviewInput extends Component {
value?: string;
view(vnode: Mithril.Vnode<ComponentAttrs, this>): JSX.Element;
}

View File

@@ -14,7 +14,7 @@
* another component / DOM element.) * another component / DOM element.)
* *
*/ */
export default class ConfirmDocumentUnload extends Component<import("../Component").ComponentAttrs, undefined> { export default class ConfirmDocumentUnload extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
handler(): any; handler(): any;
boundHandler: (() => any) | undefined; boundHandler: (() => any) | undefined;

View File

@@ -15,24 +15,24 @@
* *
* The children will be displayed as a list inside of the dropdown menu. * The children will be displayed as a list inside of the dropdown menu.
*/ */
export default class Dropdown extends Component<import("../Component").ComponentAttrs, undefined> { export default class Dropdown extends Component<import("../Component").ComponentAttrs> {
static initAttrs(attrs: any): void; static initAttrs(attrs: any): void;
constructor(); constructor();
showing: boolean | undefined; showing: boolean | undefined;
/** /**
* Get the template for the button. * Get the template for the button.
* *
* @return {import('mithril').Children} * @return {*}
* @protected * @protected
*/ */
protected getButton(children: any): import('mithril').Children; protected getButton(children: any): any;
/** /**
* Get the template for the button's content. * Get the template for the button's content.
* *
* @return {import('mithril').Children} * @return {*}
* @protected * @protected
*/ */
protected getButtonContent(children: any): import('mithril').Children; protected getButtonContent(children: any): any;
getMenu(items: any): JSX.Element; getMenu(items: any): JSX.Element;
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -1,31 +1,25 @@
/// <reference path="../../../src/common/translator-icu-rich.d.ts" /> /**
import Modal, { IInternalModalAttrs } from './Modal'; * The `EditUserModal` component displays a modal dialog with a login form.
import ItemList from '../utils/ItemList'; */
import Stream from '../utils/Stream'; export default class EditUserModal extends Modal {
import type Mithril from 'mithril'; username: Stream<any> | undefined;
import type User from '../models/User'; email: Stream<any> | undefined;
import type { SaveAttributes } from '../Model'; isEmailConfirmed: Stream<any> | undefined;
export interface IEditUserModalAttrs extends IInternalModalAttrs { setPassword: Stream<boolean> | undefined;
user: User; password: Stream<any> | undefined;
} groups: {} | undefined;
export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEditUserModalAttrs> extends Modal<CustomAttrs> { fields(): ItemList;
protected username: Stream<string>;
protected email: Stream<string>;
protected isEmailConfirmed: Stream<boolean>;
protected setPassword: Stream<boolean>;
protected password: Stream<string>;
protected groups: Record<string, Stream<boolean>>;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
className(): string;
title(): import("@askvortsov/rich-icu-message-formatter").NestedStringArray;
content(): JSX.Element;
fields(): ItemList<unknown>;
activate(): void; activate(): void;
data(): SaveAttributes; data(): {
onsubmit(e: SubmitEvent): void; relationships: {};
nonAdminEditingAdmin(): boolean | null; };
nonAdminEditingAdmin(): any;
/** /**
* @internal * @internal
* @protected
*/ */
protected userIsAdmin(user: User | null): boolean | null; protected userIsAdmin(user: any): any;
} }
import Modal from "./Modal";
import Stream from "../utils/Stream";
import ItemList from "../utils/ItemList";

View File

@@ -7,7 +7,7 @@
* *
* The children should be an array of items to show in the fieldset. * The children should be an array of items to show in the fieldset.
*/ */
export default class FieldSet extends Component<import("../Component").ComponentAttrs, undefined> { export default class FieldSet extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -6,7 +6,7 @@
* Links will default to internal; the 'external' attr must be set to * Links will default to internal; the 'external' attr must be set to
* `true` for the link to be external. * `true` for the link to be external.
*/ */
export default class Link extends Component<import("../Component").ComponentAttrs, undefined> { export default class Link extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -11,15 +11,14 @@
* the `active` prop will automatically be set to true. * the `active` prop will automatically be set to true.
* - `force` Whether the page should be fully rerendered. Defaults to `true`. * - `force` Whether the page should be fully rerendered. Defaults to `true`.
*/ */
export default class LinkButton extends Button<import("./Button").IButtonAttrs> { export default class LinkButton extends Button {
static initAttrs(attrs: any): void; static initAttrs(attrs: any): void;
/** /**
* Determine whether a component with the given attrs is 'active'. * Determine whether a component with the given attrs is 'active'.
* *
* @param {object} attrs * @param {Object} attrs
* @return {boolean} * @return {Boolean}
*/ */
static isActive(attrs: object): boolean; static isActive(attrs: Object): boolean;
constructor();
} }
import Button from "./Button"; import Button from "./Button";

View File

@@ -1,68 +1,67 @@
import Component from '../Component';
import { AlertAttrs } from './Alert';
import type Mithril from 'mithril';
import type ModalManagerState from '../states/ModalManagerState';
import type RequestError from '../utils/RequestError';
import type ModalManager from './ModalManager';
export interface IInternalModalAttrs {
state: ModalManagerState;
animateShow: ModalManager['animateShow'];
animateHide: ModalManager['animateHide'];
}
/** /**
* The `Modal` component displays a modal dialog, wrapped in a form. Subclasses * The `Modal` component displays a modal dialog, wrapped in a form. Subclasses
* should implement the `className`, `title`, and `content` methods. * should implement the `className`, `title`, and `content` methods.
*
* @abstract
*/ */
export default abstract class Modal<ModalAttrs extends IInternalModalAttrs = IInternalModalAttrs> extends Component<ModalAttrs> { export default class Modal extends Component<import("../Component").ComponentAttrs> {
/** /**
* Determine whether or not the modal should be dismissible via an 'x' button. * Determine whether or not the modal should be dismissible via an 'x' button.
*/ */
static readonly isDismissible: boolean; static isDismissible: boolean;
protected loading: boolean; constructor();
/** /**
* Attributes for an alert component to show below the header. * Attributes for an alert component to show below the header.
*
* @type {object}
*/ */
alertAttrs: AlertAttrs | null; alertAttrs: object;
oninit(vnode: Mithril.Vnode<ModalAttrs, this>): void;
oncreate(vnode: Mithril.VnodeDOM<ModalAttrs, this>): void;
onbeforeremove(vnode: Mithril.VnodeDOM<ModalAttrs, this>): Promise<void> | void;
/**
* @todo split into FormModal and Modal in 2.0
*/
view(): JSX.Element;
/** /**
* Get the class name to apply to the modal. * Get the class name to apply to the modal.
*
* @return {String}
* @abstract
*/ */
abstract className(): string; className(): string;
/** /**
* Get the title of the modal dialog. * Get the title of the modal dialog.
*
* @return {String}
* @abstract
*/ */
abstract title(): Mithril.Children; title(): string;
/** /**
* Get the content of the modal. * Get the content of the modal.
*
* @return {VirtualElement}
* @abstract
*/ */
abstract content(): Mithril.Children; content(): any;
/** /**
* Handle the modal form's submit event. * Handle the modal form's submit event.
*/
onsubmit(e: SubmitEvent): void;
/**
* Callback executed when the modal is shown and ready to be interacted with.
* *
* @remark Focuses the first input in the modal. * @param {Event} e
*/
onsubmit(): void;
/**
* Focus on the first input when the modal is ready to be used.
*/ */
onready(): void; onready(): void;
/** /**
* Hides the modal. * Hide the modal.
*/ */
hide(): void; hide(): void;
/** /**
* Sets `loading` to false and triggers a redraw. * Stop loading.
*/ */
loaded(): void; loaded(): void;
loading: boolean | undefined;
/** /**
* Shows an alert describing an error returned from the API, and gives focus to * Show an alert describing an error returned from the API, and give focus to
* the first relevant field involved in the error. * the first relevant field.
*
* @param {RequestError} error
*/ */
onerror(error: RequestError): void; onerror(error: any): void;
} }
import Component from "../Component";

View File

@@ -1,25 +1,11 @@
import Component from '../Component';
import { FocusTrap } from '../utils/focusTrap';
import type ModalManagerState from '../states/ModalManagerState';
import type Mithril from 'mithril';
interface IModalManagerAttrs {
state: ModalManagerState;
}
/** /**
* The `ModalManager` component manages a modal dialog. Only one modal dialog * The `ModalManager` component manages a modal dialog. Only one modal dialog
* can be shown at once; loading a new component into the ModalManager will * can be shown at once; loading a new component into the ModalManager will
* overwrite the previous one. * overwrite the previous one.
*/ */
export default class ModalManager extends Component<IModalManagerAttrs> { export default class ModalManager extends Component<import("../Component").ComponentAttrs> {
protected focusTrap: FocusTrap | undefined; constructor();
/** animateShow(readyCallback: any): void;
* Whether a modal is currently shown by this modal manager.
*/
protected modalShown: boolean;
view(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): Mithril.Children;
oncreate(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): void;
onupdate(vnode: Mithril.VnodeDOM<IModalManagerAttrs, this>): void;
animateShow(readyCallback: () => void): void;
animateHide(): void; animateHide(): void;
} }
export {}; import Component from "../Component";

View File

@@ -13,28 +13,28 @@
* - `drawer` Whether or not to show a button to toggle the app's drawer if * - `drawer` Whether or not to show a button to toggle the app's drawer if
* there is no more history to pop. * there is no more history to pop.
*/ */
export default class Navigation extends Component<import("../Component").ComponentAttrs, undefined> { export default class Navigation extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
/** /**
* Get the back button. * Get the back button.
* *
* @return {import('mithril').Children} * @return {Object}
* @protected * @protected
*/ */
protected getBackButton(): import('mithril').Children; protected getBackButton(): Object;
/** /**
* Get the pane pinned toggle button. * Get the pane pinned toggle button.
* *
* @return {import('mithril').Children} * @return {Object|String}
* @protected * @protected
*/ */
protected getPaneButton(): import('mithril').Children; protected getPaneButton(): Object | string;
/** /**
* Get the drawer toggle button. * Get the drawer toggle button.
* *
* @return {import('mithril').Children} * @return {Object|String}
* @protected * @protected
*/ */
protected getDrawerButton(): import('mithril').Children; protected getDrawerButton(): Object | string;
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -1,28 +1,27 @@
import type Mithril from 'mithril';
import Component from '../Component';
export interface IPageAttrs {
key?: number;
routeName: string;
}
/** /**
* The `Page` component * The `Page` component
* *
* @abstract * @abstract
*/ */
export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs> extends Component<CustomAttrs> { export default class Page extends Component<import("../Component").ComponentAttrs> {
constructor();
/** /**
* A class name to apply to the body while the route is active. * A class name to apply to the body while the route is active.
*
* @type {String}
*/ */
protected bodyClass: string; bodyClass: string | undefined;
/** /**
* Whether we should scroll to the top of the page when its rendered. * Whether we should scroll to the top of the page when its rendered.
*
* @type {Boolean}
*/ */
protected scrollTopOnCreate: boolean; scrollTopOnCreate: boolean | undefined;
/** /**
* Whether the browser should restore scroll state on refreshes. * Whether the browser should restore scroll state on refreshes.
*
* @type {Boolean}
*/ */
protected useBrowserScrollRestoration: boolean; useBrowserScrollRestoration: boolean | undefined;
oninit(vnode: Mithril.Vnode<CustomAttrs, this>): void;
oncreate(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>): void;
} }
import Component from "../Component";

View File

@@ -6,7 +6,7 @@
* *
* - `text` * - `text`
*/ */
export default class Placeholder extends Component<import("../Component").ComponentAttrs, undefined> { export default class Placeholder extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -1,12 +1,3 @@
/// <reference types="mithril" /> export default class RequestErrorModal extends Modal {
import type RequestError from '../utils/RequestError';
import Modal, { IInternalModalAttrs } from './Modal';
export interface IRequestErrorModalAttrs extends IInternalModalAttrs {
error: RequestError;
formattedError: string[];
}
export default class RequestErrorModal<CustomAttrs extends IRequestErrorModalAttrs = IRequestErrorModalAttrs> extends Modal<CustomAttrs> {
className(): string;
title(): string;
content(): JSX.Element;
} }
import Modal from "./Modal";

View File

@@ -6,11 +6,8 @@
* - `onchange` A callback to run when the selected value is changed. * - `onchange` A callback to run when the selected value is changed.
* - `value` The value of the selected option. * - `value` The value of the selected option.
* - `disabled` Disabled state for the input. * - `disabled` Disabled state for the input.
* - `wrapperAttrs` A map of attrs to be passed to the DOM element wrapping the `<select>`
*
* Other attributes are passed directly to the `<select>` element rendered to the DOM.
*/ */
export default class Select extends Component<import("../Component").ComponentAttrs, undefined> { export default class Select extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
import Component from "../Component"; import Component from "../Component";

View File

@@ -2,7 +2,7 @@ export default Separator;
/** /**
* The `Separator` component defines a menu separator item. * The `Separator` component defines a menu separator item.
*/ */
declare class Separator extends Component<import("../Component").ComponentAttrs, undefined> { declare class Separator extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
} }
declare namespace Separator { declare namespace Separator {

View File

@@ -7,10 +7,9 @@ export default class SplitDropdown extends Dropdown {
* Get the first child. If the first child is an array, the first item in that * Get the first child. If the first child is an array, the first item in that
* array will be returned. * array will be returned.
* *
* @param {unknown[] | unknown} children * @return {*}
* @return {unknown}
* @protected * @protected
*/ */
protected getFirstChild(children: unknown[] | unknown): unknown; protected getFirstChild(children: any): any;
} }
import Dropdown from "./Dropdown"; import Dropdown from "./Dropdown";

View File

@@ -11,7 +11,7 @@
* - `disabled` * - `disabled`
* - `preview` * - `preview`
*/ */
export default class TextEditor extends Component<import("../Component").ComponentAttrs, undefined> { export default class TextEditor extends Component<import("../Component").ComponentAttrs> {
constructor(); constructor();
/** /**
* The value of the editor. * The value of the editor.
@@ -36,19 +36,19 @@ export default class TextEditor extends Component<import("../Component").Compone
/** /**
* Build an item list for the text editor controls. * Build an item list for the text editor controls.
* *
* @return {ItemList<import('mithril').Children>} * @return {ItemList}
*/ */
controlItems(): ItemList<import('mithril').Children>; controlItems(): ItemList;
/** /**
* Build an item list for the toolbar controls. * Build an item list for the toolbar controls.
* *
* @return {ItemList<import('mithril').Children>} * @return {ItemList}
*/ */
toolbarItems(): ItemList<import('mithril').Children>; toolbarItems(): ItemList;
/** /**
* Handle input into the textarea. * Handle input into the textarea.
* *
* @param {string} value * @param {String} value
*/ */
oninput(value: string): void; oninput(value: string): void;
/** /**

View File

@@ -1,14 +1,8 @@
/** /**
* The `TextEditorButton` component displays a button suitable for the text * The `TextEditorButton` component displays a button suitable for the text
* editor toolbar. * editor toolbar.
*
* Automatically creates tooltips using the Tooltip component and provided text.
*
* ## Attrs
* - `title` - Tooltip for the button
*/ */
export default class TextEditorButton extends Button<import("./Button").IButtonAttrs> { export default class TextEditorButton extends Button {
static initAttrs(attrs: any): void; static initAttrs(attrs: any): void;
constructor();
} }
import Button from "./Button"; import Button from "./Button";

View File

@@ -19,11 +19,11 @@
* // something that needs to be run on creation and update * // something that needs to be run on creation and update
* }); * });
* *
* @param object The object that owns the method * @param {object} object The object that owns the method
* @param methods The name or names of the method(s) to extend * @param {string|string[]} methods The name or names of the method(s) to extend
* @param callback A callback which mutates the method's output * @param {function} callback A callback which mutates the method's output
*/ */
export declare function extend<T extends Record<string, any>, K extends KeyOfType<T, Function>>(object: T, methods: K | K[], callback: (this: T, val: ReturnType<T[K]>, ...args: Parameters<T[K]>) => void): void; export function extend(object: object, methods: string | string[], callback: Function): void;
/** /**
* Override an object's method by replacing it with a new function, so that the * Override an object's method by replacing it with a new function, so that the
* new function will be run every time the object's method is called. * new function will be run every time the object's method is called.
@@ -47,8 +47,8 @@ export declare function extend<T extends Record<string, any>, K extends KeyOfTyp
* // something that needs to be run on creation and update * // something that needs to be run on creation and update
* }); * });
* *
* @param object The object that owns the method * @param {object} object The object that owns the method
* @param methods The name or names of the method(s) to override * @param {string|string[]} method The name or names of the method(s) to override
* @param newMethod The method to replace it with * @param {function} newMethod The method to replace it with
*/ */
export declare function override<T extends Record<any, any>, K extends KeyOfType<T, Function>>(object: T, methods: K | K[], newMethod: (this: T, orig: T[K], ...args: Parameters<T[K]>) => void): void; export function override(object: object, methods: any, newMethod: Function): void;

View File

@@ -1,5 +1,5 @@
export default class Model { export default class Model {
constructor(type: any, model?: null); constructor(type: any, model?: any);
type: any; type: any;
attributes: any[]; attributes: any[];
hasOnes: any[]; hasOnes: any[];

View File

@@ -1,12 +1,9 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
import type { ComponentAttrs } from '../Component';
import User from '../models/User'; import User from '../models/User';
export interface AvatarAttrs extends ComponentAttrs {
}
/** /**
* The `avatar` helper displays a user's avatar. * The `avatar` helper displays a user's avatar.
* *
* @param user * @param user
* @param attrs Attributes to apply to the avatar element * @param attrs Attributes to apply to the avatar element
*/ */
export default function avatar(user: User, attrs?: ComponentAttrs): Mithril.Vnode; export default function avatar(user: User, attrs?: Object): Mithril.Vnode;

View File

@@ -1,26 +0,0 @@
/**
* Calls `console.warn` with the provided arguments, but only if the forum is in debug mode.
*
* This function is intended to provide warnings to extension developers about issues with
* their extensions that may not be easily noticed when testing, such as accessibility
* issues.
*
* These warnings should be hidden on production forums to ensure webmasters are not
* inundated with do-gooders telling them they have an issue when it isn't something they
* can fix.
*/
export default function fireDebugWarning(...args: Parameters<typeof console.warn>): void;
/**
* Fire a Flarum deprecation warning which is shown in the JS console.
*
* These warnings are only shown when the forum is in debug mode, and the function exists to
* reduce bundle size caused by multiple warnings across our JavaScript.
*
* @param message The message to display. (Short, but sweet, please!)
* @param githubId The PR or Issue ID with more info in relation to this change.
* @param [removedFrom] The version in which this feature will be completely removed. (default: 2.0)
* @param [repo] The repo which the issue or PR is located in. (default: flarum/core)
*
* @see {@link fireDebugWarning}
*/
export declare function fireDeprecationWarning(message: string, githubId: string, removedFrom?: string, repo?: string): void;

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
/** /**
* The `fullTime` helper displays a formatted time string wrapped in a <time> * The `fullTime` helper displays a formatted time string wrapped in a <time>
* tag. * tag.

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
/** /**
* The `highlight` helper searches for a word phrase in a string, and wraps * The `highlight` helper searches for a word phrase in a string, and wraps
* matches with the <mark> tag. * matches with the <mark> tag.

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
/** /**
* The `humanTime` helper displays a time in a human-friendly time-ago format * The `humanTime` helper displays a time in a human-friendly time-ago format
* (e.g. '12 days ago'), wrapped in a <time> tag with other information about * (e.g. '12 days ago'), wrapped in a <time> tag with other information about

View File

@@ -1,4 +1,4 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
/** /**
* The `icon` helper displays an icon. * The `icon` helper displays an icon.
* *

View File

@@ -1,34 +1,6 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
import { ComponentAttrs } from '../Component';
declare type ModdedVnodeAttrs = {
itemClassName?: string;
key?: string;
};
declare type ModdedTag = Mithril.Vnode['tag'] & {
isListItem?: boolean;
isActive?: (attrs: ComponentAttrs) => boolean;
};
declare type ModdedVnode = Mithril.Vnode<ModdedVnodeAttrs> & {
itemName?: string;
itemClassName?: string;
tag: ModdedTag;
};
declare type ModdedChild = ModdedVnode | string | number | boolean | null | undefined;
declare type ModdedChildArray = ModdedChildren[];
declare type ModdedChildren = ModdedChild | ModdedChildArray;
/** /**
* This type represents an element of a list returned by `ItemList.toArray()`, * The `listItems` helper wraps a collection of components in <li> tags,
* coupled with some static properties used on various components.
*/
export declare type ModdedChildrenWithItemName = ModdedChildren & {
itemName?: string;
};
/**
* The `listItems` helper wraps an array of components in the provided tag,
* stripping out any unnecessary `Separator` components. * stripping out any unnecessary `Separator` components.
*
* By default, this tag is an `<li>` tag, but this is customisable through the
* second function parameter, `customTag`.
*/ */
export default function listItems<Attrs extends ComponentAttrs>(rawItems: ModdedChildrenWithItemName[], customTag?: VnodeElementTag<Attrs>, attributes?: Attrs): Mithril.Vnode[]; export default function listItems(items: Mithril.Vnode | Array<Mithril.Vnode>): Array<Mithril.Vnode>;
export {};

View File

@@ -6,7 +6,7 @@
* punctuateSeries(['Toby', 'Franz', 'Dominion']) // Toby, Franz, and Dominion * punctuateSeries(['Toby', 'Franz', 'Dominion']) // Toby, Franz, and Dominion
* ``` * ```
* *
* @param {import('mithril').Children[]} items * @param {Array} items
* @return {import('mithril').Children}')} * @return {VirtualElement}
*/ */
export default function punctuateSeries(items: import('mithril').Children[]): import('mithril').Children; export default function punctuateSeries(items: any[]): any;

View File

@@ -1,6 +1,6 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
import User from '../models/User'; import User from '../models/User';
/** /**
* The `useronline` helper displays a green circle if the user is online * The `useronline` helper displays a green circle if the user is online
*/ */
export default function userOnline(user: User): Mithril.Vnode<{}, {}> | null; export default function userOnline(user: User): Mithril.Vnode;

View File

@@ -1,7 +1,7 @@
import type Mithril from 'mithril'; import * as Mithril from 'mithril';
import User from '../models/User'; import User from '../models/User';
/** /**
* The `username` helper displays a user's username in a <span class="username"> * The `username` helper displays a user's username in a <span class="username">
* tag. If the user doesn't exist, the username will be displayed as [deleted]. * tag. If the user doesn't exist, the username will be displayed as [deleted].
*/ */
export default function username(user: User | null | undefined | false): Mithril.Vnode; export default function username(user: User): Mithril.Vnode;

View File

@@ -1,13 +1,2 @@
import 'expose-loader?exposes=$,jQuery!jquery'; export { Extend };
import 'expose-loader?exposes=m!mithril'; import * as Extend from "./extend/index";
import 'expose-loader?exposes=dayjs!dayjs';
import 'bootstrap/js/affix';
import 'bootstrap/js/dropdown';
import 'bootstrap/js/modal';
import 'bootstrap/js/tooltip';
import 'bootstrap/js/transition';
import 'jquery.hotkeys/jquery.hotkeys';
import * as Extend from './extend/index';
import app from './app';
export { Extend, app };
import './utils/arrayFlatPolyfill';

Some files were not shown because too many files have changed in this diff Show More