Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ private function addButtons(): array
$confirm_reset = $this->ui_factory
->prompt()
->standard(
$this->pons->flow()->getHereAsURI(self::CMD_CONFIRM_RESET),
new \ILIAS\UI\URLBuilder($this->pons->flow()->getHereAsURI(self::CMD_CONFIRM_RESET)),
);

$this->dic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private function index(): void
}

$prompt = $this->ui_factory->prompt()->standard(
$this->pons->flow()->getHereAsURI(self::CMD_LANGUAGE_SELECTION)
new \ILIAS\UI\URLBuilder($this->pons->flow()->getHereAsURI(self::CMD_LANGUAGE_SELECTION)),
);

// Edit Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function getButtonToControlCenter(
rtrim(ILIAS_HTTP_PATH, '/') . '/' .
ltrim($this->link_factory->getViewLink($ref_id, $obj_id, $type), '/')
);
$prompt = $this->ui_factory->prompt()->standard($view_link);
$prompt = $this->ui_factory->prompt()->standard(new \ILIAS\UI\URLBuilder($view_link));
$button = $this->ui_factory->button()->standard($this->getButtonLabel(), $prompt->getShowSignal());
$message = $this->ui_factory->messageBox()->info($this->getStatusInfo($status))->withButtons([$button]);
return [$message, $prompt];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ private function buildPrompt(): Prompt
return $this->environment->getUIFactory()->prompt()->standard(
$this->environment->withSubActionParameter(
self::SUB_ACTION_SELECT_TYPE
)->getUrlBuilder()->buildURI()
)->getUrlBuilder(),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class ConsecutiveScoringGUI implements SegmentRetrieval
private const FILTER_SCOREDBY = 'fscrby';

private ?array $filter_values = null;
private Prompt $prompt;

public function __construct(
protected readonly \ilCtrlInterface $ctrl,
Expand All @@ -78,7 +77,6 @@ public function __construct(
protected readonly ConsecutiveScoringURLs $scoring_url_builder,
protected readonly \ilUIFilterService $filter_service,
) {
$this->prompt = $this->ui_factory->prompt()->standard($this->scoring_url_builder->buildURI());
}
public function executeCommand(): void
{
Expand Down Expand Up @@ -254,7 +252,7 @@ public function getSegment(
$viewcontrol_values->isUserCentric()
? $this->collectForUser($usr_active_id, $question_ids)
: $this->collectForQuestion($question_id, $usr_active_ids),
$this->prompt
$this->ui_factory->prompt()->standard(new \ILIAS\UI\URLBuilder($this->scoring_url_builder->buildURI()))
]
)
)
Expand Down Expand Up @@ -663,17 +661,22 @@ protected function singleSegmentContent(
);
}

