├── LICENSE.md
├── README.md
├── composer.json
├── extend.php
├── icon.svg
├── js
├── admin.js
├── dist-typings
│ ├── admin
│ │ └── index.d.ts
│ └── forum
│ │ ├── components
│ │ └── TagsPage.d.ts
│ │ └── index.d.ts
├── dist
│ ├── admin.js
│ ├── admin.js.map
│ ├── forum.js
│ └── forum.js.map
├── forum.js
├── package.json
├── src
│ ├── admin
│ │ └── index.js
│ └── forum
│ │ ├── components
│ │ └── TagsPage.js
│ │ └── index.js
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
├── less
├── admin.less
├── admin
│ ├── AdminPage.less
│ ├── Alert.less
│ ├── App.less
│ ├── ExtensionsPage.less
│ ├── FormGroup.less
│ ├── GroupPermissions.less
│ ├── PermissionsPage.less
│ ├── StatisticsWidget.less
│ ├── StatusWidget.less
│ ├── UserListPage.less
│ ├── Widget.less
│ ├── sideNav.less
│ └── variables.less
├── common
│ ├── App.less
│ ├── Button.less
│ ├── Dropdown.less
│ ├── common.less
│ ├── mixins
│ │ ├── asirem-block.less
│ │ ├── asirem-border-radius.less
│ │ └── asirem-icon-block.less
│ ├── scaffolding.less
│ └── variables.less
├── forum.less
└── forum
│ ├── App.less
│ ├── DiscussionList.less
│ ├── Footer.less
│ ├── Hero.less
│ ├── Post.less
│ ├── TagsPage.less
│ └── sideNav.less
├── locale
└── .gitkeep
└── views
└── frontend
└── admin.blade.php
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Sami Mazouz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Asirem
4 |
5 |
6 |
7 | The weird Flarum theme made with potatoes as fuel, by Afrux .
8 |
9 |
10 |
11 |
12 |
13 | ### Installation
14 |
15 | Install with composer:
16 |
17 | ```sh
18 | composer require afrux/asirem
19 | ```
20 |
21 | The following are nice to install with the theme as well:
22 |
23 | ```sh
24 | composer require sycho/flarum-advanced-extension-categories
25 | ```
26 |
27 | ### Updating
28 |
29 | ```sh
30 | composer update afrux/asirem --with-dependencies
31 | php flarum cache:clear
32 | ```
33 |
34 | ### Links
35 |
36 | - [Packagist](https://packagist.org/packages/afrux/asirem)
37 | - [GitHub](https://github.com/afrux/asirem)
38 | - [Discuss](https://discuss.flarum.org/d/27939-asirem-theme)
39 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afrux/asirem",
3 | "description": "The weird Flarum theme made with potatoes as fuel, by Afrux.",
4 | "keywords": [
5 | "flarum"
6 | ],
7 | "type": "flarum-extension",
8 | "license": "MIT",
9 | "funding": [
10 | {
11 | "type": "other",
12 | "url": "https://www.buymeacoffee.com/sycho"
13 | }
14 | ],
15 | "require": {
16 | "flarum/core": "^1.8.0",
17 | "afrux/flarum-theme-base": "^0.1.0"
18 | },
19 | "require-dev": {
20 | "sycho/flarum-uikit": "0.1.x-dev",
21 | "afrux/flarum-theme-base": "0.1.x-dev"
22 | },
23 | "authors": [
24 | {
25 | "name": "Sami Mazouz",
26 | "email": "ilyasmazouz@gmail.com",
27 | "role": "Developer"
28 | }
29 | ],
30 | "support": {
31 | "issues": "https://github.com/afrux/asirem/issues",
32 | "source": "https://github.com/afrux/asirem",
33 | "forum": "https://discuss.flarum.org/d/27939"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Afrux\\Asirem\\": "src/"
38 | }
39 | },
40 | "autoload-dev": {
41 | "psr-4": {
42 | "Afrux\\Asirem\\Tests\\": "tests/"
43 | }
44 | },
45 | "extra": {
46 | "branch-alias": {
47 | "dev-master": "0.1.x-dev"
48 | },
49 | "flarum-extension": {
50 | "title": "Asirem",
51 | "category": "theme",
52 | "optional-dependencies": [
53 | "flarum/tags",
54 | "flarum/approval",
55 | "sycho/flarum-advanced-extension-categories"
56 | ],
57 | "icon": {
58 | "image": "icon.svg"
59 | }
60 | },
61 | "flarum-cli": {
62 | "modules": {
63 | "admin": true,
64 | "forum": true,
65 | "js": true,
66 | "jsCommon": false,
67 | "css": true,
68 | "gitConf": true,
69 | "githubActions": true,
70 | "prettier": true,
71 | "typescript": true,
72 | "bundlewatch": false,
73 | "backendTesting": false,
74 | "editorConfig": true,
75 | "styleci": true
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/extend.php:
--------------------------------------------------------------------------------
1 | splitToNavAndContent()
24 | ->normalizeStatusWidgetStructure()
25 | ->normalizeAdminHeaderStructure()
26 | ->normalizeExtensionPageStructure()
27 | ->normalizeUserTable()
28 | ->addExtensionsPage(),
29 |
30 | (new Extend\Frontend('forum'))
31 | ->js(__DIR__.'/js/dist/forum.js')
32 | ->css(__DIR__.'/less/forum.less'),
33 |
34 | (new Extend\Frontend('admin'))
35 | ->js(__DIR__.'/js/dist/admin.js')
36 | ->css(__DIR__.'/less/admin.less')
37 | ->content(function (Document $document) {
38 | $document->layoutView = "afrux-asirem::frontend.admin";
39 | }),
40 |
41 | (new Extend\View)
42 | ->namespace("afrux-asirem", __DIR__."/views"),
43 |
44 | new Extend\Locales(__DIR__.'/locale'),
45 | ];
46 |
--------------------------------------------------------------------------------
/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Asirem
6 |
7 |
--------------------------------------------------------------------------------
/js/admin.js:
--------------------------------------------------------------------------------
1 | export * from './src/admin';
2 |
--------------------------------------------------------------------------------
/js/dist-typings/admin/index.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/js/dist-typings/forum/components/TagsPage.d.ts:
--------------------------------------------------------------------------------
1 | export default class TagsPage {
2 | view(): JSX.Element;
3 | }
4 |
--------------------------------------------------------------------------------
/js/dist-typings/forum/index.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/js/dist/admin.js:
--------------------------------------------------------------------------------
1 | (()=>{var e={n:t=>{var o=t&&t.__esModule?()=>t.default:()=>t;return e.d(o,{a:o}),o},d:(t,o)=>{for(var r in o)e.o(o,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:o[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};(()=>{"use strict";e.r(t);const o=flarum.core.compat.extend,r=flarum.core.compat["admin/components/PermissionsPage"];var n=e.n(r);const a=flarum.core.compat["admin/components/PermissionGrid"];var i=e.n(a);app.initializers.add("afrux-asirem",(function(e){(0,o.extend)(n().prototype,"content",(function(e){e&&e[0]&&e[0].children&&e[0].children[1]&&(e[0].children[1].attrs.className+=" Button--dashed")})),(0,o.override)(i().prototype,"view",(function(e,t){return[m("div",{className:"PermissionsPage-permissions-overflow"},e(t))]})),(0,o.extend)(i().prototype,["oncreate","onupdate"],(function(e){$(".PermissionGrid-child .Button--text").removeClass("Button--text")}))}),-999999)})(),module.exports=t})();
2 | //# sourceMappingURL=admin.js.map
--------------------------------------------------------------------------------
/js/dist/admin.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"admin.js","mappings":"MACA,IAAIA,EAAsB,CCA1BA,EAAyBC,IACxB,IAAIC,EAASD,GAAUA,EAAOE,WAC7B,IAAOF,EAAiB,QACxB,IAAM,EAEP,OADAD,EAAoBI,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRF,EAAwB,CAACM,EAASC,KACjC,IAAI,IAAIC,KAAOD,EACXP,EAAoBS,EAAEF,EAAYC,KAASR,EAAoBS,EAAEH,EAASE,IAC5EE,OAAOC,eAAeL,EAASE,EAAK,CAAEI,YAAY,EAAMC,IAAKN,EAAWC,MCJ3ER,EAAwB,CAACc,EAAKC,IAAUL,OAAOM,UAAUC,eAAeC,KAAKJ,EAAKC,GCClFf,EAAyBM,IACH,oBAAXa,QAA0BA,OAAOC,aAC1CV,OAAOC,eAAeL,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DX,OAAOC,eAAeL,EAAS,aAAc,CAAEe,OAAO,M,+BCLvD,MAAM,EAA+BC,OAAOC,KAAKC,OAAe,OCA1D,EAA+BF,OAAOC,KAAKC,OAAO,oC,aCAxD,MAAM,EAA+BF,OAAOC,KAAKC,OAAO,mC,aCIxDC,IAAIC,aAAaC,IACf,gBACA,SAACF,IACCG,EAAAA,EAAAA,QAAOC,IAAAA,UAA2B,WAAW,SAAUC,GACjDA,GAASA,EAAM,IAAMA,EAAM,GAAGC,UAAYD,EAAM,GAAGC,SAAS,KAC9DD,EAAM,GAAGC,SAAS,GAAGC,MAAMC,WAAa,uBAI5CC,EAAAA,EAAAA,UAASC,IAAAA,UAA0B,QAAQ,SAAUC,EAAUN,GAC7D,MAAO,CAAC,SAAKG,UAAU,wCAAwCG,EAASN,SAG1EF,EAAAA,EAAAA,QAAOO,IAAAA,UAA0B,CAAC,WAAY,aAAa,SAAUL,GACnEO,EAAE,uCAAuCC,YAAY,sBAGxD,S","sources":["webpack://@afrux/asirem/webpack/bootstrap","webpack://@afrux/asirem/webpack/runtime/compat get default export","webpack://@afrux/asirem/webpack/runtime/define property getters","webpack://@afrux/asirem/webpack/runtime/hasOwnProperty shorthand","webpack://@afrux/asirem/webpack/runtime/make namespace object","webpack://@afrux/asirem/external root \"flarum.core.compat['extend']\"","webpack://@afrux/asirem/external root \"flarum.core.compat['admin/components/PermissionsPage']\"","webpack://@afrux/asirem/external root \"flarum.core.compat['admin/components/PermissionGrid']\"","webpack://@afrux/asirem/./src/admin/index.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['extend'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/components/PermissionsPage'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['admin/components/PermissionGrid'];","import { extend, override } from 'flarum/extend';\nimport PermissionsPage from 'flarum/admin/components/PermissionsPage';\nimport PermissionGrid from 'flarum/admin/components/PermissionGrid';\n\napp.initializers.add(\n 'afrux-asirem',\n (app) => {\n extend(PermissionsPage.prototype, 'content', function (vnode) {\n if (vnode && vnode[0] && vnode[0].children && vnode[0].children[1]) {\n vnode[0].children[1].attrs.className += ' Button--dashed';\n }\n });\n\n override(PermissionGrid.prototype, 'view', function (original, vnode) {\n return [{original(vnode)}
];\n });\n\n extend(PermissionGrid.prototype, ['oncreate', 'onupdate'], function (vnode) {\n $('.PermissionGrid-child .Button--text').removeClass('Button--text');\n });\n },\n -999999\n);\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","flarum","core","compat","app","initializers","add","extend","PermissionsPage","vnode","children","attrs","className","override","PermissionGrid","original","$","removeClass"],"sourceRoot":""}
--------------------------------------------------------------------------------
/js/dist/forum.js:
--------------------------------------------------------------------------------
1 | (()=>{var t={n:e=>{var s=e&&e.__esModule?()=>e.default:()=>e;return t.d(s,{a:s}),s},d:(e,s)=>{for(var a in s)t.o(s,a)&&!t.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:s[a]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};(()=>{"use strict";function s(){return s=Object.assign||function(t){for(var e=1;e {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export default function _extends() {\n _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n\n return target;\n };\n\n return _extends.apply(this, arguments);\n}","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['common/extend'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['forum/components/DiscussionListItem'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['tags/components/TagsPage'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['components/IndexPage'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['components/Link'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['components/LoadingIndicator'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['helpers/listItems'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['helpers/humanTime'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['tags/common/helpers/tagIcon'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['tags/common/helpers/tagLabel'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['tags/common/utils/sortTags'];","import IndexPage from 'flarum/components/IndexPage';\nimport Link from 'flarum/components/Link';\nimport LoadingIndicator from 'flarum/components/LoadingIndicator';\nimport listItems from 'flarum/helpers/listItems';\nimport humanTime from 'flarum/helpers/humanTime';\n\nimport tagIcon from 'flarum/tags/common/helpers/tagIcon';\nimport tagLabel from 'flarum/tags/common/helpers/tagLabel';\nimport sortTags from 'flarum/tags/common/utils/sortTags';\n\nexport default class TagsPage {\n view() {\n if (this.loading) {\n return ;\n }\n\n const pinned = this.tags.filter((tag) => tag.position() !== null);\n const cloud = this.tags.filter((tag) => tag.position() === null);\n\n return (\n \n {IndexPage.prototype.hero()}\n
\n
\n {listItems(IndexPage.prototype.sidebarItems().toArray())} \n \n\n
\n
\n {pinned.map((tag) => {\n const lastPostedDiscussion = tag.lastPostedDiscussion();\n const children = sortTags(tag.children() || []);\n const tagIconNode = tagIcon(tag, {}, { useColor: false });\n\n if (tagIconNode.attrs.style && tagIconNode.attrs.style.backgroundColor) {\n delete tagIconNode.attrs.style.backgroundColor;\n }\n\n return (\n \n \n {tagIconNode}
\n \n
{tag.name()} \n
{tag.description()}
\n {children && children.length ? (\n
\n {children.map((child) => [\n \n {child.name()}\n ,\n ' ',\n ])}\n
\n ) : (\n ''\n )}\n {lastPostedDiscussion ? (\n
\n
{lastPostedDiscussion.title()} \n {humanTime(lastPostedDiscussion.lastPostedAt())}\n \n ) : (\n
\n )}\n
\n \n \n );\n })}\n \n\n {cloud.length ?
{cloud.map((tag) => [tagLabel(tag, { link: true }), ' '])}
: ''}\n
\n
\n
\n );\n }\n}\n","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['utils/string'];","const __WEBPACK_NAMESPACE_OBJECT__ = flarum.core.compat['extensions/afrux-theme-base/forum/components/Footer'];","import { extend, override } from 'flarum/common/extend';\nimport DiscussionListItem from 'flarum/forum/components/DiscussionListItem';\nimport TagsPage from 'flarum/tags/components/TagsPage';\nimport AsiremTagsPage from './components/TagsPage';\nimport { truncate } from 'flarum/utils/string';\n\nimport Footer from 'flarum/extensions/afrux-theme-base/forum/components/Footer';\n\napp.initializers.add('afrux-asirem', () => {\n extend(DiscussionListItem.prototype, 'view', function (vnode) {\n const discussionListItemContent = vnode.children.find(\n (e) => e && e.tag === 'div' && e.attrs && e.attrs.className.includes('DiscussionListItem-content')\n );\n\n discussionListItemContent.children[0] = (\n {[discussionListItemContent.children[0], discussionListItemContent.children[1]]}
\n );\n\n delete discussionListItemContent.children[1];\n\n discussionListItemContent.children[3] = {discussionListItemContent.children[3]}
;\n\n if (this.attrs.discussion.tags() && this.attrs.discussion.tags()[0] && this.attrs.discussion.tags()[0].color()) {\n vnode.attrs.style = { '--tag-color': this.attrs.discussion.tags()[0].color(), ...(vnode.attrs.style || {}) };\n }\n\n if (this.attrs.discussion.isUnread()) {\n vnode.attrs.className += ' DiscussionListItem--unread';\n }\n });\n\n extend(DiscussionListItem.prototype, 'infoItems', function (items) {\n if (!items.has('excerpt')) {\n const firstPost = this.attrs.discussion.firstPost();\n\n if (firstPost) {\n const excerpt = truncate(firstPost.contentPlain(), 175);\n\n items.add('excerpt', {excerpt}
, -100);\n }\n }\n });\n\n override(Footer.prototype, 'separator', function () {\n return (\n \n \n \n );\n });\n\n override(TagsPage.prototype, 'view', AsiremTagsPage.prototype.view);\n});\n"],"names":["__webpack_require__","module","getter","__esModule","d","a","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","_extends","assign","target","i","arguments","length","source","apply","this","flarum","core","compat","TagsPage","view","loading","pinned","tags","filter","tag","position","cloud","className","IndexPage","listItems","toArray","map","lastPostedDiscussion","children","sortTags","tagIconNode","tagIcon","useColor","attrs","style","backgroundColor","color","href","app","route","name","description","child","discussion","lastPostNumber","title","humanTime","lastPostedAt","tagLabel","link","initializers","add","extend","DiscussionListItem","vnode","discussionListItemContent","find","e","includes","isUnread","items","has","firstPost","excerpt","truncate","contentPlain","override","Footer","xmlns","width","height","AsiremTagsPage"],"sourceRoot":""}
--------------------------------------------------------------------------------
/js/forum.js:
--------------------------------------------------------------------------------
1 | export * from './src/forum';
2 |
--------------------------------------------------------------------------------
/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@afrux/asirem",
3 | "version": "0.0.0",
4 | "private": true,
5 | "prettier": "@flarum/prettier-config",
6 | "dependencies": {},
7 | "devDependencies": {
8 | "@flarum/prettier-config": "^1.0.0",
9 | "flarum-tsconfig": "^1.0.2",
10 | "flarum-webpack-config": "^2.0.0",
11 | "prettier": "^2.5.1",
12 | "typescript": "^4.5.4",
13 | "typescript-coverage-report": "^0.6.1",
14 | "webpack": "^5.65.0",
15 | "webpack-cli": "^4.9.1"
16 | },
17 | "scripts": {
18 | "ci": "<% if (params.jsPackageManager) { %>yarn install --immutable --immutable-cache<% } else { %>npm ci<% } %>",
19 | "dev": "webpack --mode development --watch",
20 | "build": "webpack --mode production",
21 | "analyze": "cross-env ANALYZER=true yarn run build",
22 | "format": "prettier --write src",
23 | "format-check": "prettier --check src",
24 | "clean-typings": "npx rimraf dist-typings && mkdir dist-typings",
25 | "build-typings": "yarn run clean-typings && ([ -e src/@types ] && cp -r src/@types dist-typings/@types || true) && tsc && yarn run post-build-typings",
26 | "check-typings": "tsc --noEmit --emitDeclarationOnly false",
27 | "check-typings-coverage": "typescript-coverage-report",
28 | "post-build-typings": "find dist-typings -type f -name '*.d.ts' -print0 | xargs -0 sed -i 's,../src/@types,@types,g'"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/js/src/admin/index.js:
--------------------------------------------------------------------------------
1 | import { extend, override } from 'flarum/extend';
2 | import PermissionsPage from 'flarum/admin/components/PermissionsPage';
3 | import PermissionGrid from 'flarum/admin/components/PermissionGrid';
4 |
5 | app.initializers.add(
6 | 'afrux-asirem',
7 | (app) => {
8 | extend(PermissionsPage.prototype, 'content', function (vnode) {
9 | if (vnode && vnode[0] && vnode[0].children && vnode[0].children[1]) {
10 | vnode[0].children[1].attrs.className += ' Button--dashed';
11 | }
12 | });
13 |
14 | override(PermissionGrid.prototype, 'view', function (original, vnode) {
15 | return [{original(vnode)}
];
16 | });
17 |
18 | extend(PermissionGrid.prototype, ['oncreate', 'onupdate'], function (vnode) {
19 | $('.PermissionGrid-child .Button--text').removeClass('Button--text');
20 | });
21 | },
22 | -999999
23 | );
24 |
--------------------------------------------------------------------------------
/js/src/forum/components/TagsPage.js:
--------------------------------------------------------------------------------
1 | import IndexPage from 'flarum/components/IndexPage';
2 | import Link from 'flarum/components/Link';
3 | import LoadingIndicator from 'flarum/components/LoadingIndicator';
4 | import listItems from 'flarum/helpers/listItems';
5 | import humanTime from 'flarum/helpers/humanTime';
6 |
7 | import tagIcon from 'flarum/tags/common/helpers/tagIcon';
8 | import tagLabel from 'flarum/tags/common/helpers/tagLabel';
9 | import sortTags from 'flarum/tags/common/utils/sortTags';
10 |
11 | export default class TagsPage {
12 | view() {
13 | if (this.loading) {
14 | return ;
15 | }
16 |
17 | const pinned = this.tags.filter((tag) => tag.position() !== null);
18 | const cloud = this.tags.filter((tag) => tag.position() === null);
19 |
20 | return (
21 |
22 | {IndexPage.prototype.hero()}
23 |
24 |
25 | {listItems(IndexPage.prototype.sidebarItems().toArray())}
26 |
27 |
28 |
29 |
30 | {pinned.map((tag) => {
31 | const lastPostedDiscussion = tag.lastPostedDiscussion();
32 | const children = sortTags(tag.children() || []);
33 | const tagIconNode = tagIcon(tag, {}, { useColor: false });
34 |
35 | if (tagIconNode.attrs.style && tagIconNode.attrs.style.backgroundColor) {
36 | delete tagIconNode.attrs.style.backgroundColor;
37 | }
38 |
39 | return (
40 |
41 |
42 | {tagIconNode}
43 |
44 |
{tag.name()}
45 |
{tag.description()}
46 | {children && children.length ? (
47 |
48 | {children.map((child) => [
49 |
50 | {child.name()}
51 | ,
52 | ' ',
53 | ])}
54 |
55 | ) : (
56 | ''
57 | )}
58 | {lastPostedDiscussion ? (
59 |
63 |
{lastPostedDiscussion.title()}
64 | {humanTime(lastPostedDiscussion.lastPostedAt())}
65 |
66 | ) : (
67 |
68 | )}
69 |
70 |
71 |
72 | );
73 | })}
74 |
75 |
76 | {cloud.length ?
{cloud.map((tag) => [tagLabel(tag, { link: true }), ' '])}
: ''}
77 |
78 |
79 |
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/js/src/forum/index.js:
--------------------------------------------------------------------------------
1 | import { extend, override } from 'flarum/common/extend';
2 | import DiscussionListItem from 'flarum/forum/components/DiscussionListItem';
3 | import TagsPage from 'flarum/tags/components/TagsPage';
4 | import AsiremTagsPage from './components/TagsPage';
5 | import { truncate } from 'flarum/utils/string';
6 |
7 | import Footer from 'flarum/extensions/afrux-theme-base/forum/components/Footer';
8 |
9 | app.initializers.add('afrux-asirem', () => {
10 | extend(DiscussionListItem.prototype, 'view', function (vnode) {
11 | const discussionListItemContent = vnode.children.find(
12 | (e) => e && e.tag === 'div' && e.attrs && e.attrs.className.includes('DiscussionListItem-content')
13 | );
14 |
15 | discussionListItemContent.children[0] = (
16 | {[discussionListItemContent.children[0], discussionListItemContent.children[1]]}
17 | );
18 |
19 | delete discussionListItemContent.children[1];
20 |
21 | discussionListItemContent.children[3] = {discussionListItemContent.children[3]}
;
22 |
23 | if (this.attrs.discussion.tags() && this.attrs.discussion.tags()[0] && this.attrs.discussion.tags()[0].color()) {
24 | vnode.attrs.style = { '--tag-color': this.attrs.discussion.tags()[0].color(), ...(vnode.attrs.style || {}) };
25 | }
26 |
27 | if (this.attrs.discussion.isUnread()) {
28 | vnode.attrs.className += ' DiscussionListItem--unread';
29 | }
30 | });
31 |
32 | extend(DiscussionListItem.prototype, 'infoItems', function (items) {
33 | if (!items.has('excerpt')) {
34 | const firstPost = this.attrs.discussion.firstPost();
35 |
36 | if (firstPost) {
37 | const excerpt = truncate(firstPost.contentPlain(), 175);
38 |
39 | items.add('excerpt', {excerpt}
, -100);
40 | }
41 | }
42 | });
43 |
44 | override(Footer.prototype, 'separator', function () {
45 | return (
46 |
47 |
51 |
52 | );
53 | });
54 |
55 | override(TagsPage.prototype, 'view', AsiremTagsPage.prototype.view);
56 | });
57 |
--------------------------------------------------------------------------------
/js/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use Flarum's tsconfig as a starting point
3 | "extends": "flarum-tsconfig",
4 | // This will match all .ts, .tsx, .d.ts, .js, .jsx files in your `src` folder
5 | // and also tells your Typescript server to read core's global typings for
6 | // access to `dayjs` and `$` in the global namespace.
7 | "include": [
8 | "src/**/*",
9 | "../vendor/*/*/js/dist-typings/@types/**/*",
10 | //
11 | //
12 | "@types/**/*"
13 | ],
14 | "compilerOptions": {
15 | // This will output typings to `dist-typings`
16 | "declarationDir": "./dist-typings",
17 | "baseUrl": ".",
18 | "paths": {
19 | "flarum/*": ["../vendor/flarum/core/js/dist-typings/*"],
20 | //
21 | //
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/js/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('flarum-webpack-config')();
2 |
--------------------------------------------------------------------------------
/less/admin.less:
--------------------------------------------------------------------------------
1 | @import "common/common.less";
2 |
3 | @import "admin/variables.less";
4 | @import "admin/App.less";
5 | @import "admin/sideNav.less";
6 | @import "admin/AdminPage.less";
7 | @import "admin/ExtensionsPage.less";
8 | @import "admin/PermissionsPage.less";
9 | @import "admin/GroupPermissions.less";
10 | @import "admin/UserListPage.less";
11 | @import "admin/Widget.less";
12 | @import "admin/FormGroup.less";
13 | @import "admin/StatusWidget.less";
14 | @import "admin/StatisticsWidget.less";
15 | @import "admin/Alert.less";
16 |
--------------------------------------------------------------------------------
/less/admin/AdminPage.less:
--------------------------------------------------------------------------------
1 | .ThemeBase-AdminHeader {
2 | background-color: transparent;
3 | color: @text-color;
4 | padding: 0;
5 |
6 | &-title, h2 {
7 | color: inherit !important;
8 | }
9 |
10 | &-description {
11 | color: inherit;
12 | opacity: 0.8;
13 | }
14 |
15 | &-container {
16 | padding: 0 0 15px 0px !important;
17 | border-radius: 0 14px 14px 0;
18 |
19 | @media @desktop-up {
20 | .App-content & {
21 | margin: 0 30px;
22 | width: unset;
23 | max-width: unset;
24 | }
25 | }
26 | }
27 |
28 | &-icon {
29 | --size: 60px;
30 | font-size: 24px;
31 | width: var(--size);
32 | height: var(--size);
33 | border-radius: 14px;
34 | margin-right: 20px;
35 | border-radius: 100%;
36 | box-shadow: @asirem-box-shadow;
37 | background-color: @body-bg;
38 |
39 | > .icon {
40 | margin: 0;
41 | }
42 | }
43 | }
44 |
45 | @media @tablet, @phone {
46 | .ThemeBase-AdminHeader {
47 | padding-left: 15px;
48 | padding-right: 15px;
49 | }
50 | }
51 |
52 | .AdminPage {
53 | background-color: transparent !important;
54 | }
55 |
56 | .ThemeBase-ExtensionPage {
57 | padding-bottom: 24px;
58 |
59 | &-body {
60 | > .container {
61 | > :first-child {
62 | border-top-left-radius: @border-radius;
63 | border-top-right-radius: @border-radius;
64 | }
65 | > :last-child {
66 | border-bottom-left-radius: @border-radius;
67 | border-bottom-right-radius: @border-radius;
68 | }
69 | }
70 | }
71 |
72 | &-section {
73 | padding: 15px;
74 |
75 | .container {
76 | padding: 0;
77 | max-width: unset !important;
78 | width: unset !important;
79 | }
80 | }
81 |
82 | &-headerItems {
83 | padding-top: 5px;
84 | padding-bottom: 5px;
85 | }
86 |
87 | &-headerItems, &-sections-nav {
88 | padding-left: 15px;
89 | padding-right: 15px;
90 | }
91 |
92 | &-headerItems, &-sections {
93 | background-color: @body-bg;
94 |
95 | .Button {
96 | --control-bg: @control-bg;
97 |
98 | &--primary {
99 | --control-bg: @primary-color;
100 | }
101 | }
102 | }
103 |
104 | &-sections-nav {
105 | background-color: @control-bg-light;
106 |
107 | .Button {
108 | border-radius: 0;
109 | --control-bg: @control-bg-light;
110 |
111 | &--active {
112 | --control-bg: @control-bg;
113 | }
114 | }
115 | }
116 |
117 | &-ExtensionInfo {
118 | li, a {
119 | color: @control-color;
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/less/admin/Alert.less:
--------------------------------------------------------------------------------
1 | .Alert {
2 | &--info {
3 | background: @alert-info-bg;
4 | color: @alert-info-color;
5 | }
6 |
7 | &-controls:empty {
8 | display: none;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/less/admin/App.less:
--------------------------------------------------------------------------------
1 | .Asirem-adminWaves {
2 | display: none;
3 | }
4 |
5 | .App-nav-title {
6 | display: none;
7 | }
8 |
9 | @media @desktop-up {
10 | .App-nav {
11 | background-color: @admin-nav-bg;
12 | }
13 |
14 | .ThemeBase-Header-title {
15 | margin: 0;
16 | height: 90px;
17 | margin-bottom: 30px;
18 | display: flex;
19 | justify-content: center;
20 | align-items: center;
21 | }
22 |
23 | // Overspecification is bad, but we do it because core overspecifies, forcing our hand
24 | .App-nav .AdminNav-Main {
25 | padding: 0 15px;
26 |
27 | .Dropdown-menu > li {
28 | margin-bottom: 3px;
29 | .asirem-border-radius(~'> a > .icon');
30 |
31 | &.active {
32 | > a {
33 | background-color: @primary-bg;
34 | color: @primary-color;
35 | }
36 | }
37 |
38 | &.active, &:hover {
39 | > a {
40 | > .icon {
41 | background-color: @admin-nav-bg;
42 | }
43 | }
44 | }
45 |
46 | > a {
47 | border-radius: 25px;
48 | padding: 8px !important;
49 | display: flex !important;
50 | align-items: center;
51 | transition: background-color 0.4s, color 0.4s;
52 | color: @admin-nav-color;
53 |
54 | > .icon {
55 | .asirem-icon-block(32px);
56 | font-size: 16px !important;
57 | margin: 0 !important;
58 | float: none;
59 | transition: background-color 0.4s;
60 | }
61 |
62 | > .Button-label {
63 | padding-left: 14px;;
64 | }
65 | }
66 |
67 | &.item-extensions {
68 | .Button-label {
69 | width: calc(~"100% - 32px");
70 | display: block;
71 | }
72 | }
73 | }
74 | }
75 |
76 | .Asirem-adminWaves {
77 | display: block;
78 | position: absolute;
79 | transform: scaleY(1) scaleX(.15) rotate(2deg);
80 | top: 0;
81 | left: -8.3rem;
82 | z-index: -1;
83 | max-height: 100%;
84 | fill: @admin-nav-bg;
85 | }
86 | }
87 |
88 | @media @tablet {
89 | .App {
90 | &-header {
91 | padding-left: 0;
92 | padding-right: 0;
93 | }
94 | }
95 |
96 | .container {
97 | width: unset;
98 | max-width: 100%;
99 | }
100 | }
101 |
102 | &::-webkit-scrollbar {
103 | width: 8px;
104 | }
105 | /*& {
106 | scrollbar-width: thin;
107 | scrollbar-color: @control-color @control-color;
108 | }*/
109 | &::-webkit-scrollbar-track {
110 | background: transparent;
111 | }
112 | &::-webkit-scrollbar-thumb {
113 | background-color: @control-color;
114 | border-radius: 6px;
115 | }
116 |
--------------------------------------------------------------------------------
/less/admin/ExtensionsPage.less:
--------------------------------------------------------------------------------
1 | .ThemeBase-ExtensionCategory {
2 | .asirem-block();
3 | padding: 15px;
4 | }
5 |
6 | .ThemeBase-Extension {
7 | --icon-size: 48px;
8 | padding: 6px 18px 6px 6px;
9 | border-radius: @border-radius;
10 | transition: border-color 0.4s, background-color 0.4s;
11 | color: inherit;
12 | .asirem-border-radius(~'.ExtensionIcon');
13 |
14 | &:hover {
15 | text-decoration: none;
16 | background-color: @control-bg;
17 | }
18 |
19 | &-icon {
20 | .ExtensionIcon {
21 | width: var(--icon-size);
22 | height: var(--icon-size);
23 | font-size: calc(~"var(--icon-size) / 2.5");
24 | box-shadow: @asirem-box-shadow;
25 | }
26 | }
27 |
28 | &-title {
29 | &-value {
30 | font-size: 14px;
31 | font-weight: 600;
32 | }
33 | }
34 |
35 | &-details {
36 | font-size: 12px;
37 | opacity: 0.6;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/less/admin/FormGroup.less:
--------------------------------------------------------------------------------
1 | .AdminPage:not(.AppearancePage) .Form-group,
2 | .AppearancePage fieldset {
3 | background: @body-bg;
4 | padding: 25px;
5 | border-radius: 14px;
6 |
7 | .Button {
8 | --control-bg: @control-bg;
9 |
10 | &--primary {
11 | --control-bg: @primary-color;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/less/admin/GroupPermissions.less:
--------------------------------------------------------------------------------
1 | .GroupPermissions {
2 | --padding: 15px;
3 | margin-top: 10px;
4 |
5 | &-header {
6 | color: @control-color;
7 | background-color: @body-bg;
8 | font-weight: 600;
9 | margin: 0;
10 | padding: var(--padding);
11 | }
12 |
13 | &-permission {
14 | display: flex;
15 | align-items: center;
16 | padding: 0 var(--padding);
17 | background-color: @body-bg;
18 | min-height: 42px;
19 |
20 | &-label {
21 | &-icon {
22 | color: @control-color;
23 | }
24 | }
25 |
26 | &-control {
27 | margin-left: auto;
28 | }
29 | }
30 |
31 | &-permissionGroup-items {
32 | display: flex;
33 | flex-direction: column;
34 | gap: 1px;
35 | }
36 |
37 | &-permissionGroup-label {
38 | color: @control-color;
39 | background-color: @control-bg-light;
40 | padding: 5px var(--padding);
41 | }
42 |
43 | .Checkbox {
44 | &-display {
45 | float: none;
46 | }
47 | }
48 | }
49 |
50 |
51 | .IconicText {
52 | display: flex;
53 | align-items: center;
54 | gap: 4px;
55 |
56 | &-icon {
57 | width: 22px;
58 | text-align: center;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/less/admin/PermissionsPage.less:
--------------------------------------------------------------------------------
1 | .PermissionsPage {
2 | &-groups {
3 | padding: 0;
4 | overflow: visible;
5 | }
6 |
7 | &-permissions {
8 | overflow: unset;
9 | padding-bottom: 30px;
10 |
11 | &-overflow {
12 | overflow: auto;
13 | padding-bottom: 200px;
14 | max-height: 85vh;
15 | }
16 | }
17 | }
18 |
19 | .Group {
20 | padding: 15px 15px 10px;
21 | width: unset;
22 | min-width: 90px;
23 | margin-right: 5px;
24 | .asirem-border-radius(~'.Group-icon');
25 |
26 | &, .Button-label {
27 | display: inline-flex;
28 | flex-direction: column;
29 | align-items: center;
30 | justify-content: center;
31 | }
32 |
33 | &--add {
34 | margin: 0;
35 | }
36 |
37 | &--add &-icon {
38 | margin: 0;
39 | box-shadow: none;
40 | background: transparent;
41 | }
42 |
43 | &-icon {
44 | .asirem-icon-block(60px, false);
45 | background-image: linear-gradient(-45deg, rgba(0, 0, 0, 19%), transparent);
46 | }
47 |
48 | &-icon .icon, & &-icon {
49 | font-size: 18px;
50 | }
51 |
52 | .icon.fa-caret-down {
53 | display: none;
54 | }
55 | }
56 |
57 | .PermissionGrid td, .PermissionGrid th {
58 | background: @body-bg;
59 | padding: 10px 15px;
60 | }
61 |
62 | .PermissionGrid {
63 | border-collapse: separate;
64 | border-spacing: 0 1px;
65 | min-width: 100%;
66 |
67 | &-section {
68 | td, th {
69 | background: @control-bg-light;
70 | }
71 | }
72 |
73 | td {
74 | padding: 0;
75 |
76 | .Button {
77 | padding: 10px 15px;
78 | height: auto;
79 | border: 0;
80 | margin: 0;
81 | display: flex;
82 | align-items: center;
83 | border-radius: 0;
84 | }
85 | }
86 |
87 | tbody .Button .Badge {
88 | margin: 0;
89 | }
90 |
91 | tbody .Button-label {
92 | display: inline-flex;
93 | align-items: center;
94 | gap: 3px;
95 | }
96 |
97 | thead {
98 | td:first-child {
99 | border-top-left-radius: 14px;
100 | }
101 | th:last-child {
102 | border-top-right-radius: 14px;
103 | }
104 | }
105 | tbody:last-child {
106 | tr:last-child {
107 | th:first-child {
108 | border-bottom-left-radius: 14px;
109 | }
110 | td:last-child {
111 | border-bottom-right-radius: 14px;
112 | }
113 | }
114 | }
115 |
116 | thead td:first-child, tbody th {
117 | position: sticky;
118 | left: 0;
119 | z-index: 2;
120 | background-image: linear-gradient(to left, rgba(0,0,0,.1), transparent 3%);
121 | }
122 |
123 | thead th {
124 | position: sticky;
125 | top: 0;
126 | z-index: 1;
127 | background-image: linear-gradient(to top, rgba(0,0,0,.07), transparent 13%);
128 | }
129 | }
130 |
131 | .Asirem-PermissionsPage-scopes {
132 | display: flex;
133 | align-items: flex-end;
134 | border-radius: 14px;
135 | margin-bottom: 15px;
136 |
137 | &-controls {
138 | margin-left: auto;
139 |
140 | .Button {
141 | margin-left: 4px;
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/less/admin/StatisticsWidget.less:
--------------------------------------------------------------------------------
1 | .App {
2 | .StatisticsWidget {
3 | background: transparent;
4 | padding: 0;
5 |
6 | &-table {
7 | display: flex;
8 | flex-direction: column;
9 | flex-wrap: wrap;
10 | margin: 0;
11 | }
12 |
13 | &-labels {
14 | margin-right: auto;
15 | }
16 |
17 | &-entity, &-chart {
18 | .asirem-block();
19 | }
20 |
21 | &-entity {
22 | margin-right: 8px;
23 | position: relative;
24 | overflow: hidden;
25 | z-index: 0;
26 |
27 | &:last-of-type {
28 | margin-right: 0;
29 | }
30 |
31 | &.active {
32 | border: 0;
33 | padding-top: 15px;
34 | background-color: @primary-color;
35 | color: @body-bg;
36 |
37 | .StatisticsWidget {
38 | &-heading {
39 | color: lighten(@primary-color, 30);
40 | }
41 |
42 | &-change {
43 | background-color: mix(@control-bg, @primary-color, 60);
44 | }
45 | }
46 | }
47 |
48 | &::after {
49 | font-family: 'Font Awesome 5 Free';
50 | font-weight: 600;
51 | position: absolute;
52 | bottom: -38px;
53 | right: -12px;
54 | font-size: 5rem;
55 | color: @control-bg;
56 | transform: rotate(-21deg);
57 | z-index: -1;
58 | opacity: 0.6;
59 | }
60 |
61 | &:first-of-type::after {
62 | content: "\f500";
63 | }
64 | &:nth-of-type(2)::after {
65 | content: "\f27a";
66 | bottom: -20px;
67 | right: -2px;
68 | font-size: 4rem;
69 | }
70 | &:last-of-type::after {
71 | content: "\f086";
72 | bottom: -26px;
73 | right: 0;
74 | font-size: 4rem;
75 | }
76 | }
77 |
78 | &-chart {
79 | width: 100%;
80 | margin: 8px 0 0 0;
81 |
82 | .chart-container .axis line, .chart-container .chart-label line {
83 | stroke: @control-bg !important;
84 | }
85 | }
86 |
87 | &-change {
88 | font-size: 85%;
89 | font-weight: 600;
90 | display: inline-block;
91 | padding: 0.1em 0.5em;
92 | border-radius: @border-radius;
93 | text-transform: none;
94 | font-size: 11px;
95 | vertical-align: middle;
96 | background-color: @control-bg;
97 |
98 | &--up {
99 | color: #00a502;
100 | }
101 | &--down {
102 | color: #d0011b;
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/less/admin/StatusWidget.less:
--------------------------------------------------------------------------------
1 | .ThemeBase-StatusWidget {
2 | padding: 0;
3 | background-color: transparent;
4 |
5 | > ul {
6 | > li {
7 | padding: 8px;
8 | background-color: @body-bg;
9 | border-radius: @border-radius;
10 | .asirem-border-radius(~'.ThemeBase-StatusWidget-icon');
11 | width: 25%;
12 | }
13 | }
14 |
15 | @media @phone {
16 | > ul {
17 | flex-wrap: wrap;
18 |
19 | > li {
20 | width: calc(~"100% / 2 - 8px");
21 | flex-grow: 0;
22 | margin-bottom: 8px;
23 | }
24 | }
25 | }
26 |
27 | &-icon {
28 | .asirem-icon-block(45px);
29 | font-size: 20px;
30 | }
31 |
32 | &-value {
33 | font-weight: 600;
34 | }
35 |
36 | &-control {
37 | --size: 35px;
38 | font-size: 18px;
39 | width: var(--size);
40 | height: var(--size);
41 |
42 | &.Button {
43 | --control-color: @alert-info-bg;
44 | --control-bg: @alert-info-color;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/less/admin/UserListPage.less:
--------------------------------------------------------------------------------
1 | .UserListPage-grid {
2 | grid-gap: 1px 0;
3 | width: -webkit-fit-content;
4 | width: fit-content;
5 |
6 | &-header,
7 | // @todo rowItem
8 | &-rowItem {
9 | background: @body-bg;
10 | padding: 10px 15px;
11 | color: @control-color;
12 | }
13 |
14 | &-rowItem {
15 | &[data-column-name="avatar"], &[data-column-name="profileLink"] {
16 | padding: 0;
17 | }
18 |
19 | &[data-column-name="emailAddress"] {
20 | padding: 0 0 0 15px;
21 | }
22 |
23 | &[data-column-name="username"] {
24 | a {
25 | color: @control-color;
26 | }
27 | }
28 | }
29 |
30 | &-header {
31 | border-bottom: none;
32 | color: @control-color;
33 |
34 | &:nth-child(2) {
35 | padding: 10px 0;
36 | text-align: center;
37 | }
38 | }
39 |
40 | &-avatar {
41 | --size: 32px;
42 | width: var(--size);
43 | height: var(--size);
44 | line-height: 0;
45 | display: flex;
46 | align-items: center;
47 | justify-content: center;
48 | font-size: 17px;
49 | }
50 | }
51 |
52 | .UserList {
53 | &-email {
54 | height: 100%;
55 | align-items: center;
56 |
57 | &[data-email-shown="false"] {
58 | .UserList-emailAddress {
59 | color: transparent;
60 | background-color: @control-bg;
61 | filter: none;
62 | }
63 | }
64 |
65 | &Address {
66 | transition: color .2s ease-out, background-color .2s ease-out;
67 | }
68 | }
69 | }
70 |
71 | .UserList-emailIconBtn,
72 | .UserList-profileLinkBtn {
73 | border-radius: 0;
74 | height: 100%;
75 | align-items: center;
76 | display: flex;
77 | justify-content: center;
78 | }
79 |
80 | .UserListPage-stat {
81 | padding: 15px;
82 | background: @body-bg;
83 | color: @control-color;
84 | border-radius: 14px;
85 | min-width: 15%;
86 |
87 | &-container {
88 | margin-bottom: 10px;
89 | display: flex;
90 | }
91 |
92 | &-value {
93 | font-size: 1.4rem;
94 | }
95 |
96 | &-key {
97 | opacity: 0.7;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/less/admin/Widget.less:
--------------------------------------------------------------------------------
1 | .Widget {
2 | background-color: @body-bg;
3 |
4 | .Button {
5 | background: var(--control-bg);
6 | color: var(--control-color);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/less/admin/sideNav.less:
--------------------------------------------------------------------------------
1 | .sideNavOffset {
2 | background-color: @control-bg;
3 |
4 | .Button {
5 | --control-bg: @body-bg;
6 |
7 | &--primary {
8 | --control-bg: @primary-color;
9 | }
10 | }
11 | .Button--dashed {
12 | .Button--dashed();
13 | }
14 |
15 | }
16 |
17 | .Button {
18 | background-color: var(--control-bg);
19 | color: var(--control-color);
20 |
21 | &--primary {
22 | --control-color: @control-bg;
23 | --control-bg: @primary-color;
24 | }
25 | }
26 |
27 | @media @tablet {
28 | .sideNav {
29 | padding-left: 15px;
30 | padding-right: 15px;
31 | }
32 | }
33 |
34 | @media @tablet, @phone {
35 | .sideNavOffset {
36 | padding-top: 15px;
37 | }
38 | }
39 |
40 | @media @desktop-up {
41 | .sideNav {
42 | margin-right: 0;
43 | height: 100vh;
44 | }
45 |
46 | .sideNavOffset {
47 | position: relative;
48 | padding-left: 1rem;
49 | z-index: 0;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/less/admin/variables.less:
--------------------------------------------------------------------------------
1 | @alert-info-bg: #aecbff;
2 | @alert-info-color: #006dad;
3 |
4 | :root {
5 | --control-bg: @control-bg;
6 | --control-color: @control-color;
7 | }
8 |
--------------------------------------------------------------------------------
/less/common/App.less:
--------------------------------------------------------------------------------
1 | .App {
2 | overflow-x: visible;
3 | }
4 |
--------------------------------------------------------------------------------
/less/common/Button.less:
--------------------------------------------------------------------------------
1 | .Button {
2 | border: 1px solid transparent;
3 | transition: background-color 0.4s;
4 | }
5 |
6 | .Button--dashed {
7 | border: 1px dashed;
8 | --control-bg: transparent;
9 | }
10 |
--------------------------------------------------------------------------------
/less/common/Dropdown.less:
--------------------------------------------------------------------------------
1 | .Dropdown-menu {
2 | padding: 8px;
3 |
4 | > li {
5 | > a, > button, > span {
6 | border-radius: @border-radius;
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/less/common/common.less:
--------------------------------------------------------------------------------
1 | @import "variables.less";
2 | @import "mixins/asirem-block.less";
3 | @import "mixins/asirem-icon-block.less";
4 | @import "mixins/asirem-border-radius.less";
5 |
6 | @import "scaffolding.less";
7 | @import "App.less";
8 | @import "Button.less";
9 | @import "Dropdown.less";
10 |
--------------------------------------------------------------------------------
/less/common/mixins/asirem-block.less:
--------------------------------------------------------------------------------
1 | .asirem-block(@bg: @body-bg) {
2 | border-radius: @border-radius;
3 | background: @bg;
4 | }
5 |
--------------------------------------------------------------------------------
/less/common/mixins/asirem-border-radius.less:
--------------------------------------------------------------------------------
1 | .asirem-border-radius(@elem) {
2 | &:nth-child(2n) {
3 | @{elem} {
4 | border-radius: @asirem-border-radius-a;
5 | }
6 | }
7 |
8 | &:nth-child(2n+1) {
9 | @{elem} {
10 | border-radius: @asirem-border-radius-b;
11 | }
12 | }
13 |
14 | &:nth-child(3n) {
15 | @{elem} {
16 | border-radius: @asirem-border-radius-c;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/less/common/mixins/asirem-icon-block.less:
--------------------------------------------------------------------------------
1 | .asirem-icon-block(@size: 30px, @bg: @control-bg, @br: @border-radius) {
2 | --size: @size;
3 | width: var(--size);
4 | height: var(--size);
5 | display: flex;
6 | align-items: center;
7 | justify-content: center;
8 | border-radius: @br;
9 | box-shadow: @asirem-box-shadow;
10 |
11 | & when (isstring(@bg)) {
12 | background-color: @bg;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/less/common/scaffolding.less:
--------------------------------------------------------------------------------
1 | body {
2 | overflow-x: hidden;
3 | }
--------------------------------------------------------------------------------
/less/common/variables.less:
--------------------------------------------------------------------------------
1 | @border-radius: 14px;
2 | @asirem-border-radius-a: ~"43% 57% 68% 32% / 33% 50% 50% 67%";
3 | @asirem-border-radius-b: ~"42% 58% 33% 67% / 57% 49% 51% 43%";
4 | @asirem-border-radius-c: ~"58% 42% 68% 32% / 48% 73% 27% 52%";
5 |
6 | .define-asirem-colors(@config-dark-mode);
7 | .define-asirem-colors(false) {
8 | @primary-bg: lighten(@primary-color, 40);
9 | @control-bg-light: lighten(@control-bg, 3);
10 |
11 | @unapproved-discussion-bg: #fdddc2;
12 | }
13 | .define-asirem-colors(true) {
14 | @primary-bg: mix(@body-bg, @primary-color, 90);
15 | @control-bg-light: darken(@control-bg, 6);
16 |
17 | @unapproved-discussion-bg: #1f1914;
18 | }
19 |
20 | .define-asirem-header(@config-colored-header);
21 | .define-asirem-header(false) {
22 | @admin-nav-bg: @body-bg;
23 | @admin-nav-color: @control-color;
24 | }
25 | .define-asirem-header(true) {
26 | @admin-nav-bg: @body-bg;
27 | @admin-nav-color: @control-color;
28 | }
29 |
30 | @asirem-box-shadow: rgba(0, 0, 0, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
31 |
32 | @screen-desktop-hd: 1200px;
33 |
--------------------------------------------------------------------------------
/less/forum.less:
--------------------------------------------------------------------------------
1 | @import "common/common.less";
2 |
3 | .define-colors(false) {
4 | @primary-color: @config-primary-color;
5 | @secondary-color: @config-secondary-color;
6 |
7 | @body-bg: #fff;
8 | @text-color: #111;
9 | @link-color: saturate(@primary-color, 10%);
10 | @heading-color: @text-color;
11 | @muted-color: hsl(@secondary-hue, min(20%, @secondary-sat), 50%);
12 | @muted-more-color: #aaa;
13 | @shadow-color: rgba(0, 0, 0, 0.35);
14 |
15 | @control-bg: hsl(@secondary-hue, min(50%, @secondary-sat), 93%);
16 | @control-color: @muted-color;
17 | @control-danger-bg: #fdd;
18 | @control-danger-color: #d66;
19 |
20 | @overlay-bg: fade(@secondary-color, 90%);
21 |
22 | @code-bg: darken(@body-bg, 3%);
23 | @code-color: lighten(@text-color, 30%);
24 | }
25 | .define-colors(true) {
26 | @primary-color: @config-primary-color;
27 | @secondary-color: @config-secondary-color;
28 |
29 | @body-bg: hsl(@secondary-hue, min(20%, @secondary-sat), 13%);
30 | @text-color: #ddd;
31 | @link-color: saturate(@primary-color, 10%);
32 | @heading-color: @text-color;
33 | @muted-color: hsl(@secondary-hue, min(15%, @secondary-sat), 50%);
34 | @muted-more-color: hsl(@secondary-hue, min(10%, @secondary-sat), 40%);
35 | @shadow-color: rgba(0, 0, 0, 0.5);
36 |
37 | @control-bg: hsl(@secondary-hue, min(20%, @secondary-sat), 10%);
38 | @control-color: @muted-color;
39 | @control-danger-bg: #411;
40 | @control-danger-color: #a88;
41 |
42 | @overlay-bg: fade(darken(@body-bg, 5%), 90%);
43 |
44 | @code-bg: darken(@body-bg, 3%);
45 | @code-color: #fff;
46 | }
47 |
48 | @import "forum/App.less";
49 | @import "forum/DiscussionList.less";
50 | @import "forum/Hero.less";
51 | @import "forum/Post.less";
52 | @import "forum/TagsPage.less";
53 | @import "forum/sideNav.less";
54 | @import "forum/Footer.less";
55 |
--------------------------------------------------------------------------------
/less/forum/App.less:
--------------------------------------------------------------------------------
1 | .App {
2 | padding-bottom: 0;
3 |
4 | &-footer {
5 | &:empty {
6 | display: none;
7 | }
8 | }
9 | }
10 |
11 | @media @tablet-up {
12 | .App {
13 | display: flex;
14 | flex-direction: column;
15 |
16 | &-content {
17 | flex-grow: 1;
18 | border-top: 0;
19 | }
20 | }
21 |
22 | .container {
23 | padding: 0;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/less/forum/DiscussionList.less:
--------------------------------------------------------------------------------
1 | .DiscussionList {
2 | @media @phone {
3 | margin: 0;
4 | }
5 |
6 | &-discussions > li {
7 | .asirem-border-radius(~'.Avatar');
8 | }
9 |
10 | &Item {
11 | background: @control-bg;
12 | margin: 0 0 4px;
13 | padding: 0;
14 | border-radius: @border-radius;
15 |
16 | &-controls .Dropdown-toggle {
17 | border-radius: @border-radius;
18 | }
19 |
20 | &-info {
21 | font-size: 12px;
22 | }
23 |
24 | &--unapproved {
25 | background-color: @unapproved-discussion-bg;
26 |
27 | .DiscussionListItem-content {
28 | opacity: 1;
29 | }
30 | }
31 |
32 | &-badges {
33 | .Badge {
34 | --size: 20px;
35 | box-shadow: -1px 1px 2px #00000038;
36 | width: var(--size);
37 | height: var(--size);
38 | line-height: var(--size);
39 |
40 | &-icon {
41 | font-size: 10px;
42 | }
43 | }
44 | }
45 |
46 | &--unread &-title::after {
47 | --size: 8px;
48 | content: "";
49 | height: var(--size);
50 | width: var(--size);
51 | line-height: var(--size);
52 | background: @primary-color;
53 | color: #fff;
54 | border-radius: 100%;
55 | font-size: 10px;
56 | display: inline-flex;
57 | align-items: center;
58 | justify-content: center;
59 | vertical-align: text-top;
60 | margin-top: 6px;
61 | margin-left: 5px;
62 | }
63 | }
64 |
65 | &-loadMore {
66 | margin-top: 5px;
67 |
68 | .Button {
69 | width: 100%;
70 | height: 100%;
71 | }
72 | }
73 | }
74 |
75 | .TagsLabel .TagLabel:not(.overspecifying__) {
76 | border-radius: @border-radius;
77 | margin-right: 5px;
78 | font-size: 11px;
79 | }
80 |
81 | .App {
82 | .IndexPage .DiscussionListItem,
83 | .UserPage .DiscussionListItem {
84 | &-info {
85 | > .item-tags {
86 | top: 0;
87 | right: 0;
88 | }
89 | }
90 | }
91 |
92 | .DiscussionListItem {
93 | &-info {
94 | display: flex;
95 | align-items: center;
96 | flex-wrap: wrap;
97 | }
98 |
99 | &-title {
100 | color: @text-color !important;
101 | }
102 |
103 | &-main {
104 | padding: 0;
105 | margin: 0;
106 | position: relative;
107 | }
108 |
109 | &-badges {
110 | margin: 0;
111 | width: auto;
112 | float: none;
113 | position: absolute;
114 | right: -7px;
115 | top: -4px;
116 | display: block;
117 | }
118 |
119 | &-author {
120 | float: none;
121 | margin: 0;
122 | display: block;
123 |
124 | &-container {
125 | margin-right: 15px;
126 | position: relative;
127 |
128 | > .tooltip {
129 | width: max-content;
130 | }
131 | }
132 |
133 | .Avatar {
134 | .asirem-icon-block(50px, @body-bg);
135 | }
136 | }
137 |
138 | &-content {
139 | display: flex;
140 | padding: 12px;
141 | }
142 |
143 | &-stats {
144 | margin-left: 10px;
145 | margin-right: 15px;
146 | }
147 |
148 | &-count {
149 | margin: 0;
150 | padding: 0;
151 | float: none;
152 | display: block;
153 |
154 | &::before {
155 | margin: 0 5px 0 0;
156 | padding: 0;
157 | float: none;
158 | }
159 | }
160 | }
161 | }
162 |
163 | @media @phone {
164 | .DiscussionListItem {
165 | .unread &-count {
166 | padding: 0 4px;
167 | }
168 | }
169 | }
170 |
171 |
172 | @media @tablet-up {
173 | .DiscussionListItem {
174 | .item-excerpt {
175 | margin-right: 0;
176 | width: 100%;
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/less/forum/Footer.less:
--------------------------------------------------------------------------------
1 | .ThemeBaseFooter {
2 | position: relative;
3 | }
4 |
5 | .Asirem-footerWaves {
6 | display: none;
7 | }
8 |
9 | @media @tablet-up {
10 | .Asirem-footerWaves {
11 | display: block;
12 | position: absolute;
13 | top: -5.1rem;
14 | left: 50%;
15 | z-index: -1;
16 | transform: scaleY(.35) translateX(-50%) translateY(-21rem);
17 | fill: @control-bg;
18 | max-width: 100%;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/less/forum/Hero.less:
--------------------------------------------------------------------------------
1 | .Hero {
2 | z-index: 0;
3 | margin: 15px auto 0;
4 | max-width: @screen-desktop-hd;
5 | border-radius: @border-radius;
6 |
7 | @media @desktop {
8 | max-width: @screen-desktop;
9 | }
10 |
11 | @media @tablet {
12 | max-width: @screen-tablet;
13 | }
14 |
15 | @media @phone {
16 | max-width: calc(~"100% - 30px");
17 | margin-bottom: 15px;
18 | }
19 |
20 | &--banner {
21 | --banner-position: center;
22 | background-image: var(--banner-url);
23 | background-repeat: no-repeat;
24 | background-size: 100%;
25 | background-position: var(--banner-position);
26 | color: #fff;
27 | }
28 |
29 | .container {
30 | padding: 30px;
31 | }
32 |
33 | + .container {
34 | position: relative;
35 | }
36 | }
37 |
38 | .darkenBackground {
39 | border-radius: @border-radius;
40 | }
41 |
--------------------------------------------------------------------------------
/less/forum/Post.less:
--------------------------------------------------------------------------------
1 | .Post {
2 | margin: 0 0 15px 0;
3 | background: @control-bg;
4 | border-radius: 8px;
5 | }
6 |
7 | .PostStream-item {
8 | &:not(:last-child) {
9 | border-bottom: none;
10 | }
11 | }
12 |
13 | .ReplyPlaceholder {
14 | margin-top: 30px;
15 | }
16 |
17 | .EventPost {
18 | background-color: transparent;
19 |
20 | .Post-footer {
21 | display: none;
22 | }
23 | }
24 |
25 | .EventPost .Post-actions, .Post--hidden:not(.revealContent) .Post-actions {
26 | margin-top: -30px;
27 | }
28 |
--------------------------------------------------------------------------------
/less/forum/TagsPage.less:
--------------------------------------------------------------------------------
1 | .Asirem-TagTiles {
2 | padding: 0;
3 | margin: 0;
4 | list-style: none;
5 | display: grid;
6 | grid-template-columns: repeat(2, 50%);
7 | gap: 8px;
8 |
9 | @media @phone {
10 | grid-template-columns: 100%;
11 | }
12 | }
13 |
14 | .Asirem-TagTile {
15 | --tag-bg: @control-bg;
16 | --tag-color: @control-color;
17 | .asirem-border-radius(~".Asirem-TagTile-icon .icon");
18 | display: flex;
19 |
20 | &.colored {
21 | --tag-color: @body-bg;
22 | }
23 |
24 | &:not(.colored) &-icon .icon {
25 | color: var(--tag-color);
26 | }
27 |
28 | &-content {
29 | display: flex;
30 | flex-direction: column;
31 | }
32 |
33 | &-name {
34 | margin: 0;
35 | }
36 |
37 | &-info {
38 | flex-grow: 1;
39 | display: grid;
40 | grid-template-columns: 88px 1fr;
41 | color: var(--tag-color);
42 | background: var(--tag-bg);
43 | border-radius: @border-radius;
44 | padding: 16px 16px 16px 0;
45 |
46 | &:hover {
47 | text-decoration: none;
48 | }
49 | }
50 |
51 | &-icon {
52 | display: flex;
53 | justify-content: center;
54 | align-items: flex-start;
55 |
56 | .icon {
57 | .asirem-icon-block(50px);
58 | font-size: 28px;
59 | background-color: @body-bg;
60 | color: var(--tag-bg);
61 | }
62 | }
63 |
64 | &-lastPostedDiscussion {
65 | margin-top: auto;
66 | font-weight: bold;
67 | color: var(--tag-color);
68 | opacity: 0.6;
69 | display: grid;
70 | grid-template-columns: auto auto;
71 |
72 | &-title {
73 | overflow: hidden;
74 | text-overflow: ellipsis;
75 | white-space: nowrap;
76 | display: block;
77 | }
78 |
79 | time {
80 | margin-left: auto;
81 | font-style: italic;
82 | }
83 | }
84 |
85 | &-children + &-lastPostedDiscussion {
86 | padding-top: 16px;
87 | }
88 | }
89 |
90 | .Asirem-TagCloud {
91 | text-align: center;
92 | margin: 16px 0;
93 | }
94 |
--------------------------------------------------------------------------------
/less/forum/sideNav.less:
--------------------------------------------------------------------------------
1 | .sideNavOffset, .sideNav > ul {
2 | margin-top: 0;
3 | }
4 |
5 | .AfruxWidgets-sideNavAlt {
6 | padding-top: 0;
7 | }
8 |
9 | @media @desktop-up {
10 | .sideNav {
11 | .Dropdown--select .Dropdown-menu {
12 | > li {
13 | margin: 2px 0;
14 |
15 | > a {
16 | --icon-size: 30px;
17 | padding: 6px;
18 | display: grid;
19 | grid-template-columns: var(--icon-size) 1fr;
20 | align-items: center;
21 | border-radius: 25px;
22 | transition: background-color 0.4s, color 0.4s;
23 | font-size: 14px;
24 | grid-gap: 10px;
25 |
26 | .Button-icon {
27 | .asirem-icon-block(var(--icon-size));
28 | float: none;
29 | margin: 0;
30 | border-radius: 100%;
31 | transition: background-color 0.4s;
32 | }
33 |
34 | .Button-label {
35 | overflow: hidden;
36 | text-overflow: ellipsis;
37 | }
38 | }
39 |
40 | &.active {
41 | > a {
42 | background-color: @primary-bg;
43 |
44 | .Button-icon {
45 | background-color: @body-bg;
46 | }
47 | }
48 | }
49 |
50 | &:hover {
51 | > a {
52 | background-color: @control-bg;
53 | color: @text-color;
54 |
55 | .Button-icon {
56 | background-color: @body-bg;
57 | }
58 | }
59 | }
60 | }
61 | > li[class^="item-tag"]:not(.item-tags) a {
62 | font-size: 13px;
63 |
64 | .icon {
65 | --size: 24px;
66 | }
67 | }
68 | > .Dropdown-separator {
69 | margin: 12px 0;
70 | }
71 | }
72 | }
73 |
74 | .sideNavContainer {
75 | margin-top: 30px;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/locale/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/afrux/asirem/31e5baa5d009a44b7c74d6e68aafcd5f9822654c/locale/.gitkeep
--------------------------------------------------------------------------------
/views/frontend/admin.blade.php:
--------------------------------------------------------------------------------
1 | @extends('afrux-theme-base::frontend.admin')
2 |
3 | @section('separator')
4 |
5 |
6 |
7 | @endsection
8 |
--------------------------------------------------------------------------------