Initial commit

This commit is contained in:
Neill Cox 2024-05-04 19:45:07 +10:00
commit 03761d2e2f
17 changed files with 5662 additions and 0 deletions

16
.eslintrc.json Normal file
View file

@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"standard"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"indent": ["error", 4]
}
}

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules/*
.vscode/
mkzip.sh
*.zip

20
languages/en.json Normal file
View file

@ -0,0 +1,20 @@
{
"tokenActionHud": {
"template": {
"armor": "Armor",
"containers": "Containers",
"consumables": "Consumables",
"equipment": "Equipment",
"inventory": "Inventory",
"item": "Item",
"weapons": "Weapons",
"treasure": "Treasure",
"settings": {
"displayUnequipped": {
"hint": "Choose whether to display unequipped items on the HUD",
"name": "Display Unequipped"
}
}
}
}
}

72
module.json Normal file
View file

@ -0,0 +1,72 @@
{
"id": "token-action-hud-gurps",
"title": "Token Action HUD for GURPS",
"description": "Token Action HUD is a repositionable HUD of actions for a selected token",
"authors": [
{
"name": "Neill Cox",
"url": "https://github.com/neillc"
}
],
"url": "This is auto replaced",
"flags": {},
"version": "0.0.37",
"compatibility": {
"minimum": "11",
"verified": "11.351"
},
"esmodules": [
"./scripts/init.js"
],
"scripts": [
],
"styles": [
"./styles/tah-template-style.css"
],
"languages": [
{
"lang": "en",
"name": "English",
"path": "languages/en.json"
}
],
"packs": [],
"relationships": {
"systems": [
{
"id": "gurps",
"type": "system",
"compatibility": [
{
"minimum": "0.16.10",
"verified": "01.16.10"
}
]
}
],
"requires": [
{
"id": "token-action-hud-core",
"type": "module",
"compatibility": [
{
"minimum": "1.5.0",
"maximum": "1.5",
"verified": "1.5.0"
}
]
}
],
"optional": [],
"flags": {
"optional": []
}
},
"socket": false,
"readme": "http://172.23.0.17:8000/module.json",
"manifest": "http://172.23.0.17:8000/module.json",
"download": "http://172.23.0.17:8000/fvtt-token-action-hud-gurps.zip",
"protected": false,
"coreTranslation": false,
"library": false
}

4462
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

16
package.json Normal file
View file

@ -0,0 +1,16 @@
{
"scripts": {
"build": "rollup -c",
"dev": "rollup -wcm"
},
"devDependencies": {
"@rollup/plugin-multi-entry": "^6.0.0",
"eslint": "^8.25.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.3.0",
"eslint-plugin-promise": "^6.1.0",
"rollup": "^2.56.3",
"rollup-plugin-terser": "^7.0.2"
}
}

51
readme.md Normal file
View file

@ -0,0 +1,51 @@
![Downloads](https://img.shields.io/github/downloads/Larkinabout/fvtt-token-action-hud-template/latest/module.zip?color=2b82fc&label=DOWNLOADS&style=for-the-badge) [![Forge Installs](https://img.shields.io/badge/dynamic/json?label=Forge%20Installs&query=package.installs&suffix=%25&url=https%3A%2F%2Fforge-vtt.com%2Fapi%2Fbazaar%2Fpackage%2Ftoken-action-hud-template&colorB=448d34&style=for-the-badge)](https://forge-vtt.com/bazaar#package=token-action-hud-template)
# Token Action HUD Template
Token Action HUD is a repositionable HUD of actions for a selected token.
![Token Action HUD](.github/readme/token-action-hud.gif)
# Features
- Make rolls directly from the HUD instead of opening your character sheet.
- Use items from the HUD or right-click an item to open its sheet.
- Move the HUD and choose to expand the menus up or down.
- Unlock the HUD to customise layout and groups per user, and actions per actor.
- Add your own macros, journal entries and roll table compendiums.
# Installation
## Method 1
1. On Foundry VTT's **Configuration and Setup** screen, go to **Add-on Modules**
2. Click **Install Module**
3. Search for **Token Action HUD Pathfinder 2**
4. Click **Install** next to the module listing
## Method 2
1. On Foundry VTT's **Configuration and Setup** screen, go to **Add-on Modules**
2. Click **Install Module**
3. In the Manifest URL field, paste: `https://github.com/Larkinabout/fvtt-token-action-hud-template/releases/latest/download/module.json`
4. Click **Install** next to the pasted Manifest URL
## Required Modules
**IMPORTANT** - Token Action HUD Template requires the [Token Action HUD Core](https://foundryvtt.com/packages/token-action-hud-core) module to be installed.
## Recommended Modules
Token Action HUD uses the [Color Picker](https://foundryvtt.com/packages/color-picker) library module for its color picker settings.
# Support
For a guide on using Token Action HUD, go to: [How to Use Token Action HUD](https://github.com/Larkinabout/fvtt-token-action-hud-core/wiki/How-to-Use-Token-Action-HUD)
For questions, feature requests or bug reports, please open an issue [here](https://github.com/Larkinabout/fvtt-token-action-hud-core/issues).
Pull requests are welcome. Please include a reason for the request or create an issue before starting one.
# Acknowledgements
Thank you to the Community Helpers on Foundry's Discord who provide tireless support for people seeking help with the HUD.
# License
This Foundry VTT module is licensed under a [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/) and this work is licensed under [Foundry Virtual Tabletop EULA - Limited License Agreement for module development](https://foundryvtt.com/article/license/).

23
rollup.config.js Normal file
View file

@ -0,0 +1,23 @@
import { terser } from 'rollup-plugin-terser'
import multi from '@rollup/plugin-multi-entry'
export default [
{
input: {
include: [
'scripts/*.js',
'scripts/*/*.js'
],
exclude: [
'scripts/token-action-hud-gurps.min.js']
},
output: {
format: 'esm',
file: 'scripts/token-action-hud-gurps.min.js'
},
plugins: [
terser({ keep_classnames: true, keep_fnames: true }),
multi()
]
}
]