protected function getSingleFormButton(int $question_id, int $usr_active_id, int $attempt_id): StdButton
/** @return array{0: StdButton, 1: Prompt} */
protected function getSingleFormButton(int $question_id, int $usr_active_id, int $attempt_id): array
{
$url = $this->scoring_url_builder
->withAction(self::ACTION_FORM_STATE)
->withIdParameters($question_id, $usr_active_id, $attempt_id)
->buildURI();

return $this->ui_factory->button()->standard(
$prompt = $this->ui_factory->prompt()->standard(new \ILIAS\UI\URLBuilder($url));

$button = $this->ui_factory->button()->standard(
$this->lng->txt('edit_score'),
$this->prompt->getShowSignal($url)
$prompt->getShowSignal()
);

return [$button, $prompt];
}

protected function getSegmentActionsForUser(int $usr_active_id): array
Expand Down
20 changes: 20 additions & 0 deletions components/ILIAS/UI/docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,26 @@ contain an object that is exported from a `.js` file, which can be imported dire
Alternatively, we build this as a string and store this in an `.php` artifact and ship its content via our component
renderers, so it is available as global state (e.g. `il.UI.Config`).

### Refactoring `UI\Component\Table\Data` and `Ordering` components (advanced, ~8d)

The Data and Ordering Table have accumulated some structural debt on both server and client, which makes it increasingly
hard to maintain and extend it. We should therefore refactor these components internally and focus on the following
aspects:

- Remove the unnecessary `Table\AbstractTable` and `Table\Table` inheritance level.
- Refine the public interface of `Table\Action` and `Table\Column` so consumers can contribute custom implementations in
theory. Today they expose too little information for the UI framework to process them and since they are passed to
Tables by composition, we should also support less concrete implementations than our internals. A symptom of this is the
check that given Actions and Columns are an instanfe of our implementation.
- Simplify the rendering by reusing more UI components internally, e.g. for warning dialog and replace implementation of
`Input\NameSource` with proper context rendering.
- Stop carrying around Action information between rendering steps via the composable DTO. This can most likely be
avoided by delegating more of this work to the client.
- Modularise the current client-side facility which has grown into a god object. Create smaller more managable modules
e.g. for selection, action handling, async rendering, navigation.
- Modernise the client-side architecture to fit modern patterns and remove the use of globals. Remove dynamic DOM
lookups (which potentially happen many times) and follow correctness on construction.

## Long Term

### Mark Some Components as Internal
Expand Down
2 changes: 1 addition & 1 deletion components/ILIAS/UI/resources/js/Table/dist/table.min.js

Large diffs are not rendered by default.

29 changes: 20 additions & 9 deletions components/ILIAS/UI/resources/js/Table/src/datatable.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ export default class DataTable {
#jquery;

/**
* @type {actionId: string, rowId: string}
* @type {JQueryEventDispatcher}
*/
#eventDispatcher;

/**
* @type {actionId: string, rowId: string, async: string}
*/
#signalConstants;

Expand Down Expand Up @@ -70,18 +75,19 @@ export default class DataTable {
#table;

/**
* @type {array<string, {async: bool, urlBuilder: URLBuilder, urlTokens: Map}>}
* @type {array<string, {async: bool, signalId: string, urlBuilder: URLBuilder, urlTokens: Map}>}
*/
#actionsRegistry;

/**
* @param {jQuery} jquery
* @param {JQueryEventDispatcher} eventDispatcher
* @param {string} optActionId
* @param {string} optRowId
* @param {string} tableId
* @throws {Error} if DOM element is missing
*/
constructor(jquery, optActionId, optRowId, componentId) {
constructor(jquery, eventDispatcher, optActionId, optRowId, componentId) {
this.#component = document.getElementById(componentId);
if (this.#component === null) {
throw new Error(`Could not find a DataTable for id '${componentId}'.`);
Expand All @@ -95,6 +101,7 @@ export default class DataTable {
this.#responseContent = this.#responseContainer.getElementsByClassName('c-table-data__async_messageresponse').item(0);

this.#jquery = jquery;
this.#eventDispatcher = eventDispatcher;
this.#signalConstants = {
actionId: optActionId,
rowId: optRowId,
Expand All @@ -113,13 +120,15 @@ export default class DataTable {
/**
* @param {string} actionId
* @param {bool} async
* @param {string} signalId
* @param {URLBuilder} urlBuilder
* @param {Map} urlTokens
* @return {void}
*/
registerAction(actionId, async, urlBuilder, urlTokens) {
registerAction(actionId, async, signalId, urlBuilder, urlTokens) {
this.#actionsRegistry[actionId] = {
async,
signalId,
urlBuilder,
urlTokens,
};
Expand Down Expand Up @@ -164,7 +173,7 @@ export default class DataTable {
* @return {void}
*/
#disableRowSelection(flag) {
this.#table.querySelectorAll('.c-table-data__row-selector').forEach(
this.#table.querySelectorAll('.c-table-data__row-selector')?.forEach(
(chk) => {
chk.disabled = (flag === true && !chk.checked);
},
Expand Down Expand Up @@ -207,7 +216,7 @@ export default class DataTable {
collectSelectedRowIds() {
const ret = [];

this.#table.querySelectorAll('.c-table-data__row-selector').forEach(
this.#table.querySelectorAll('.c-table-data__row-selector')?.forEach(
(chk) => {
if (chk.checked) {
ret.push(chk.value);
Expand Down Expand Up @@ -249,7 +258,6 @@ export default class DataTable {
const k = this.#signalConstants.actionId;
const signalData = { options: {} };
signalData.options[k] = selectedAction;

modalClose.click();
this.doAction(signalData, ['ALL_OBJECTS']);
}
Expand All @@ -267,10 +275,13 @@ export default class DataTable {
action.urlBuilder.writeParameter(token, rowIds);
const target = decodeURI(action.urlBuilder.getUrl().toString());

if (!action.async) {
if (action.signalId === '') {
window.location.href = target;
} else {
} else if (action.async) {
this.asyncAction(target);
} else {
signalData.options.url = target;
this.#eventDispatcher.dispatch(this.#component, action.signalId, signalData);
}
}

Expand Down
10 changes: 9 additions & 1 deletion components/ILIAS/UI/resources/js/Table/src/datatable.factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@ export default class DataTableFactory {
*/
#jquery;

/**
* @type {JQueryEventDispatcher}
*/
#eventDispatcher;

/**
* @type {Array<string, DataTable>}
*/
#instances = [];

/**
* @param {jQuery} jquery
* @param {JQueryEventDispatcher} eventDispatcher
*/
constructor(jquery) {
constructor(jquery, eventDispatcher) {
this.#jquery = jquery;
this.#eventDispatcher = eventDispatcher;
}

/**
Expand All @@ -47,6 +54,7 @@ export default class DataTableFactory {

this.#instances[tableId] = new DataTable(
this.#jquery,
this.#eventDispatcher,
optActionId,
optRowId,
tableId,
Expand Down
11 changes: 7 additions & 4 deletions components/ILIAS/UI/resources/js/Table/src/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@

import il from 'il';
import $ from 'jquery';
import DataTableFactory from './datatable.factory';
import PresentationTableFactory from './presentationtable.factory';
import OrderingTableFactory from './orderingtable.factory';
import DataTableFactory from './datatable.factory.js';
import PresentationTableFactory from './presentationtable.factory.js';
import OrderingTableFactory from './orderingtable.factory.js';
import JQueryEventDispatcher from '../../Core/src/jqueryeventdispatcher.js';

const eventDispatcher = new JQueryEventDispatcher($);

il.UI = il.UI || {};
il.UI.table = il.UI.table || {};
il.UI.table.data = new DataTableFactory($);
il.UI.table.data = new DataTableFactory($, eventDispatcher);
il.UI.table.presentation = new PresentationTableFactory();
il.UI.table.ordering = new OrderingTableFactory();
7 changes: 4 additions & 3 deletions components/ILIAS/UI/src/Component/Prompt/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

namespace ILIAS\UI\Component\Prompt;

use ILIAS\Data\URI;
use ILIAS\UI\URLBuilder;

interface Factory
{
Expand All @@ -46,11 +46,12 @@ interface Factory
* usage:
* 1: >
* The server MUST answer with an Prompt State Component
* to a request to the url provided to the Prompt.
* to a request to the url provided to the Prompt via the URLBuilder.
* ---
* @param \ILIAS\UI\URLBuilder $async_url_builder
* @return \ILIAS\UI\Component\Prompt\Prompt
*/
public function standard(URI $async_url): Prompt;
public function standard(URLBuilder $async_url_builder): Prompt;

/**
* ---
Expand Down
26 changes: 23 additions & 3 deletions components/ILIAS/UI/src/Component/Prompt/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
use ILIAS\UI\Component\JavaScriptBindable;
use ILIAS\UI\Component\Signal;
use ILIAS\Data\URI;
use ILIAS\UI\URLBuilderToken;
use ILIAS\UI\URLBuilder;

interface Prompt extends Component, JavaScriptBindable
{
Expand All @@ -33,12 +35,30 @@ interface Prompt extends Component, JavaScriptBindable
public function withTitle(string $title): static;

/**
* Get the signal to load and show this Prompt.
* Get a Prompt like this, but alter a parameter of the URLBuilder
* with another value. The parameter is identified using a token.
*
* This alters the Signal returned by {@see self::getShowSignal()}.
*/
public function getShowSignal(?URI $uri = null): Signal;
public function withParameter(URLBuilderToken $token, string|array $value): static;

/**
* Get the signal to close this Prompt.
* Get the URLBuilder used to load Prompt State's for this Prompt.
*
* This is on the public interface so other UI components can access
* the endpoint of the Prompt as well.
*/
public function getUrlBuilder(): URLBuilder;

/**
* Get the Signal to show this Prompt and load its Prompt State.
*
* Alter this Signal using {@see self::withParameter()}.
*/
public function getShowSignal(): Signal;

/**
* Get the Signal to close this Prompt.
*/
public function getCloseSignal(): Signal;
}
9 changes: 6 additions & 3 deletions components/ILIAS/UI/src/Component/Table/Action/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@

namespace ILIAS\UI\Component\Table\Action;

use ILIAS\UI\Component\Signal;
use ILIAS\UI\Component as C;
use ILIAS\Data\URI;

interface Action extends \ILIAS\UI\Component\Component
interface Action extends C\Component
{
public function withAsync(bool $async = true): self;
/**
* @deprecated construct the Action using a {@see C\Prompt\Prompt} instead.
*/
public function withAsync(bool $is_async = true): self;
}
Loading
Loading