initial style in compedium browser

This commit is contained in:
moliloo 2025-08-05 01:20:41 -03:00
parent 845d72e20c
commit 1eae802c46
6 changed files with 244 additions and 110 deletions

View file

@ -2312,7 +2312,8 @@
"appliedEvenIfSuccessful": "Applied even if save succeeded", "appliedEvenIfSuccessful": "Applied even if save succeeded",
"diceIsRerolled": "The dice has been rerolled (x{times})", "diceIsRerolled": "The dice has been rerolled (x{times})",
"pendingSaves": "Pending Reaction Rolls", "pendingSaves": "Pending Reaction Rolls",
"openSheetSettings": "Open Settings" "openSheetSettings": "Open Settings",
"compediumBrowser": "Compedium Browser"
} }
} }
} }

View file

@ -1,4 +1,3 @@
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
/** /**
@ -21,12 +20,13 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
/** @inheritDoc */ /** @inheritDoc */
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
id: 'itemBrowser', id: 'itemBrowser',
classes: ['dh-style'], classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'],
tag: 'div', tag: 'div',
// title: 'Item Browser', // title: 'Item Browser',
window: { window: {
frame: true, frame: true,
title: 'Item Browser', title: 'Compedium Browser',
icon: 'fa-solid fa-book-atlas',
positioned: true, positioned: true,
resizable: true resizable: true
}, },
@ -104,7 +104,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
context.formatChoices = this.formatChoices; context.formatChoices = this.formatChoices;
context.fieldFilter = this.fieldFilter = this.selectedMenu.data?.filters ? this._createFieldFilter() : []; context.fieldFilter = this.fieldFilter = this.selectedMenu.data?.filters ? this._createFieldFilter() : [];
context.items = this.items; context.items = this.items;
console.log(this.items) console.log(this.items);
return context; return context;
} }
@ -115,11 +115,13 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
id: c.id, id: c.id,
label: c.label, label: c.label,
selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id selected: (!parent || parent.selected) && this.selectedMenu.path[depth] === c.id
} };
folder.folders = c.folders ? ItemBrowser.sortBy(this.getCompendiumFolders(c.folders, folder, depth + 2), 'label') : []; folder.folders = c.folders
? ItemBrowser.sortBy(this.getCompendiumFolders(c.folders, folder, depth + 2), 'label')
: [];
// sortBy(Object.values(c.folders), 'label') // sortBy(Object.values(c.folders), 'label')
folders.push(folder) folders.push(folder);
}) });
// console.log(folders) // console.log(folders)
return folders; return folders;
@ -135,12 +137,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
this.selectedMenu = { this.selectedMenu = {
path: folderPath.split('.'), path: folderPath.split('.'),
data: folderData data: folderData
} };
let items = []; let items = [];
for(const key of folderData.keys) { for (const key of folderData.keys) {
const comp = game.packs.get(`${compendium}.${key}`); const comp = game.packs.get(`${compendium}.${key}`);
if(!comp) return; if (!comp) return;
items = items.concat(await comp.getDocuments({ type__in: folderData.type })); items = items.concat(await comp.getDocuments({ type__in: folderData.type }));
} }
@ -150,37 +152,37 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
static expandContent(_, target) { static expandContent(_, target) {
const parent = target.parentElement; const parent = target.parentElement;
parent.classList.toggle("expanded"); parent.classList.toggle('expanded');
} }
static sortBy(data, property) { static sortBy(data, property) {
return data.sort((a, b) => a[property] > b[property] ? 1 : -1) return data.sort((a, b) => (a[property] > b[property] ? 1 : -1));
} }
formatLabel(item, field) { formatLabel(item, field) {
const property = foundry.utils.getProperty(item, field.key); const property = foundry.utils.getProperty(item, field.key);
if(typeof field.format !== 'function') return property; if (typeof field.format !== 'function') return property;
return field.format(property); return field.format(property);
} }
formatChoices(data) { formatChoices(data) {
if(!data.field.choices) return null; if (!data.field.choices) return null;
const config = { const config = {
choices: data.field.choices choices: data.field.choices
}; };
foundry.data.fields.StringField._prepareChoiceConfig(config); foundry.data.fields.StringField._prepareChoiceConfig(config);
return config.options.filter(c => data.filtered.includes(c.value) || data.filtered.includes(c.label.toLowerCase())); return config.options.filter(
c => data.filtered.includes(c.value) || data.filtered.includes(c.label.toLowerCase())
);
} }
_createFieldFilter() { _createFieldFilter() {
const filters = []; const filters = [];
this.selectedMenu.data.filters.forEach(f => { this.selectedMenu.data.filters.forEach(f => {
if(typeof f.field === 'string') if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field);
f.field = foundry.utils.getProperty(game, f.field); else if (typeof f.choices === 'function') f.choices = f.choices();
else if(typeof f.choices === 'function') filters.push(f);
f.choices = f.choices(); });
filters.push(f)
})
return filters; return filters;
} }
@ -201,7 +203,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
const filters = [ const filters = [
{ {
key: 'browser', key: 'browser',
input: 'input[type="search"].search-browser', input: 'input[type="search"].search-input',
content: '[data-application-part="list"] .item-list', content: '[data-application-part="list"] .item-list',
callback: this._onSearchFilterBrowser.bind(this) callback: this._onSearchFilterBrowser.bind(this)
} }
@ -228,7 +230,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
key: 'browser', key: 'browser',
container: '[data-application-part="list"] .filter-content .wrapper', container: '[data-application-part="list"] .filter-content .wrapper',
content: '[data-application-part="list"] .item-list', content: '[data-application-part="list"] .item-list',
callback: this._onInputFilterBrowser.bind(this), callback: this._onInputFilterBrowser.bind(this)
// target: '.filter-button', // target: '.filter-button',
// filters: FilterMenu.invetoryFilters // filters: FilterMenu.invetoryFilters
} }
@ -236,10 +238,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
inputs.forEach(m => { inputs.forEach(m => {
const container = this.element.querySelector(m.container); const container = this.element.querySelector(m.container);
if(!container) return this.#input[m.key] = {}; if (!container) return (this.#input[m.key] = {});
const inputs = container.querySelectorAll('input, select'); const inputs = container.querySelectorAll('input, select');
inputs.forEach(input => { inputs.forEach(input => {
input.addEventListener('change', this._onInputFilterBrowser.bind(this)) input.addEventListener('change', this._onInputFilterBrowser.bind(this));
}); });
this.#filteredItems[m.key].input = new Set(this.items.map(i => i.id)); this.#filteredItems[m.key].input = new Set(this.items.map(i => i.id));
this.#input[m.key] = inputs; this.#input[m.key] = inputs;
@ -276,7 +278,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
async _onInputFilterBrowser(event) { async _onInputFilterBrowser(event) {
this.#filteredItems.browser.input.clear(); this.#filteredItems.browser.input.clear();
console.log(event.target.name) console.log(event.target.name);
this.fieldFilter.find(f => f.key === event.target.name).value = event.target.value; this.fieldFilter.find(f => f.key === event.target.name).value = event.target.value;
@ -287,8 +289,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
item = this.items.find(i => i.uuid === itemUUID); item = this.items.find(i => i.uuid === itemUUID);
const matchesMenu = const matchesMenu =
this.fieldFilter.length === 0 || this.fieldFilter.every(f => { this.fieldFilter.length === 0 ||
return (!f.value && f.value !== false) || foundry.applications.ux.SearchFilter.evaluateFilter(item, this.createFilterData(f)) this.fieldFilter.every(f => {
return (
(!f.value && f.value !== false) ||
foundry.applications.ux.SearchFilter.evaluateFilter(item, this.createFilterData(f))
);
}); });
if (matchesMenu) this.#filteredItems.browser.input.add(item.id); if (matchesMenu) this.#filteredItems.browser.input.add(item.id);
@ -301,10 +307,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
createFilterData(filter) { createFilterData(filter) {
return { return {
field: filter.key, field: filter.key,
value: isNaN(filter.value) ? (["true", "false"].includes(filter.value) ? filter.value === "true" : filter.value) : Number(filter.value), value: isNaN(filter.value)
? ['true', 'false'].includes(filter.value)
? filter.value === 'true'
: filter.value
: Number(filter.value),
operator: filter.operator, operator: filter.operator,
negate: filter.negate negate: filter.negate
} };
} }
static resetFilters() { static resetFilters() {
@ -324,15 +334,15 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
_createDragProcess() { _createDragProcess() {
new foundry.applications.ux.DragDrop.implementation({ new foundry.applications.ux.DragDrop.implementation({
dragSelector: ".item-container", dragSelector: '.item-container',
// dropSelector: ".directory-list", // dropSelector: ".directory-list",
permissions: { permissions: {
dragstart: this._canDragStart.bind(this), dragstart: this._canDragStart.bind(this)
// drop: this._canDragDrop.bind(this) // drop: this._canDragDrop.bind(this)
}, },
callbacks: { callbacks: {
// dragover: this._onDragOver.bind(this), // dragover: this._onDragOver.bind(this),
dragstart: this._onDragStart.bind(this), dragstart: this._onDragStart.bind(this)
// drop: this._onDrop.bind(this) // drop: this._onDrop.bind(this)
} }
}).bind(this.element); }).bind(this.element);
@ -349,7 +359,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
const dragData = foundry.utils.fromUuidSync(itemUuid).toDragData(); const dragData = foundry.utils.fromUuidSync(itemUuid).toDragData();
// console.log(dragData) // console.log(dragData)
// const dragData = { UUID: itemUuid }; // const dragData = { UUID: itemUuid };
event.dataTransfer.setData("text/plain", JSON.stringify(dragData)); event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
} }
_canDragStart() { _canDragStart() {

View file

@ -1,10 +1,12 @@
@scrollbar-width: 20px; @import '../../utils/colors.less';
#itemBrowser { @import '../../utils/fonts.less';
.application.daggerheart.dh-style.compendium-browser {
border: initial; border: initial;
.window-content { .window-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 50px; padding: 0px;
> div { > div {
overflow: hidden; overflow: hidden;
@ -13,23 +15,42 @@
gap: 20px; gap: 20px;
} }
div[data-application-part="list"] { div[data-application-part='list'] {
flex: 1; flex: 1;
gap: 10px; gap: 10px;
} }
.compedium-sidebar {
position: relative;
width: 200px;
padding: 16px;
&::before {
content: '';
position: absolute;
right: 0;
background: @golden;
mask-image: linear-gradient(180deg, transparent 0%, black 50%, transparent 100%);
width: 1px;
height: 100%;
}
.compendium-container { .compendium-container {
summary { summary {
font-family: 'Montserrat', sans-serif; display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
font-family: @font-subtitle;
font-weight: bold; font-weight: bold;
padding: 2px 12px; padding: 2px 12px;
border-radius: 3px; color: light-dark(@dark, @beige);
background-color: light-dark(#18162e, #f3c267);
color: light-dark(#efe6d8, #18162e);
list-style: none; list-style: none;
cursor: pointer;
&::marker, /* Latest Chrome, Edge, Firefox */ &::marker, // Latest Chrome, Edge, Firefox
&::-webkit-details-marker /* Safari */ { &::-webkit-details-marker // Safari
{
display: none; display: none;
} }
} }
@ -44,49 +65,54 @@
margin-top: 5px; margin-top: 5px;
} }
} }
&:not(.folder-list) {
margin-top: 10px;
} }
} }
} }
} }
.menu-path, option, select { .compedium-results {
padding: 16px;
}
.menu-path,
option,
select {
text-transform: capitalize; text-transform: capitalize;
} }
.menu-path > :first-child { .menu-path > :first-child {
text-transform: uppercase; font-weight: bold;
} }
.menu-path { .menu-path {
display: flex; display: flex;
align-items: center; align-items: center;
> span { gap: 10px;
font-family: 'Montserrat', sans-serif;
font-weight: bold; .item-path {
padding: 2px 12px; font-family: @font-body;
border-radius: 3px; color: light-dark(@dark, @beige);
background-color: light-dark(#18162e, #f3c267);
color: light-dark(#efe6d8, #18162e);
&.path-link { &.path-link {
padding: 0 5px; color: light-dark(@dark, @beige);
background-color: light-dark(#f3c267, #18162e);
color: light-dark(#18162e, #efe6d8);
} }
} }
} }
.folder-list, .item-list-header, .item-header > div { .folder-list,
.item-list-header,
.item-header > div {
gap: 10px; gap: 10px;
cursor: pointer; cursor: pointer;
} }
.item-filter { .item-filter {
display: flex;
align-items: center;
flex-direction: column;
.wrapper, .form-group { .wrapper,
.form-group {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
@ -100,6 +126,47 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
width: 100%;
.search-bar {
position: relative;
color: light-dark(@dark-blue-50, @beige-50);
width: 100%;
input {
border-radius: 50px;
font-family: @font-body;
background: light-dark(@dark-blue-10, @golden-10);
border: none;
outline: 2px solid transparent;
transition: all 0.3s ease;
padding: 0 20px;
width: 100%;
&:hover {
outline: 2px solid light-dark(@dark, @golden);
}
&:placeholder {
color: light-dark(@dark-blue-50, @beige-50);
}
&::-webkit-search-cancel-button {
-webkit-appearance: none;
display: none;
}
}
.icon {
align-content: center;
height: 32px;
position: absolute;
right: 20px;
font-size: 16px;
z-index: 1;
color: light-dark(@dark-blue-50, @beige-50);
}
}
} }
} }
@ -107,25 +174,36 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
[data-folder-id] { [data-folder-id] {
padding: 2px 12px; padding: 5px 10px;
border: 1px solid transparent; border: 1px solid transparent;
font-family: @font-body;
transition: all 0.1s ease;
} }
.is-selected, > [data-folder-id]:hover { .is-selected,
[data-folder-id]:hover {
font-weight: bold; font-weight: bold;
border-radius: 3px; border-radius: 3px;
background-color: light-dark(transparent, #18162e); background-color: light-dark(@dark-blue-40, @golden-40);
color: light-dark(#18162e, #f3c267); color: light-dark(@dark-blue, @golden);
border-color: light-dark(#18162e, #f3c267);
} }
.subfolder-list { .subfolder-list {
margin: 0 10px; margin: 5px 0;
gap: 0; gap: 0;
.is-selected,
[data-folder-id]:hover {
font-weight: bold;
border-radius: 3px;
background-color: light-dark(@dark-blue-10, @golden-10);
color: light-dark(@dark-blue, @golden);
}
} }
} }
.item-list-header, .item-header > div { .item-list-header,
.item-header > div {
display: grid; display: grid;
grid-template-columns: 40px 400px repeat(auto-fit, minmax(100px, 1fr)); grid-template-columns: 40px 400px repeat(auto-fit, minmax(100px, 1fr));
align-items: center; align-items: center;
@ -133,9 +211,12 @@
// gap: 10px; // gap: 10px;
} }
.item-list-header, .item-list { .item-list-header,
.item-list {
overflow-y: auto; overflow-y: auto;
scrollbar-gutter: stable; scrollbar-gutter: stable;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
} }
.item-list-header { .item-list-header {
@ -153,7 +234,7 @@
.item-container { .item-container {
&:hover { &:hover {
background: rgba(255, 255, 255, .1); background: rgba(255, 255, 255, 0.1);
} }
} }
@ -167,10 +248,11 @@
} }
.filter-content { .filter-content {
padding: 0 10px; padding: 0px;
} }
.filter-content, .item-desc { .filter-content,
.item-desc {
display: grid; display: grid;
grid-template-rows: 0fr; grid-template-rows: 0fr;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
@ -178,13 +260,20 @@
overflow: hidden; overflow: hidden;
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
padding: 0;
.form-group { .form-group {
label { label {
flex: 1; flex: 1;
font-family: @font-body;
} }
.form-fields { .form-fields {
flex: 2; flex: 2;
input[type='number'] {
text-align: center;
color: light-dark(@dark, @beige);
}
} }
} }
} }
@ -197,8 +286,20 @@
.welcome-message { .welcome-message {
display: flex; display: flex;
flex-direction: column;
gap: 5px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: -webkit-fill-available;
margin: 0;
.title {
font-family: @font-subtitle;
margin: 0;
}
.hint {
font-family: @font-body;
}
} }
} }
} }

View file

@ -10,6 +10,9 @@
<a class="filter-button"> <a class="filter-button">
<i class="fa-solid fa-filter"></i> <i class="fa-solid fa-filter"></i>
</a> </a>
<a data-tooltip="{{localize 'DAGGERHEART.UI.Tooltip.compediumBrowser'}}" data-action="tempBrowser">
<i class="fa-solid fa-book-atlas"></i>
</a>
</div> </div>
<div class="currency-section"> <div class="currency-section">
@ -68,5 +71,4 @@
canCreate=true canCreate=true
}} }}
</div> </div>
<button type="button" data-action="tempBrowser">Item Browser</button>
</section> </section>

View file

@ -1,22 +1,29 @@
<div> <div class="compedium-results">
{{#if menu.data }} {{#if menu.data }}
<div class="menu-path"> <div class="menu-path">
{{#each menu.path}} {{#each menu.path}}
{{#unless (eq this "folders")}} {{#unless (eq this "folders")}}
<span>{{this}}</span> <span class="item-path">{{this}}</span>
{{#unless @last}} {{#unless @last}}
<span class="path-link"> > </span> <span class="path-link">
/
</span>
{{/unless}} {{/unless}}
{{/unless}} {{/unless}}
{{/each}} {{/each}}
</div> </div>
<fieldset class="item-filter glassy"> <div class="item-filter">
<div class="filter-header"> <div class="filter-header">
<input type="search" name="search" class="search-browser" placeholder="Search..."> <div class="search-bar">
<div class="icon">
<i class="fa-solid fa-magnifying-glass"></i>
</div>
<input type="search" name="search" class="search-input" placeholder="Search...">
</div>
{{#if fieldFilter}} {{#if fieldFilter}}
<button type="button" data-action="expandContent">Filters</button> <a data-tooltip="Filters" data-action="expandContent"><i class="fa-solid fa-filter"></i></a>
{{/if}} {{/if}}
<button type="button" data-action="resetFilters">Reset</button> <a data-tooltip="Erase" data-action="resetFilters"><i class="fa-solid fa-eraser"></i></a>
</div> </div>
<div class="filter-content extensible"> <div class="filter-content extensible">
<div class="wrapper"> <div class="wrapper">
@ -44,7 +51,7 @@
{{/each}} {{/each}}
</div> </div>
</div> </div>
</fieldset> </div>
{{!-- <div class="item-list-container"> --}} {{!-- <div class="item-list-container"> --}}
{{#if menu.data.columns.length}} {{#if menu.data.columns.length}}
<div class="item-list-header"> <div class="item-list-header">
@ -75,6 +82,9 @@
</div> </div>
{{!-- </div> --}} {{!-- </div> --}}
{{else}} {{else}}
<h2 class="welcome-message">Welcome to DAGGERHEART Items Browser</h2> <div class="welcome-message">
<h2 class="title">Daggerheart Compedium Browser</h2>
<span class="hint"><i>Select a Folder in sidebar to start browsing trought the compedium</i></span>
</div>
{{/if}} {{/if}}
</div> </div>

View file

@ -1,18 +1,28 @@
<div> <div class="compedium-sidebar">
{{#each compendiums}} {{#each compendiums}}
<details class="compendium-container" data-compendium-id="{{id}}" open> <details class="compendium-container" data-compendium-id="{{id}}" open>
<summary>{{label}}</summary> <summary>
{{label}}
<line-div></line-div>
</summary>
<div class="folder-list"> <div class="folder-list">
{{#each folders}} {{#each folders}}
<div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div> <div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div>
{{!-- <div data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div> --}} {{!-- <div data-action="selectFolder" data-folder-id="{{id}}">{{label}}</div> --}}
<div class="folder-list subfolder-list"> <div class="subfolder-list">
{{#each folders}} {{#each folders}}
<div class="{{#if selected}} is-selected{{/if}}" data-action="selectFolder" data-folder-id="{{../id}}.folders.{{id}}">- {{label}}</div> <div
class="subfolder-item {{#if selected}} is-selected{{/if}}"
data-action="selectFolder"
data-folder-id="{{../id}}.folders.{{id}}"
>
{{label}}
</div>
{{/each}} {{/each}}
</div> </div>
{{/each}} {{/each}}
</div> </div>
<line-div></line-div>
</details> </details>
{{/each}} {{/each}}
</div> </div>