131
scripts/action-handler.js Normal file
View file

@ -0,0 +1,131 @@
// System Module Imports
import { ACTION_TYPE, ITEM_TYPE } from './constants.js'
import { Utils } from './utils.js'
export let ActionHandler = null
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
/**
* Extends Token Action HUD Core's ActionHandler class and builds system-defined actions for the HUD
*/
ActionHandler = class ActionHandler extends coreModule.api.ActionHandler {
/**
* Build system actions
* Called by Token Action HUD Core
* @override
* @param {array} groupIds
*/a
async buildSystemActions (groupIds) {
// Set actor and token variables
this.actors = (!this.actor) ? this._getActors() : [this.actor]
this.actorType = this.actor?.type
// Settings
this.displayUnequipped = Utils.getSetting('displayUnequipped')
// Set items variable
if (this.actor) {
let items = this.actor.items
items = coreModule.api.Utils.sortItemsByName(items)
this.items = items
}
if (this.actorType === 'character') {
debugger;
this.#buildCharacterActions()
} else if (!this.actor) {
this.#buildMultipleTokenActions()
}
}
/**
* Build character actions
* @private
*/
#buildCharacterActions () {
this._get_attributes({id: "attributes", type:"system"})
this.#buildInventory()
}
_get_attributes(parent) {
const macroType = "attributes";
let actions = [];
let attributes = Object.entries(this.actor.system.attributes)
attributes.forEach((a) => {
const key = a[0];
const value = a[1].value
// img
actions.push({
id: key,
name: coreModule.api.Utils.i18n(key),
description: coreModule.api.Utils.i18n('GURPS.Attributes'),
encodedValue: [macroType, key].join(this.delimiter),
})
});
this.addActions(actions, parent)
}
/**
* Build multiple token actions
* @private
* @returns {object}
*/
#buildMultipleTokenActions () {
}
/**
* Build inventory
* @private
*/
async #buildInventory () {
if (this.items.size === 0) return
const actionTypeId = 'item'
const inventoryMap = new Map()
for (const [itemId, itemData] of this.items) {
const type = itemData.type
const equipped = itemData.equipped
if (equipped || this.displayUnequipped) {
const typeMap = inventoryMap.get(type) ?? new Map()
typeMap.set(itemId, itemData)
inventoryMap.set(type, typeMap)
}
}
for (const [type, typeMap] of inventoryMap) {
const groupId = ITEM_TYPE[type]?.groupId
if (!groupId) continue
const groupData = { id: groupId, type: 'system' }
// Get actions
const actions = [...typeMap].map(([itemId, itemData]) => {
const id = itemId
const name = itemData.name
const actionTypeName = coreModule.api.Utils.i18n(ACTION_TYPE[actionTypeId])
const listName = `${actionTypeName ? `${actionTypeName}: ` : ''}${name}`
const encodedValue = [actionTypeId, id].join(this.delimiter)
return {
id,
name,
listName,
encodedValue
}
})
// TAH Core method to add actions to the action list
this.addActions(actions, groupData)
}
}
}
})

