forked from extern/egroupware
Home: Weather portlet improvements
This commit is contained in:
parent
665dcc18da
commit
5cfe1cef58
@ -22,6 +22,7 @@ import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||
import {et2_IResizeable} from "../et2_core_interfaces";
|
||||
import {HomeApp} from "../../../../home/js/app";
|
||||
import {etemplate2} from "../etemplate2";
|
||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||
|
||||
/**
|
||||
* Participate in Home
|
||||
@ -392,6 +393,17 @@ export class Et2Portlet extends Et2Widget(SlCard)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of user-configurable properties
|
||||
* @returns {[{name : string, type : string, select_options? : [SelectOption]}]}
|
||||
*/
|
||||
get portletProperties() : { name : string, type : string, label : string, select_options? : SelectOption[] }[]
|
||||
{
|
||||
return [
|
||||
{name: 'color', label: "Color", type: 'et2-colorpicker'}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create & show a dialog for customizing this portlet
|
||||
*
|
||||
@ -404,7 +416,7 @@ export class Et2Portlet extends Et2Widget(SlCard)
|
||||
callback: this._process_edit.bind(this),
|
||||
template: this.editTemplate,
|
||||
value: {
|
||||
content: this.settings
|
||||
content: {...this.settings, ...this.portletProperties}
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
@ -455,6 +467,7 @@ export class Et2Portlet extends Et2Widget(SlCard)
|
||||
{
|
||||
this.settings = {...this.settings, value};
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
public update_settings(settings)
|
||||
|
@ -11,6 +11,8 @@ import {ClassWithAttributes, ClassWithInterfaces} from "../et2_core_inheritance"
|
||||
import {css, dedupeMixin, LitElement, PropertyValues, unsafeCSS} from "@lion/core";
|
||||
import type {et2_container} from "../et2_core_baseWidget";
|
||||
import type {et2_DOMWidget} from "../et2_core_DOMWidget";
|
||||
import {egw_getActionManager, egw_getAppObjectManager} from "../../egw_action/egw_action.js";
|
||||
import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER} from "../../egw_action/egw_action_constants";
|
||||
|
||||
/**
|
||||
* This mixin will allow any LitElement to become an Et2Widget
|
||||
@ -84,6 +86,11 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
||||
*/
|
||||
protected _deferred_properties : { [key : string] : string } = {};
|
||||
|
||||
/**
|
||||
* EGroupware action system action manager
|
||||
*/
|
||||
private _actionManager : egwAction;
|
||||
|
||||
|
||||
/** WebComponent **/
|
||||
static get styles()
|
||||
@ -91,18 +98,20 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
||||
return [
|
||||
...(super.styles ? (Array.isArray(super.styles) ? super.styles : [super.styles]) : []),
|
||||
css`
|
||||
:host([disabled]) {
|
||||
:host([disabled]) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* CSS to align internal inputs according to box alignment */
|
||||
:host([align="center"]) .input-group__input {
|
||||
/* CSS to align internal inputs according to box alignment */
|
||||
|
||||
:host([align="center"]) .input-group__input {
|
||||
justify-content: center;
|
||||
}
|
||||
:host([align="right"]) .input-group__input {
|
||||
}
|
||||
|
||||
:host([align="right"]) .input-group__input {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
`];
|
||||
}
|
||||
`];
|
||||
}
|
||||
|
||||
static get properties()
|
||||
@ -192,6 +201,9 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
||||
data: {
|
||||
type: String,
|
||||
reflect: false
|
||||
},
|
||||
actions: {
|
||||
type: Object
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1331,6 +1343,124 @@ const Et2WidgetMixin = <T extends Constructor>(superClass : T) =>
|
||||
// If we're the root object, return the phpgwapi API instance
|
||||
return typeof egw === "function" ? egw('phpgwapi', wnd) : (window['egw'] ? window['egw'] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Actions on the widget
|
||||
*
|
||||
* Each action is defined as an object:
|
||||
*
|
||||
* move: {
|
||||
* type: "drop",
|
||||
* acceptedTypes: "mail",
|
||||
* icon: "move",
|
||||
* caption: "Move to"
|
||||
* onExecute: javascript:app.mail.drop_move"
|
||||
* }
|
||||
*
|
||||
* This will turn the widget into a drop target for "mail" drag types. When "mail" drag types are dropped,
|
||||
* the function drop_move(egwAction action, egwActionObject sender) will be called. The ID of the
|
||||
* dragged "mail" will be in sender.id, some information about the sender will be in sender.context. The
|
||||
* widget involved can typically be found in action.parent.data.widget, so your handler
|
||||
* can operate in the widget context easily. The location varies depending on your action though. It
|
||||
* might be action.parent.parent.data.widget
|
||||
*
|
||||
* To customise how the actions are handled for a particular widget, override _link_actions(). It handles
|
||||
* the more widget-specific parts.
|
||||
*
|
||||
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
||||
*/
|
||||
set actions(actions : egwAction[])
|
||||
{
|
||||
if(this.id == "" || typeof this.id == "undefined")
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Initialize the action manager and add some actions to it
|
||||
// Only look 1 level deep
|
||||
let gam = egw_getActionManager(this.egw().app_name(), true, 1);
|
||||
if(typeof this._actionManager != "object" && actions.length > 0)
|
||||
{
|
||||
if(gam.getActionById(this.getInstanceManager().uniqueId, 1) !== null)
|
||||
{
|
||||
gam = gam.getActionById(this.getInstanceManager().uniqueId, 1);
|
||||
}
|
||||
if(gam.getActionById(this.id, 1) != null)
|
||||
{
|
||||
this._actionManager = gam.getActionById(this.id, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._actionManager = gam.addAction("actionManager", this.id);
|
||||
}
|
||||
}
|
||||
this._actionManager.updateActions(actions, this.egw().appName);
|
||||
//if (this.options.default_execute) this._actionManager.setDefaultExecute(this.options.default_execute);
|
||||
|
||||
// Put a reference to the widget into the action stuff, so we can
|
||||
// easily get back to widget context from the action handler
|
||||
this._actionManager.data = {widget: this};
|
||||
|
||||
// Link the actions to the DOM
|
||||
this._link_actions(actions);
|
||||
}
|
||||
|
||||
get actions()
|
||||
{
|
||||
return this._actionManager?.children || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Link the EGroupware actions to the DOM nodes / widget bits.
|
||||
*
|
||||
* @param {object} actions {ID: {attributes..}+} map of egw action information
|
||||
*/
|
||||
protected _link_actions(actions)
|
||||
{
|
||||
// Get the top level element for the tree
|
||||
const objectManager = egw_getAppObjectManager(true);
|
||||
let widget_object = objectManager.getObjectById(this.id);
|
||||
|
||||
if(widget_object == null)
|
||||
{
|
||||
// Add a new container to the object manager which will hold the widget
|
||||
// objects
|
||||
widget_object = objectManager.insertObject(false, new egwActionObject(
|
||||
this.id, objectManager, (new Et2ActionObjectForWidget(this)).getAOI(),
|
||||
this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
widget_object.setAOI((new Et2ActionObjectForWidget(this, this.getDOMNode())).getAOI());
|
||||
}
|
||||
|
||||
// Delete all old objects
|
||||
widget_object.clear();
|
||||
widget_object.unregisterActions();
|
||||
|
||||
// Go over the widget & add links - this is where we decide which actions are
|
||||
// 'allowed' for this widget at this time
|
||||
widget_object.updateActionLinks(this._get_action_links(actions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all action-links / id's of 1.-level actions from a given action object
|
||||
*
|
||||
* This can be overwritten to not allow all actions, by not returning them here.
|
||||
*
|
||||
* @param actions
|
||||
* @returns {Array}
|
||||
*/
|
||||
_get_action_links(actions)
|
||||
{
|
||||
const action_links = [];
|
||||
for(let i in actions)
|
||||
{
|
||||
let action = actions[i];
|
||||
action_links.push(typeof action.id != 'undefined' ? action.id : i);
|
||||
}
|
||||
return action_links;
|
||||
}
|
||||
}
|
||||
|
||||
// Add some more stuff in
|
||||
@ -1627,3 +1757,61 @@ export function cssImage(image_name : string, app_name? : string)
|
||||
return css``;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The egw_action system requires an egwActionObjectInterface Interface implementation
|
||||
* to tie actions to DOM nodes. This one can be used by any widget.
|
||||
*
|
||||
*
|
||||
* @param {et2_DOMWidget} widget
|
||||
* @param {Object} node
|
||||
*
|
||||
*/
|
||||
export class Et2ActionObjectForWidget
|
||||
{
|
||||
aoi : egwActionObjectInterface;
|
||||
|
||||
constructor(_widget : LitElement, _node? : HTMLElement)
|
||||
{
|
||||
const widget = _widget;
|
||||
const objectNode = _node;
|
||||
this.aoi = new egwActionObjectInterface();
|
||||
this.aoi['getWidget'] = function()
|
||||
{
|
||||
return widget;
|
||||
};
|
||||
this.aoi.doGetDOMNode = function()
|
||||
{
|
||||
return widget
|
||||
};
|
||||
|
||||
// _outerCall may be used to determine, whether the state change has been
|
||||
// evoked from the outside and the stateChangeCallback has to be called
|
||||
// or not.
|
||||
this.aoi.doSetState = function(_state, _outerCall)
|
||||
{
|
||||
};
|
||||
|
||||
// The doTiggerEvent function may be overritten by the aoi if it wants to
|
||||
// support certain action implementation specific events like EGW_AI_DRAG_OVER
|
||||
// or EGW_AI_DRAG_OUT
|
||||
this.aoi.doTriggerEvent = function(_event, _data)
|
||||
{
|
||||
switch(_event)
|
||||
{
|
||||
case EGW_AI_DRAG_OVER:
|
||||
this.widget.classList.add("ui-state-active");
|
||||
break;
|
||||
case EGW_AI_DRAG_OUT:
|
||||
this.widget.classList.remove("ui-state-active");
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
getAOI()
|
||||
{
|
||||
return this.aoi;
|
||||
}
|
||||
}
|
@ -189,7 +189,10 @@ class home_ui
|
||||
$settings = $portlet->get_properties();
|
||||
foreach($settings as $key => $setting)
|
||||
{
|
||||
if(is_array($setting) && !array_key_exists('type',$setting)) unset($settings[$key]);
|
||||
if(is_array($setting) && !array_key_exists('type', $setting))
|
||||
{
|
||||
unset($settings[$key]);
|
||||
}
|
||||
}
|
||||
$settings += $context;
|
||||
foreach(home_portlet::$common_attributes as $attr)
|
||||
@ -197,6 +200,10 @@ class home_ui
|
||||
unset($settings[$attr]);
|
||||
}
|
||||
$portlet_content['settings'] = $settings;
|
||||
if(method_exists($portlet, "get_value"))
|
||||
{
|
||||
$portlet_content['value'] = $portlet->get_value();
|
||||
}
|
||||
|
||||
// Set actions
|
||||
// Must be after settings so actions can take settings into account
|
||||
|
@ -27,7 +27,6 @@ class home_weather_portlet extends home_portlet
|
||||
{
|
||||
|
||||
const API_URL = "http://api.openweathermap.org/data/2.5/";
|
||||
const ICON_URL = 'http://openweathermap.org/img/w/';
|
||||
const API_KEY = '45484f039c5caa14d31aefe7f5514292';
|
||||
const CACHE_TIME = 3600; // Cache weather for an hour
|
||||
|
||||
@ -51,22 +50,15 @@ class home_weather_portlet extends home_portlet
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function exec($id = null, Etemplate &$etemplate = null)
|
||||
public function get_value()
|
||||
{
|
||||
// Allow to submit directly back here
|
||||
if(is_array($id) && $id['id'])
|
||||
{
|
||||
$id = $id['id'];
|
||||
}
|
||||
$etemplate->read('home.weather');
|
||||
|
||||
$etemplate->set_dom_id($id);
|
||||
$content = $this->context;
|
||||
$id = $this->context['id'];
|
||||
$content = array();
|
||||
$request = array(
|
||||
'units' => $this->context['units'] ? $this->context['units'] : 'metric',
|
||||
'lang' => $GLOBALS['egw_info']['user']['preferences']['common']['lang'],
|
||||
'units' => $this->context['units'] ?: 'metric',
|
||||
'lang' => $GLOBALS['egw_info']['user']['preferences']['common']['lang'],
|
||||
// Always get (& cache) 10 days, we'll cut down later
|
||||
'cnt' => 10
|
||||
'cnt' => 10
|
||||
);
|
||||
if($this->context['city_id'])
|
||||
{
|
||||
@ -78,9 +70,9 @@ class home_weather_portlet extends home_portlet
|
||||
$request['q'] = $this->context['city'];
|
||||
$content += $this->get_weather($request);
|
||||
}
|
||||
elseif ($this->context['position'])
|
||||
elseif($this->context['position'])
|
||||
{
|
||||
list($request['lat'],$request['lon']) = explode(',',$this->context['position']);
|
||||
list($request['lat'], $request['lon']) = explode(',', $this->context['position']);
|
||||
$content += $this->get_weather($request);
|
||||
}
|
||||
|
||||
@ -94,34 +86,25 @@ class home_weather_portlet extends home_portlet
|
||||
// Save updated Api\Preferences
|
||||
$portlets[$id]['city_id'] = $content['city_id'];
|
||||
$this->context['city'] = $portlets[$id]['city'] = $content['settings']['city'] =
|
||||
$content['settings']['title'] = $content['city'] = is_array($content['city']) ? $content['city']['name'] : $content['city'];
|
||||
$content['settings']['title'] = $content['city'] = is_array($content['city']) ? $content['city']['name'] : $content['city'];
|
||||
unset($portlets[$id]['position']);
|
||||
$GLOBALS['egw']->preferences->add('home', $id, $portlets[$id]);
|
||||
$GLOBALS['egw']->preferences->save_repository(True);
|
||||
}
|
||||
|
||||
// Adjust data to match portlet size
|
||||
if($this->context['height'] <= 2 && $this->context['width'] <= 3)
|
||||
{
|
||||
// Too small for the other days
|
||||
unset($content['list']);
|
||||
}
|
||||
else if ($this->context['height'] == 2 && $this->context['width'] > 3)
|
||||
{
|
||||
// Wider, but not taller
|
||||
unset($content['current']);
|
||||
}
|
||||
// Even too small for current high/low
|
||||
if($this->context['width'] < 3)
|
||||
{
|
||||
$content['current']['no_current_temp'] = true;
|
||||
}
|
||||
|
||||
|
||||
// Direct to full forecast page
|
||||
$content['attribution'] ='http://openweathermap.org/city/'.$content['city_id'];
|
||||
$content['attribution'] = 'http://openweathermap.org/city/' . $content['city_id'];
|
||||
|
||||
$etemplate->exec('home.home_weather_portlet.exec',$content,array(),array('__ALL__'=>true),array('id' =>$id));
|
||||
return [
|
||||
'color' => $this->context['color'],
|
||||
'city' => $this->context['city'],
|
||||
'display' => $this->context['display'],
|
||||
'weather' => $content
|
||||
];
|
||||
}
|
||||
|
||||
public function exec($id = null, Etemplate &$etemplate = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,10 +171,10 @@ class home_weather_portlet extends home_portlet
|
||||
{
|
||||
$massage =& $data['list'];
|
||||
|
||||
for($i = 0; $i < min(count($massage), $this->context['width']); $i++)
|
||||
for($i = 0; $i < count($data['list']); $i++)
|
||||
{
|
||||
$forecast =& $massage[$i];
|
||||
$forecast['day'] = Api\DateTime::to($forecast['dt'],'l');
|
||||
$forecast['day'] = Api\DateTime::to($forecast['dt'], 'l');
|
||||
self::format_forecast($forecast);
|
||||
}
|
||||
// Chop data to fit into portlet
|
||||
@ -223,29 +206,49 @@ class home_weather_portlet extends home_portlet
|
||||
$weather =& $data['weather'] ? $data['weather'] : $data;
|
||||
$temp =& $data['temp'] ? $data['temp'] : $data;
|
||||
|
||||
// Full URL for icon
|
||||
// Find icon
|
||||
if(is_array($weather))
|
||||
{
|
||||
foreach($weather as &$w)
|
||||
{
|
||||
$w['icon'] = static::ICON_URL . $w['icon'].'.png';
|
||||
$w['icon'] = static::get_icon($w);
|
||||
}
|
||||
}
|
||||
|
||||
// Round
|
||||
foreach(array('temp','temp_min','temp_max','min','max') as $temp_name)
|
||||
foreach(array('temp', 'temp_min', 'temp_max', 'min', 'max') as $temp_name)
|
||||
{
|
||||
if(array_key_exists($temp_name, $temp))
|
||||
{
|
||||
$temp[$temp_name] = ''.round($temp[$temp_name]);
|
||||
$temp[$temp_name] = '' . round($temp[$temp_name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an icon to represent the forecast
|
||||
*
|
||||
* We use icon names from shoelace
|
||||
* @param $weather
|
||||
* @return string
|
||||
*/
|
||||
protected static function get_icon(&$weather)
|
||||
{
|
||||
$icon = "question-lg";
|
||||
switch(strtolower($weather['main']))
|
||||
{
|
||||
case 'clear' :
|
||||
$icon = 'sun';
|
||||
break;
|
||||
default:
|
||||
$icon = strtolower($weather['main']);
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
public function get_actions()
|
||||
{
|
||||
$actions = array(
|
||||
);
|
||||
$actions = array();
|
||||
return $actions;
|
||||
}
|
||||
|
||||
@ -270,9 +273,9 @@ class home_weather_portlet extends home_portlet
|
||||
$properties = parent::get_properties();
|
||||
|
||||
$properties[] = array(
|
||||
'name' => 'city',
|
||||
'type' => 'textbox',
|
||||
'label' => lang('Location'),
|
||||
'name' => 'city',
|
||||
'type' => 'textbox',
|
||||
'label' => lang('Location'),
|
||||
);
|
||||
return $properties;
|
||||
}
|
||||
@ -280,9 +283,14 @@ class home_weather_portlet extends home_portlet
|
||||
public function get_description()
|
||||
{
|
||||
return array(
|
||||
'displayName'=> lang('Weather'),
|
||||
'title'=> $this->context['city'],
|
||||
'description'=> lang('Weather')
|
||||
'displayName' => lang('Weather'),
|
||||
'title' => $this->context['city'],
|
||||
'description' => lang('Weather')
|
||||
);
|
||||
}
|
||||
|
||||
public function get_type()
|
||||
{
|
||||
return 'et2-portlet-weather';
|
||||
}
|
||||
}
|
173
home/js/Et2PortletWeather.ts
Normal file
173
home/js/Et2PortletWeather.ts
Normal file
@ -0,0 +1,173 @@
|
||||
import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
|
||||
import {classMap, css, html, nothing, repeat, TemplateResult} from "@lion/core";
|
||||
import shoelace from "../../api/js/etemplate/Styles/shoelace";
|
||||
import {SelectOption} from "../../api/js/etemplate/Et2Select/FindSelectOptions";
|
||||
|
||||
/**
|
||||
* Show current and forecast weather
|
||||
*/
|
||||
export class Et2PortletWeather extends Et2Portlet
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
return [
|
||||
...shoelace,
|
||||
...(super.styles || []),
|
||||
css`
|
||||
.portlet--weather {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.day__forecast {
|
||||
width: fit-content;
|
||||
min-width: 12ex;
|
||||
max-width: 20ex;
|
||||
}
|
||||
|
||||
.temperature__day-label {
|
||||
text-align: center;
|
||||
font-size: 120%;
|
||||
padding-bottom: var(--sl-spacing-medium);
|
||||
}
|
||||
|
||||
.temperature {
|
||||
font-size: 160%;
|
||||
}
|
||||
|
||||
.temperature__high_low {
|
||||
}
|
||||
|
||||
sl-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.portlet--weather .temperature__current {
|
||||
/* Make current day a little bigger */
|
||||
font-size: 180%;
|
||||
padding: var(--sl-spacing-large);
|
||||
}
|
||||
|
||||
.temperature__current .day__forecast {
|
||||
padding: var(--sl-spacing-medium) 0px;
|
||||
}
|
||||
|
||||
:host([style*="span 1"]) .temperature__current {
|
||||
/* No padding if portlet is small */
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.portlet--weather .temperature__current sl-icon {
|
||||
font-size: 250%;
|
||||
}
|
||||
|
||||
.temperature__day-list {
|
||||
flex: 1 1 auto;
|
||||
display: grid;
|
||||
gap: var(--sl-spacing-x-large) var(--sl-spacing-medium);
|
||||
grid-template-columns: repeat(auto-fill, minmax(12ex, 1fr));
|
||||
padding-top: var(--sl-spacing-large);
|
||||
}
|
||||
|
||||
.temperature__day-list .weather__day-forecast {
|
||||
min-height: 12ex;
|
||||
}
|
||||
`
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of user-configurable properties
|
||||
* @returns {[{name : string, type : string, select_options? : [SelectOption]}]}
|
||||
*/
|
||||
get portletProperties() : { name : string, type : string, label : string, select_options? : SelectOption[] }[]
|
||||
{
|
||||
return [
|
||||
...super.portletProperties,
|
||||
{
|
||||
'name': 'city',
|
||||
'type': 'et2-textbox',
|
||||
'label': this.egw().lang('Location'),
|
||||
}
|
||||
/* Use size to control what we show
|
||||
{
|
||||
name: "display", label: "Display", type: "et2-select", select_options: [
|
||||
{'value': 'today', 'label': this.egw().lang('today')},
|
||||
{'value': '3', 'label': this.egw().lang('%1 day', 3)},
|
||||
{'value': '10', 'label': this.egw().lang('%1 day', 10)},
|
||||
]
|
||||
}
|
||||
*/
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Template for one day of the forecast
|
||||
* @param day
|
||||
* @protected
|
||||
*/
|
||||
protected forecastDayTemplate(day)
|
||||
{
|
||||
return html`
|
||||
<div class="weather__day-forecast">
|
||||
<et2-description class="temperature__day-label" value="${day.day}"></et2-description>
|
||||
<et2-hbox part="day" class="day__forecast">
|
||||
<sl-icon class="weather_icon" name="${day.weather[0].icon}"></sl-icon>
|
||||
${(typeof day.temp?.temp != "undefined") ? html`
|
||||
<et2-hbox class="temperature">
|
||||
<span>${day.temp.temp}</span>
|
||||
</et2-hbox>` : nothing
|
||||
}
|
||||
<et2-vbox class="temperature__high_low">
|
||||
<span class="temperature__max">${day.temp.max}</span>
|
||||
<span class="temperature__min">${day.temp.min}</span>
|
||||
</et2-vbox>
|
||||
</et2-hbox>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
bodyTemplate() : TemplateResult
|
||||
{
|
||||
const doList = parseInt(getComputedStyle(this).width) > 300;
|
||||
const current = this.settings?.weather?.current || {weather: [{icon: "question-lg"}], temp: {temp: "?"}};
|
||||
|
||||
// Get the forecast, excluding today
|
||||
let list = this.settings.weather && (Object.values(this.settings?.weather?.list).slice(1) || []);
|
||||
|
||||
return html`
|
||||
<div
|
||||
part="base"
|
||||
class=${classMap({
|
||||
portlet: true,
|
||||
"portlet--weather": true
|
||||
})}
|
||||
>
|
||||
<div part="current" class="temperature__current">
|
||||
${this.forecastDayTemplate({
|
||||
...{
|
||||
day: 'Today',
|
||||
// Current has a different data format
|
||||
temp: {
|
||||
min: current.temp.temp_min,
|
||||
max: current.temp.temp_max
|
||||
}, ...current
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
${doList ? html`
|
||||
<div part="list" class="temperature__day-list">
|
||||
${repeat(list, (item, index) =>
|
||||
{
|
||||
return this.forecastDayTemplate(item);
|
||||
})}
|
||||
</div>` : nothing
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!customElements.get("et2-portlet-weather"))
|
||||
{
|
||||
customElements.define("et2-portlet-weather", Et2PortletWeather);
|
||||
}
|
@ -16,6 +16,7 @@ import {Et2PortletFavorite} from "./Et2PortletFavorite";
|
||||
import {loadWebComponent} from "../../api/js/etemplate/Et2Widget/Et2Widget";
|
||||
import "./Et2PortletList";
|
||||
import "./Et2PortletNote";
|
||||
import './Et2PortletWeather';
|
||||
import "../../calendar/js/Et2PortletCalendar"
|
||||
import Sortable from "sortablejs/modular/sortable.complete.esm.js";
|
||||
|
||||
|
@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2.0//EN" "https://www.egroupware.org/etemplate2.0.dtd">
|
||||
<overlay>
|
||||
<template id="home.weather" template="" lang="" group="0" version="1.9.001">
|
||||
<grid id="current" disabled="!@current" width="100%">
|
||||
<columns>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<et2-image class="weather_icon" src="weather[0][icon]"></et2-image>
|
||||
<et2-hbox id="temp">
|
||||
<et2-description class="current temperature" id="temp" noLang="true"></et2-description>
|
||||
</et2-hbox>
|
||||
<et2-vbox id="temp" disabled="@no_current_temp">
|
||||
<et2-description class="high_low temperature" id="max" noLang="true"></et2-description>
|
||||
<et2-description class="high_low temperature" id="min" noLang="true"></et2-description>
|
||||
</et2-vbox>
|
||||
</row>
|
||||
<row disabled="!@weather[0][description]">
|
||||
<et2-description id="weather[0][description]" noLang="true"></et2-description>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<et2-box id="list" class="forecast" disabled="!@list" width="100%">
|
||||
<!-- Box wrapper needed to get box to auto-repeat -->
|
||||
<et2-box id="${row}">
|
||||
<grid width="100%">
|
||||
<columns>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row><et2-description align="center" id="day"></et2-description></row>
|
||||
<row class="weather_icon"><et2-image align="center" class="weather_icon" src="weather[0][icon]"></et2-image></row>
|
||||
<row>
|
||||
<et2-vbox align="center" id="temp">
|
||||
<et2-description class="high_low temperature" id="max" noLang="true"></et2-description>
|
||||
<et2-description class="high_low temperature" id="min" noLang="true"></et2-description>
|
||||
</et2-vbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</et2-box>
|
||||
</et2-box>
|
||||
<et2-description align="center" class="attribution" href="@attribution" value="openweathermap.org" activateLinks="true" extraLinkTarget="_blank"></et2-description>
|
||||
</template>
|
||||
</overlay>
|
Loading…
Reference in New Issue
Block a user