55
scripts/constants.js Normal file
View file

@ -0,0 +1,55 @@
/**
* Module-based constants
*/
export const MODULE = {
ID: 'token-action-hud-gurps'
}
/**
* Core module
*/
export const CORE_MODULE = {
ID: 'token-action-hud-core'
}
/**
* Core module version required by the system module
*/
export const REQUIRED_CORE_MODULE_VERSION = '1.5'
/**
* Action types
*/
export const ACTION_TYPE = {
item: 'tokenActionHud.template.item',
utility: 'tokenActionHud.utility'
}
/**
* Groups
*/
export const GROUP = {
attributes: { id: 'attributes', name: 'tokenActionHud.gurps.attributes', type: 'system' },
armor: { id: 'armor', name: 'tokenActionHud.template.armor', type: 'system' },
equipment: { id: 'equipment', name: 'tokenActionHud.template.equipment', type: 'system' },
consumables: { id: 'consumables', name: 'tokenActionHud.template.consumables', type: 'system' },
containers: { id: 'containers', name: 'tokenActionHud.template.containers', type: 'system' },
treasure: { id: 'treasure', name: 'tokenActionHud.template.treasure', type: 'system' },
weapons: { id: 'weapons', name: 'tokenActionHud.template.weapons', type: 'system' },
combat: { id: 'combat', name: 'tokenActionHud.combat', type: 'system' },
token: { id: 'token', name: 'tokenActionHud.token', type: 'system' },
utility: { id: 'utility', name: 'tokenActionHud.utility', type: 'system' }
}
/**
* Item types
*/
export const ITEM_TYPE = {
attributes: { groupId: 'attributes' },
armor: { groupId: 'armor' },
backpack: { groupId: 'containers' },
consumable: { groupId: 'consumables' },
equipment: { groupId: 'equipment' },
treasure: { groupId: 'treasure' },
weapon: { groupId: 'weapons' }
}

53
scripts/defaults.js Normal file
View file

@ -0,0 +1,53 @@
import { GROUP } from './constants.js'
/**
* Default layout and groups
*/
export let DEFAULTS = null
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
const groups = GROUP
Object.values(groups).forEach(group => {
group.name = coreModule.api.Utils.i18n(group.name)
group.listName = `Group: ${coreModule.api.Utils.i18n(group.listName ?? group.name)}`
})
const groupsArray = Object.values(groups)
debugger;
DEFAULTS = {
layout: [
{
nestId: 'inventory',
id: 'inventory',
name: coreModule.api.Utils.i18n('Template.Inventory'),
groups: [
{ ...groups.weapons, nestId: 'inventory_weapons' },
{ ...groups.armor, nestId: 'inventory_armor' },
{ ...groups.equipment, nestId: 'inventory_equipment' },
{ ...groups.consumables, nestId: 'inventory_consumables' },
{ ...groups.containers, nestId: 'inventory_containers' },
{ ...groups.treasure, nestId: 'inventory_treasure' }
]
},
{
nestId: 'utility',
id: 'utility',
name: coreModule.api.Utils.i18n('tokenActionHud.utility'),
groups: [
{ ...groups.combat, nestId: 'utility_combat' },
{ ...groups.token, nestId: 'utility_token' },
{ ...groups.rests, nestId: 'utility_rests' },
{ ...groups.utility, nestId: 'utility_utility' }
]
},
{
nestId: 'attributes',
id: 'attributes',
name: coreModule.api.Utils.i18n('GURPS.attributes'),
groups: [
{ ...groups.attributes, nestId: 'attributes_attributes' }
]
}
],
groups: groupsArray
}
})

14
scripts/init.js Normal file
View file

@ -0,0 +1,14 @@
import { SystemManager } from './system-manager.js'
import { MODULE, REQUIRED_CORE_MODULE_VERSION } from './constants.js'
Hooks.on('tokenActionHudCoreApiReady', async () => {
/**
* Return the SystemManager and requiredCoreModuleVersion to Token Action HUD Core
*/
const module = game.modules.get(MODULE.ID)
module.api = {
requiredCoreModuleVersion: REQUIRED_CORE_MODULE_VERSION,
SystemManager
}
Hooks.call('tokenActionHudSystemReady', module)
})

108
scripts/roll-handler.js Normal file
View file

@ -0,0 +1,108 @@
export let RollHandler = null
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
/**
* Extends Token Action HUD Core's RollHandler class and handles action events triggered when an action is clicked
*/
RollHandler = class RollHandler extends coreModule.api.RollHandler {
/**
* Handle action click
* Called by Token Action HUD Core when an action is left or right-clicked
* @override
* @param {object} event The event
* @param {string} encodedValue The encoded value
*/
async handleActionClick (event, encodedValue) {
const [actionTypeId, actionId] = encodedValue.split('|')
const renderable = ['item']
if (renderable.includes(actionTypeId) && this.isRenderItem()) {
return this.doRenderItem(this.actor, actionId)
}
const knownCharacters = ['character']
// If single actor is selected
if (this.actor) {
await this.#handleAction(event, this.actor, this.token, actionTypeId, actionId)
return
}
const controlledTokens = canvas.tokens.controlled
.filter((token) => knownCharacters.includes(token.actor?.type))
// If multiple actors are selected
for (const token of controlledTokens) {
const actor = token.actor
await this.#handleAction(event, actor, token, actionTypeId, actionId)
}
}
/**
* Handle action hover
* Called by Token Action HUD Core when an action is hovered on or off
* @override
* @param {object} event The event
* @param {string} encodedValue The encoded value
*/
async handleActionHover (event, encodedValue) {}
/**
* Handle group click
* Called by Token Action HUD Core when a group is right-clicked while the HUD is locked
* @override
* @param {object} event The event
* @param {object} group The group
*/
async handleGroupClick (event, group) {}
/**
* Handle action
* @private
* @param {object} event The event
* @param {object} actor The actor
* @param {object} token The token
* @param {string} actionTypeId The action type id
* @param {string} actionId The actionId
*/
async #handleAction (event, actor, token, actionTypeId, actionId) {
switch (actionTypeId) {
case 'item':
this.#handleItemAction(event, actor, actionId)
break
case 'utility':
this.#handleUtilityAction(token, actionId)
break
}
}
/**
* Handle item action
* @private
* @param {object} event The event
* @param {object} actor The actor
* @param {string} actionId The action id
*/
#handleItemAction (event, actor, actionId) {
const item = actor.items.get(actionId)
item.toChat(event)
}
/**
* Handle utility action
* @private
* @param {object} token The token
* @param {string} actionId The action id
*/
async #handleUtilityAction (token, actionId) {
switch (actionId) {
case 'endTurn':
if (game.combat?.current?.tokenId === token.id) {
await game.combat?.nextTurn()
}
break
}
}
}
})

21
scripts/settings.js Normal file
View file

@ -0,0 +1,21 @@
import { MODULE } from './constants.js'
/**
* Register module settings
* Called by Token Action HUD Core to register Token Action HUD system module settings
* @param {function} coreUpdate Token Action HUD Core update function
*/
export function register (coreUpdate) {
game.settings.register(MODULE.ID, 'displayUnequipped', {
name: game.i18n.localize('tokenActionHud.template.settings.displayUnequipped.name'),
hint: game.i18n.localize('tokenActionHud.template.settings.displayUnequipped.hint'
),
scope: 'client',
config: true,
type: Boolean,
default: true,
onChange: (value) => {
coreUpdate(value)
}
})
}

92
scripts/system-manager.js Normal file
View file

@ -0,0 +1,92 @@
// System Module Imports
import { ActionHandler } from './action-handler.js'
import { RollHandler as Core } from './roll-handler.js'
import { MODULE } from './constants.js'
import { DEFAULTS } from './defaults.js'
import * as systemSettings from './settings.js'
export let SystemManager = null
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
/**
* Extends Token Action HUD Core's SystemManager class
*/
SystemManager = class SystemManager extends coreModule.api.SystemManager {
/**
* Returns an instance of the ActionHandler to Token Action HUD Core
* Called by Token Action HUD Core
* @override
* @returns {class} The ActionHandler instance
*/
getActionHandler () {
return new ActionHandler()
}
/**
* Returns a list of roll handlers to Token Action HUD Core
* Used to populate the Roll Handler module setting choices
* Called by Token Action HUD Core
* @override
* @returns {object} The available roll handlers
*/
getAvailableRollHandlers () {
const coreTitle = 'Core Template'
const choices = { core: coreTitle }
return choices
}
/**
* Returns an instance of the RollHandler to Token Action HUD Core
* Called by Token Action HUD Core
* @override
* @param {string} rollHandlerId The roll handler ID
* @returns {class} The RollHandler instance
*/
getRollHandler (rollHandlerId) {
let rollHandler
switch (rollHandlerId) {
case 'core':
default:
rollHandler = new Core()
break
}
return rollHandler
}
/**
* Returns the default layout and groups to Token Action HUD Core
* Called by Token Action HUD Core
* @returns {object} The default layout and groups
*/
async registerDefaults () {
return DEFAULTS
}
/**
* Register Token Action HUD system module settings
* Called by Token Action HUD Core
* @override
* @param {function} coreUpdate The Token Action HUD Core update function
*/
registerSettings (coreUpdate) {
systemSettings.register(coreUpdate)
}
/**
* Returns styles to Token Action HUD Core
* Called by Token Action HUD Core
* @override
* @returns {object} The TAH system styles
*/
registerStyles () {
return {
template: {
class: 'tah-style-template-style', // The class to add to first DIV element
file: 'tah-template-style', // The file without the css extension
moduleId: MODULE.ID, // The module ID
name: 'Template Style' // The name to display in the Token Action HUD Core 'Style' module setting
}
}
}
}
})

40
scripts/utils.js Normal file
View file

@ -0,0 +1,40 @@
import { MODULE } from './constants.js'
export let Utils = null
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
/**
* Utility functions
*/
Utils = class Utils {
/**
* Get setting
* @param {string} key The key
* @param {string=null} defaultValue The default value
* @returns {string} The setting value
*/
static getSetting (key, defaultValue = null) {
let value = defaultValue ?? null
try {
value = game.settings.get(MODULE.ID, key)
} catch {
coreModule.api.Logger.debug(`Setting '${key}' not found`)
}
return value
}
/**
* Set setting
* @param {string} key The key
* @param {string} value The value
*/
static async setSetting (key, value) {
try {
value = await game.settings.set(MODULE.ID, key, value)
coreModule.api.Logger.debug(`Setting '${key}' set to '${value}'`)
} catch {
coreModule.api.Logger.debug(`Setting '${key}' not found`)
}
}
}
})

View file

@ -0,0 +1,484 @@
:root {
--tah-background-color: #00000000;
--tah-border-radius: 3px;
--tah-gap: 5px;
--tah-button-background-color: #000000b3;
--tah-button-border-color: none;
--tah-button-box-shadow: 0 2px 0 -1px #0c0c0c, 0 0 0 1px #060606,
0 0 5px #000000ff;
--tah-button-disabled-text-color: var(--tah-text-secondary-color);
--tah-button-text-color: var(--tah-text-primary-color);
--tah-button-height: 32px;
--tah-button-min-width: 32px;
--tah-button-hover-box-shadow: 0 2px 0 -1px var(--tah-text-tertiary-color),
0 0 0 1px red, 0 0 10px var(--tah-text-tertiary-color);
--tah-button-hover-text-color: #fff;
--tah-button-active-background-color: #3c0078bf;
--tah-button-active-box-shadow: 0 0 0 1px #9b8dff, inset 0 0 10px #9b8dff;
--tah-button-toggle-background-color: #000000b3;
--tah-button-toggle-hover-background-color: #3c0078bf;
--tah-button-toggle-hover-box-shadow: 0 0 0 1px #9b8dff, 0 0 10px #9b8dff;
--tah-text-disabled-color: var(--tah-text-secondary-color);
--tah-text-primary-color: #dddddd;
--tah-text-secondary-color: #dddddd80;
--tah-text-tertiary-color: #ff6400;
--tah-text-hover-primary-color: var(--tah-text-primary-color);
--tah-text-hover-secondary-color: var(--tah-text-secondary-color);
--tah-text-hover-tertiary-color: var(--tah-text-tertiary-color);
--tah-text-shadow: 1px 1px 1px rgb(0 0 0), 0px 1px 3px rgb(0 0 0);
}
#token-action-hud {
align-items: center;
border-radius: var(--tah-border-radius);
display: flex;
flex-direction: row;
height: auto;
left: 150px;
position: fixed;
top: 80px;
width: auto;
z-index: 100;
}
#token-action-hud [class*="icon-"] {
display: inline-block;
width: 100%;
}
#tah-character-name {
color: var(--tah-text-primary-color);
font-size: var(--font-size-16);
margin: 0;
padding: 0;
pointer-events: none;
position: absolute;
text-align: left;
text-shadow: var(--tah-text-shadow);
top: -22px !important;
}
.tah-hidden {
display: none !important;
}
#token-action-hud:hover #tah-collapse-hud,
#token-action-hud:hover > #tah-buttons {
display: flex;
}
#tah-collapse-hud,
#tah-buttons {
align-items: center;
display: none;
font-style: normal;
font-weight: normal;
height: var(--tah-button-height);
position: relative;
z-index: 1;
}
#tah-collapse-expand {
font-style: normal;
font-weight: normal;
left: -16px;
position: absolute;
}
#tah-collapse-hud,
#tah-buttons button {
background: transparent;
border: 0;
line-height: unset;
margin: 0;
padding: 0;
}
#tah-collapse-hud:hover,
#tah-collapse-hud:focus,
#tah-buttons button:hover,
#tah-buttons button:focus {
box-shadow: none;
}
#tah-collapse-expand button > :is(.fa, .fas),
#tah-buttons button > :is(.fa, .fas) {
color: var(--tah-text-primary-color);
font-size: var(--font-size-12);
margin: 3px;
padding: 3px;
pointer-events: none;
position: relative;
text-align: center;
text-shadow: var(--tah-text-shadow);
}
#tah-collapse-expand button > :is(.fa, .fas) {
font-size: medium;
}
#tah-collapse-hud {
left: -3px;
}
#tah-expand-hud {
left: -3px;
top: 16px;
}
#tah-expand-hud,
#tah-expand-hud:focus,
.tah-action-button,
.tah-action-button:focus,
.tah-group-button,
.tah-group-button:focus {
align-items: center;
background-color: var(--tah-button-background-color);
border: var(--tah-button-border-color);
border-radius: var(--tah-border-radius);
box-shadow: var(--tah-button-box-shadow);
color: var(--tah-button-text-color);
display: flex;
height: var(--tah-button-height);
margin: 0;
padding: 0;
position: relative;
text-align: center;
text-decoration: none;
text-shadow: var(--tah-text-shadow);
transition: all 0.1s ease-in-out;
white-space: nowrap;
z-index: 1;
}
.tah-action-button,
.tah-group-button {
font-size: var(--font-size-13);
min-width: var(--tah-button-min-width);
}
#tah-expand-hud:hover,
.tah-action-button:active,
.tah-action-button:not(.disabled):hover,
.tah-group-button:not(.disabled):hover {
box-shadow: var(--tah-button-hover-box-shadow);
color: var(--tah-button-hover-text-color);
}
.tah-action-button.toggle:not(.disabled):hover {
background: var(--tah-button-toggle-hover-background-color);
box-shadow: var(--tah-button-toggle-hover-box-shadow);
}
.tah-action-button.active {
background: var(--tah-button-active-background-color);
box-shadow: var(--tah-button-active-box-shadow);
}
.tah-action-button.active.activeText > .tah-action-button-content:after {
content: "*";
}
.tah-action-button.disabled {
color: var(--tah-button-disabled-text-color);
}
.tah-action-button.disabled:hover {
box-shadow: var(--tah-button-box-shadow);
}
.tah-action-button.shrink {
min-width: min-content;
}
.tah-group-button > :is(.fa, .fas) {
font-size: 8px;
position: absolute;
right: 0px;
top: 2px;
visibility: hidden;
}
.tah-group-button:hover:not(.disable-edit)
> :is(.fa, .fas) {
visibility: visible;
}
.tah-button-content:empty {
display: none;
}
.tah-button-content {
align-items: center;
display: flex;
gap: var(--tah-gap);
overflow: hidden;
padding: 0 10px;
width: 100%;
}
.tah-button-text {
padding: 0;
}
.tah-action-button-content {
align-items: center;
display: flex;
gap: var(--tah-gap);
overflow: hidden;
padding: 0 10px;
width: 100%;
}
.tah-action-button-text {
overflow: hidden;
padding-right: 1px;
text-align: left;
text-overflow: ellipsis;
width: 100%;
}
#tah-collapse-expand:hover button > i,
#tah-buttons button:hover > i {
color: var(--tah-text-hover-primary-color);
text-shadow: 0 0 8px var(--color-shadow-primary);
}
#tah-edit-hud > :is(.fa, .fas) {
font-size: var(--font-size-16);
}
#tah-groups,
.tah-tab-groups {
display: flex;
gap: var(--tah-gap);
position: relative;
}
.tah-tab-groups {
width: max-content;
}
.tah-tab-group,
.tah-group {
display: flex;
position: relative;
}
.tah-groups-container {
display: none;
position: absolute;
}
.tah-tab-group.hover > .tah-groups-container,
.tah-tab-group.hover > .tah-groups-container > .tah-groups {
display: flex;
}
.tah-tab-group > .tah-groups-container.expand-down > .tah-groups {
flex-direction: column;
left: -10px;
padding: 3px 10px 10px 10px;
position: relative;
}
.tah-tab-group > .tah-groups-container.expand-up > .tah-groups {
flex-direction: column;
left: -10px;
padding: 10px 10px 3px 10px;
position: relative;
}
#tah-groups > .tah-tab-group > .tah-groups-container.expand-up > .tah-groups {
flex-direction: column-reverse;
}
.tah-tab-group > .tah-groups-container.expand-down {
top: calc(100% - 7px);
padding-top: 10px;
}
.tah-tab-group > .tah-groups-container.expand-up {
bottom: calc(100% + 3px + -10px);
flex-direction: column-reverse;
padding-bottom: 10px;
}
.tah-tab-group
> .tah-groups-container.expand-up
> .tah-groups
> .tah-list-groups {
flex-direction: column-reverse;
}
.tah-list-groups,
.tah-tab-groups > .tah-tab-group > .tah-groups > .tah-actions {
background: var(--tah-background-color);
border-radius: var(--tah-border-radius);
}
.tah-list-groups {
flex-direction: column;
display: flex;
gap: var(--tah-gap);
}
.tah-list-groups .tah-list-groups {
background: none;
}
.tah-list-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.tah-groups {
display: flex;
flex-direction: column;
gap: var(--tah-gap);
width: max-content;
}
.tah-groups.expand-down {
flex-direction: column;
}
#tah-groups.tah-unlocked
.tah-group
> .tah-groups
> .tah-list-groups
> .tah-group {
padding-left: 10px;
}
.tah-group[data-show-title="false"]
> .tah-list-group
> .tah-groups
> .tah-list-groups
> .tah-group:not([data-show-title="false"]) {
padding-left: 0px;
}
.tah-group:not([data-show-title="false"])[data-has-image="false"]
> .tah-list-group
> .tah-groups
> .tah-list-groups
> .tah-group:not([data-show-title="false"])[data-has-image="false"] {
padding-left: 10px;
}
.tah-unlocked
.tah-group
> .tah-list-group
> .tah-groups
> .tah-list-groups
> .tah-group {
padding-left: 10px;
}
.tah-subtitle {
align-items: center;
color: var(--tah-text-primary-color);
display: flex;
flex-wrap: wrap;
font-size: var(--tah-font-size-10);
gap: var(--tah-gap);
letter-spacing: 0.1em;
line-height: 1;
text-shadow: var(--tah-text-shadow);
text-transform: uppercase;
}
.tah-subtitle:hover {
color: var(--tah-text-hover-primary-color);
cursor: pointer;
}
.tah-subtitle-text {
color: var(--tah-text-primary-color);
}
.tah-group[data-show-title="false"] > div > .tah-subtitle > .tah-subtitle-text {
color: var(--tah-text-disabled-color);
}
.tah-subtitle > :is(.tah-edit-icon, .tah-collapse-icon, .tah-expand-icon) {
bottom: 1px;
font-size: 8px;
pointer-events: none;
position: relative;
visibility: hidden;
}
.tah-subtitle:hover
> :is(
.tah-collapse-icon:not(.tah-hidden),
.tah-expand-icon:not(.tah-hidden)
),
.tah-subtitle:hover:not(.disable-edit) > .tah-edit-icon {
visibility: visible;
}
.tah-tab-group > .tah-actions {
margin-top: 5px;
}
.tah-actions {
align-items: center;
display: flex;
flex-flow: row wrap;
gap: var(--tah-gap);
width: 100%;
}
.tah-info1,
.tah-info2,
.tah-info3 {
color: var(--tah-text-secondary-color);
font-size: xx-small;
}
.tah-info1.tah-spotlight,
.tah-info2.tah-spotlight,
.tah-info3.tah-spotlight {
color: var(--tah-text-tertiary-color);
}
.tah-subtitle > :is(.tah-info1, .tah-info2, .tah-info3) {
background: var(--tah-button-background-color);
border-radius: 5px;
margin: 1px;
padding: 1px 3px;
}
.tah-button-image {
border-radius: var(--tah-border-radius);
height: var(--tah-button-height);
min-width: var(--tah-button-min-width);
width: var(--tah-button-min-width);
}
.tah-list-image {
border: none;
border-radius: var(--tah-border-radius);
box-shadow: var(--tah-button-box-shadow);
height: var(--tah-button-height);
margin-right: 5px;
min-width: var(--tah-button-min-width);
width: var(--tah-button-min-width);
}
.tah-icon > :is(.fa, .fas) {
font-size: x-small;
margin: 0px 2px 0px 0px;
}
.tah-icon-disabled {
color: var(--tah-text-disabled-color);
}
.tah-subtitle > .tah-icon > :is(.fa, .fas) {
text-shadow: var(--tah-text-shadow);
}