pengyuthon wechat
@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -1,96 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "anita_theme_setting",
|
||||
|
||||
'summary': """
|
||||
anita theme
|
||||
advance theme
|
||||
sunsine theme
|
||||
mnk theme for odoo
|
||||
free backend theme
|
||||
odoo theme
|
||||
owl theme
|
||||
multi mode theme
|
||||
free theme
|
||||
multi style theme
|
||||
button style theme
|
||||
crazy theme
|
||||
nice theme
|
||||
mutli tab theme
|
||||
multi layout theme
|
||||
mult tab style theme
|
||||
""",
|
||||
|
||||
'description': """
|
||||
anita theme setting for odoo, it is a powerfull theme
|
||||
""",
|
||||
|
||||
'author': "OpenERPnext",
|
||||
'website': "https://www.openerpnext.com",
|
||||
'live_test_url': 'https://www.openerpnext.com',
|
||||
'images': [
|
||||
'static/description/screen_shot.png',
|
||||
'static/description/banner.png',
|
||||
],
|
||||
'license': 'OPL-1',
|
||||
|
||||
'category': 'Themes/Backend',
|
||||
'version': '17.0.0.3',
|
||||
'price': 999,
|
||||
|
||||
'depends': ['web', 'web_enterprise'],
|
||||
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
|
||||
# settings, setting page add by modules
|
||||
'views/anita_theme_mode.xml',
|
||||
'views/anita_theme_style.xml',
|
||||
'views/anita_theme_style_group.xml',
|
||||
'views/anita_theme_style_item.xml',
|
||||
'views/anita_theme_style_item_sub_group.xml',
|
||||
'views/anita_theme_var.xml',
|
||||
'views/anita_import_theme_style.xml',
|
||||
'views/anita_web.xml',
|
||||
'views/anita_bk_image_wizard.xml',
|
||||
'views/anita_res_setting.xml',
|
||||
],
|
||||
|
||||
'assets': {
|
||||
|
||||
'web.assets_backend': [
|
||||
|
||||
# aniate.css
|
||||
'anita_theme_setting/static/libs/animate.css',
|
||||
|
||||
# theme customizer
|
||||
'anita_theme_setting/static/src/components/customizer/*',
|
||||
'anita_theme_setting/static/src/components/settings/*.js',
|
||||
'anita_theme_setting/static/src/components/settings/*.xml',
|
||||
|
||||
'anita_theme_setting/static/src/anita_theme_setting.js',
|
||||
|
||||
# according
|
||||
'anita_theme_setting/static/src/components/accordion/*',
|
||||
|
||||
# dialog
|
||||
'anita_theme_setting/static/src/components/dialog/*',
|
||||
|
||||
# color picker
|
||||
'anita_theme_setting/static/src/components/color_picker/color_picker.js',
|
||||
'anita_theme_setting/static/src/components/color_picker/color_picker.xml',
|
||||
'anita_theme_setting/static/src/components/color_picker/color_picker.scss',
|
||||
|
||||
'anita_theme_setting/static/libs/spectrum.js',
|
||||
'anita_theme_setting/static/libs/spectrum.css',
|
||||
'anita_theme_setting/static/src/anita_icon.js',
|
||||
|
||||
# client
|
||||
'anita_theme_setting/static/src/components/webclient/webclient.js',
|
||||
],
|
||||
|
||||
'web.assets_web': [
|
||||
('replace', 'web_enterprise/static/src/main.js', 'anita_theme_setting/static/src/anita_main.js'),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import anita_home
|
||||
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import odoo
|
||||
import odoo.modules.registry
|
||||
from odoo.tools.translate import _
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.addons.web.controllers.home import Home, SIGN_UP_REQUEST_PARAMS
|
||||
from odoo.addons.web.controllers.utils import ensure_db, is_user_internal
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from odoo.tools.misc import file_path
|
||||
|
||||
try:
|
||||
from werkzeug.utils import send_file
|
||||
except ImportError:
|
||||
from odoo.tools._vendor.send_file import send_file
|
||||
|
||||
import json
|
||||
import base64
|
||||
import functools
|
||||
import io
|
||||
import os
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
BASE_PATH = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
class anitaHome(Home):
|
||||
'''
|
||||
inherit home to extend web.login style
|
||||
'''
|
||||
@http.route([
|
||||
'/web/binary/company_logo',
|
||||
'/logo',
|
||||
'/logo.png',
|
||||
], type='http', auth="none", cors="*")
|
||||
def company_logo(self, dbname=None, **kw):
|
||||
imgname = 'logo'
|
||||
imgext = '.png'
|
||||
dbname = request.db
|
||||
uid = (request.session.uid if dbname else None) or odoo.SUPERUSER_ID
|
||||
|
||||
if not dbname:
|
||||
response = http.Stream.from_path(file_path('web/static/img/logo.png')).get_response()
|
||||
else:
|
||||
try:
|
||||
# create an empty registry
|
||||
registry = odoo.modules.registry.Registry(dbname)
|
||||
with registry.cursor() as cr:
|
||||
company = int(kw['company']) if kw and kw.get('company') else False
|
||||
if company:
|
||||
cr.execute("""SELECT logo_web, write_date
|
||||
FROM res_company
|
||||
WHERE id = %s
|
||||
""", (company,))
|
||||
else:
|
||||
cr.execute("""SELECT c.logo_web, c.write_date
|
||||
FROM res_users u
|
||||
LEFT JOIN res_company c
|
||||
ON c.id = u.company_id
|
||||
WHERE u.id = %s
|
||||
""", (uid,))
|
||||
row = cr.fetchone()
|
||||
if row and row[0]:
|
||||
image_base64 = base64.b64decode(row[0])
|
||||
image_data = io.BytesIO(image_base64)
|
||||
mimetype = guess_mimetype(image_base64, default='image/png')
|
||||
imgext = '.' + mimetype.split('/')[1]
|
||||
if imgext == '.svg+xml':
|
||||
imgext = '.svg'
|
||||
response = send_file(
|
||||
image_data,
|
||||
request.httprequest.environ,
|
||||
download_name=imgname + imgext,
|
||||
mimetype=mimetype,
|
||||
last_modified=row[1],
|
||||
response_class=Response,
|
||||
)
|
||||
else:
|
||||
response = http.Stream.from_path(file_path('web/static/img/nologo.png')).get_response()
|
||||
except Exception:
|
||||
response = http.Stream.from_path(file_path(f'web/static/img/{imgname}{imgext}')).get_response()
|
||||
|
||||
return response
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<function model="anita_theme_setting.mode_template" name="install_template">
|
||||
<value eval="'template1'"/>
|
||||
<value eval="'/anita_theme_setting/static/template/template1.scss'"/>
|
||||
<value eval="['/anita_theme_setting/static/json/white_mode.json', '/anita_theme_setting/static/json/dark_mode.json']"/>
|
||||
</function>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import anita_setting_base
|
||||
from . import anita_template_var_vals
|
||||
from . import anita_theme_mode
|
||||
from . import anita_theme_style
|
||||
from . import anita_theme_style_item_group
|
||||
from . import anita_theme_style_item
|
||||
from . import anita_company
|
||||
from . import anita_theme_users
|
||||
from . import anita_theme_vars
|
||||
from . import anita_setting_manager
|
||||
from . import anita_res_setting
|
||||
from . import anita_default_mode_data
|
||||
from . import anita_mode_template
|
||||
from . import anita_theme_style_item_sub_group
|
||||
from . import anita_import_theme_style
|
||||
from . import anita_default_menu_icon
|
||||
from . import anita_menu_extend
|
||||
from . import anita_favorite_menu
|
||||
from . import anita_bk_image_wizard
|
||||
@@ -1,14 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class AnitaBkImageWizard(models.TransientModel):
|
||||
'''
|
||||
background image wizard
|
||||
'''
|
||||
_name = 'anita_theme_setting.background_image_wizard'
|
||||
_description = 'background image wizard'
|
||||
|
||||
data = fields.Binary(string='data', required=True)
|
||||
opacity = fields.Float(string='theme opacity', default=0.8)
|
||||
@@ -1,130 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import fields, models, exceptions
|
||||
import base64
|
||||
from datetime import date, datetime
|
||||
from odoo.tools.misc import file_path
|
||||
|
||||
|
||||
class AnitaCompany(models.Model):
|
||||
'''
|
||||
extend to support current mode and current style
|
||||
'''
|
||||
_inherit = "res.company"
|
||||
_name = 'res.company'
|
||||
|
||||
setting_id = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.setting_base",
|
||||
ondelete="cascade",
|
||||
string="setting id")
|
||||
|
||||
def _get_default_favicon_icon(self):
|
||||
"""
|
||||
get default favicon icon form static path
|
||||
"""
|
||||
tmp_path = file_path('anita_theme_setting/static/src/img/favicon.png')
|
||||
return base64.b64encode(open(tmp_path, 'rb').read())
|
||||
|
||||
favicon_icon = fields.Binary(
|
||||
string='Web Favicon Icon', default='_get_default_favicon_icon')
|
||||
|
||||
def _get_default_small_logo(self):
|
||||
'''
|
||||
get the default small logo
|
||||
:return:
|
||||
'''
|
||||
tmp_path = file_path('anita_theme_setting/static/src/img/res_small_logo.png')
|
||||
return base64.b64encode(open(tmp_path, 'rb').read())
|
||||
|
||||
logo_small = fields.Binary(
|
||||
default=_get_default_small_logo, string="Company Small Logo")
|
||||
|
||||
def get_mode_data(self):
|
||||
'''
|
||||
get mode data
|
||||
:return:
|
||||
'''
|
||||
owner = 'res.company, {company_id}'.format(
|
||||
company_id=self.env.user.company_id)
|
||||
self.env["anita_theme_setting.theme_mode"].check_default_mode_data(owner)
|
||||
return self.env["anita_theme_setting.theme_mode"].get_mode_data()
|
||||
|
||||
def get_mode_domain(self):
|
||||
"""
|
||||
get mode domain dynamic
|
||||
:return:
|
||||
"""
|
||||
owner = 'res.company, {company_id}'.format(
|
||||
company_id=self.env.user.company_id.id)
|
||||
return [('owner', '=', owner)]
|
||||
|
||||
def get_company_mode_data(self):
|
||||
'''
|
||||
get company mode data
|
||||
:return:
|
||||
'''
|
||||
return self.get_mode_data()
|
||||
|
||||
def ensure_setting_id(self):
|
||||
'''
|
||||
check setting id
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
if self.setting_id:
|
||||
return
|
||||
|
||||
# copy use the global default data
|
||||
values = self.env['res.config.settings'].get_theme_settings()
|
||||
|
||||
# get fields of anita_theme_setting.setting_base
|
||||
field_names = [name for name, field in self.setting_id._fields.items() if name not in [
|
||||
'id', 'create_uid', 'create_date', 'write_uid', 'write_date']]
|
||||
|
||||
# remove name not in fields
|
||||
values = {k: v for k, v in values.items() if k in field_names}
|
||||
|
||||
del values["current_theme_mode"]
|
||||
del values["current_theme_style"]
|
||||
|
||||
if not self.setting_id:
|
||||
tmp_record = self.env["anita_theme_setting.setting_base"].create(values)
|
||||
self.setting_id = tmp_record.id
|
||||
|
||||
return self.setting_id
|
||||
|
||||
def get_theme_setting(self):
|
||||
'''
|
||||
get company setting
|
||||
:return:
|
||||
'''
|
||||
record = self.env.user.company_id
|
||||
record.ensure_setting_id()
|
||||
result = record.setting_id.read()[0]
|
||||
result["current_theme_mode"] = record.setting_id.current_theme_mode.id
|
||||
result["current_theme_style"] = record.setting_id.current_theme_style.id
|
||||
for key, item in result.items():
|
||||
if isinstance(item, datetime):
|
||||
result[key] = fields.Datetime.to_string(item)
|
||||
if isinstance(item, date):
|
||||
result[key] = fields.Date.to_string(item)
|
||||
|
||||
return result
|
||||
|
||||
def check_default_data(self):
|
||||
"""
|
||||
check default data
|
||||
:return:
|
||||
"""
|
||||
# check mode data
|
||||
owner = 'res.company, {company_id}'.format(company_id=self.id)
|
||||
self.env["anita_theme_setting.theme_mode"].check_default_mode_data(owner)
|
||||
|
||||
def save_theme_setting(self, settings):
|
||||
'''
|
||||
save theme setting
|
||||
:param settings:
|
||||
:return:
|
||||
'''
|
||||
self.ensure_setting_id()
|
||||
self.setting_id.write(settings)
|
||||
@@ -1,147 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions
|
||||
from odoo.tools import ormcache
|
||||
import json
|
||||
from odoo.tools.misc import file_path
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaDefaultMode(models.Model):
|
||||
'''
|
||||
default mode
|
||||
'''
|
||||
_name = 'anita_theme_setting.default_mode_data'
|
||||
_description = 'anita Theme Setting Mode Template'
|
||||
|
||||
name = fields.Char(string="Name", required=True)
|
||||
data = fields.Text(string="Content", required=True)
|
||||
template_var_vals = fields.Text(string="Template Var Vals")
|
||||
version = fields.Char(string="Version", required=True)
|
||||
config_path = fields.Char(string="Config Path", help="This is the file path of the template")
|
||||
|
||||
_sql_constraints = [('name_unique', 'UNIQUE(name)', "Name Must Be Unique!")]
|
||||
|
||||
@ormcache('self.id')
|
||||
def get_mode_data(self):
|
||||
"""
|
||||
get mode
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
mode_data = json.loads(self.data)
|
||||
mode_data.update({
|
||||
"name": self.name,
|
||||
"version": self.version
|
||||
})
|
||||
return mode_data
|
||||
|
||||
@ormcache('self.id')
|
||||
def get_var_val_cache(self):
|
||||
"""
|
||||
get var value cache
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
try:
|
||||
var_vals = self.template_var_vals
|
||||
var_vals = var_vals.replace('\'', '\"')
|
||||
data = json.loads(var_vals)
|
||||
except Exception as e:
|
||||
_logger.error(e)
|
||||
data = {}
|
||||
|
||||
var_val_cache = {}
|
||||
for group_name in data:
|
||||
group = data[group_name]
|
||||
for var_item in group:
|
||||
var_val_cache[var_item['name']] = var_item['value']
|
||||
return var_val_cache
|
||||
|
||||
@ormcache()
|
||||
def get_default_modes(self):
|
||||
"""
|
||||
get default modes
|
||||
:return:
|
||||
"""
|
||||
records = self.search([])
|
||||
default_models = []
|
||||
for record in records:
|
||||
default_models.append(record.get_mode_data())
|
||||
return default_models
|
||||
|
||||
@api.model
|
||||
def refresh_default_mode(self):
|
||||
"""
|
||||
udpate data
|
||||
"""
|
||||
records = self.search([])
|
||||
for record in records:
|
||||
if not record.config_path:
|
||||
return
|
||||
|
||||
tmp_path = file_path(record.config_path.lstrip('/'))
|
||||
if not tmp_path:
|
||||
continue
|
||||
with open(tmp_path, 'r') as fd:
|
||||
mode_text = fd.read()
|
||||
|
||||
# check the data is valid json
|
||||
try:
|
||||
mode_data = json.loads(mode_text)
|
||||
record.write({
|
||||
"data": json.dumps(mode_data),
|
||||
"template_var_vals": json.dumps(mode_data.get('template_var_vals', {}))
|
||||
})
|
||||
except Exception as e:
|
||||
self.data = False
|
||||
_logger.error(e)
|
||||
|
||||
@api.model
|
||||
def install_mode(self, config_path, template):
|
||||
"""
|
||||
install mode
|
||||
:return:
|
||||
"""
|
||||
# trim start '/'
|
||||
tmp_path = file_path(config_path.lstrip('/'))
|
||||
|
||||
if not tmp_path:
|
||||
raise exceptions.ValidationError('File not found! {}'.format(config_path))
|
||||
|
||||
with open(tmp_path, 'r') as fd:
|
||||
|
||||
mode_text = fd.read()
|
||||
|
||||
# check the data is valid json
|
||||
try:
|
||||
mode_data = json.loads(mode_text)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(
|
||||
'Template {template} must be a valid json file!'.format(template=config_path))
|
||||
|
||||
name = mode_data.get('name')
|
||||
if not name:
|
||||
raise exceptions.ValidationError(
|
||||
'Template {template} must has a name!'.format(template=config_path))
|
||||
|
||||
model_record = self.search([('name', '=', name)])
|
||||
if not model_record:
|
||||
self.create([{
|
||||
"name": name,
|
||||
"data": json.dumps(mode_data),
|
||||
"version": mode_data.get('version', '1.0'),
|
||||
"config_path": config_path,
|
||||
"template_var_vals": mode_data.get("template_var_vals", "")
|
||||
}])
|
||||
else:
|
||||
model_record.write({
|
||||
"name": name,
|
||||
"data": json.dumps(mode_data),
|
||||
"version": mode_data.get('version', '1.0'),
|
||||
"config_path": config_path,
|
||||
"template_var_vals": mode_data.get("template_var_vals", "")
|
||||
})
|
||||
return model_record
|
||||
@@ -1,53 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class AnitaFavoriteMenu(models.Model):
|
||||
'''
|
||||
user favorite menu
|
||||
'''
|
||||
_name = 'anita_theme_setting.favorite_menu'
|
||||
_order = "sequence"
|
||||
_description = "user favorite menu"
|
||||
|
||||
def _default_sequence(self):
|
||||
return self.env['ir.sequence'].next_by_code('anita_theme_setting.favorite.menu')
|
||||
|
||||
sequence = fields.Integer(
|
||||
string="sequence",
|
||||
default=_default_sequence)
|
||||
|
||||
menu_id = fields.Many2one(
|
||||
comodel_name='ir.ui.menu',
|
||||
string='Favorite Menu',
|
||||
required=True,
|
||||
ondelete='cascade')
|
||||
|
||||
user_id = fields.Many2one(
|
||||
comodel_name='res.users',
|
||||
string='User Name',
|
||||
default=lambda self: self.env.user.id)
|
||||
|
||||
@api.model
|
||||
def unlink_menu_id(self, user_id, menu_id):
|
||||
'''
|
||||
unlink
|
||||
:param user_id:
|
||||
:param menu_id:
|
||||
:return:
|
||||
'''
|
||||
value = self.search(
|
||||
[('menu_id', '=', menu_id), ('user_id', '=', user_id)])
|
||||
return value.unlink()
|
||||
|
||||
@api.model
|
||||
def update_favorite_menu(self, favorites):
|
||||
'''
|
||||
update favoirte
|
||||
:return:
|
||||
'''
|
||||
records = self.search([('user_id', '=', self.env.uid)])
|
||||
records.unlink()
|
||||
self.create(favorites)
|
||||
return self.search_read([('user_id', '=', self.env.uid)])
|
||||
@@ -1,13 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class AnitaImportThemeStyle(models.TransientModel):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.import_theme_style'
|
||||
_description = 'user setting'
|
||||
|
||||
file = fields.Binary(string="Theme File", required=True)
|
||||
@@ -1,244 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo.tools.convert import xml_import, nodeattr2bool
|
||||
import odoo
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.osv import expression
|
||||
from .anita_default_menu_icon import svg_icons, font_icons
|
||||
|
||||
|
||||
class AnitaMenuExtend(models.Model):
|
||||
'''
|
||||
extend to support font icon and svg icon
|
||||
'''
|
||||
_inherit = 'ir.ui.menu'
|
||||
|
||||
font_icon = fields.Char(string='font icon')
|
||||
svg_icon = fields.Text(string='svg icon')
|
||||
|
||||
def load_web_menus(self, debug):
|
||||
""" Loads all menu items (all applications and their sub-menus) and
|
||||
processes them to be used by the webclient. Mainly, it associates with
|
||||
each application (top level menu) the action of its first child menu
|
||||
that is associated with an action (recursively), i.e. with the action
|
||||
to execute when the opening the app.
|
||||
|
||||
:return: the menus (including the images in Base64)
|
||||
"""
|
||||
theme_setting = self.env["anita_theme_setting.setting_manager"].sudo().get_theme_setting()
|
||||
icon_policy = theme_setting.get("icon_policy", "svg_icon")
|
||||
|
||||
menus = self.load_menus(debug)
|
||||
favorite_cache = {}
|
||||
if theme_setting.get("favorite_mode", False):
|
||||
favorite_apps = self.env['anita_theme_setting.favorite_menu'].sudo().search(
|
||||
[('user_id', '=', self.env.uid)])
|
||||
favorite_cache = {fav.menu_id.id: fav for fav in favorite_apps}
|
||||
|
||||
web_menus = {}
|
||||
for menu in menus.values():
|
||||
if not menu['id']:
|
||||
# special root menu case
|
||||
web_menus['root'] = {
|
||||
"id": 'root',
|
||||
"name": menu['name'],
|
||||
"children": menu['children'],
|
||||
"appID": False,
|
||||
"xmlid": "",
|
||||
"actionID": False,
|
||||
"actionModel": False,
|
||||
"webIcon": None,
|
||||
"webIconData": None,
|
||||
"backgroundImage": menu.get('backgroundImage'),
|
||||
"icon_policy": icon_policy,
|
||||
}
|
||||
else:
|
||||
action = menu['action']
|
||||
|
||||
if menu['id'] == menu['app_id']:
|
||||
# if it's an app take action of first (sub)child having one defined
|
||||
child = menu
|
||||
while child and not action:
|
||||
action = child['action']
|
||||
child = menus[child['children'][0]] if child['children'] else False
|
||||
|
||||
action_model, action_id = action.split(',') if action else (False, False)
|
||||
action_id = int(action_id) if action_id else False
|
||||
|
||||
web_menus[menu['id']] = {
|
||||
"id": menu['id'],
|
||||
"name": menu['name'],
|
||||
"children": menu['children'],
|
||||
"appID": menu['app_id'],
|
||||
"xmlid": menu['xmlid'],
|
||||
"actionID": action_id,
|
||||
"actionModel": action_model,
|
||||
"svg_icon": menu.get('svg_icon'),
|
||||
"font_icon": menu.get('font_icon'),
|
||||
"webIcon": menu['web_icon'],
|
||||
"webIconData": menu['web_icon_data'],
|
||||
"is_favorite": True if favorite_cache.get(menu['id']) else False,
|
||||
"sequence": favorite_cache.get(menu['id'], {}).sequence if favorite_cache.get(menu['id']) else 0
|
||||
}
|
||||
|
||||
return web_menus
|
||||
|
||||
@api.model
|
||||
# @tools.ormcache_context('self._uid', 'debug', keys=('lang',))
|
||||
def load_menus(self, debug):
|
||||
""" Loads all menu items (all applications and their sub-menus).
|
||||
:return: the menu root
|
||||
:rtype: dict('children': menu_nodes)
|
||||
"""
|
||||
theme_setting = self.env["anita_theme_setting.setting_manager"].sudo().get_theme_setting()
|
||||
icon_policy = theme_setting.get("icon_policy", "svg_icon")
|
||||
|
||||
fields = ['name', 'sequence', 'parent_id', 'action', 'web_icon']
|
||||
if icon_policy == "font_icon":
|
||||
fields.append('font_icon')
|
||||
elif icon_policy == "svg_icon":
|
||||
fields.append('svg_icon')
|
||||
else:
|
||||
fields.append('web_icon')
|
||||
fields.append('web_icon_data')
|
||||
|
||||
menu_roots = self.get_user_roots()
|
||||
menu_roots_data = menu_roots.read(fields) if menu_roots else []
|
||||
|
||||
menu_root = {
|
||||
'id': False,
|
||||
'name': 'root',
|
||||
'parent_id': [-1, ''],
|
||||
'children': [menu['id'] for menu in menu_roots_data],
|
||||
}
|
||||
|
||||
all_menus = {'root': menu_root}
|
||||
|
||||
if not menu_roots_data:
|
||||
return all_menus
|
||||
|
||||
# menus are loaded fully unlike a regular tree view, cause there are a
|
||||
# limited number of items (752 when all 6.1 addons are installed)
|
||||
menus_domain = [('id', 'child_of', menu_roots.ids)]
|
||||
blacklisted_menu_ids = self._load_menus_blacklist()
|
||||
if blacklisted_menu_ids:
|
||||
menus_domain = expression.AND([menus_domain, [('id', 'not in', blacklisted_menu_ids)]])
|
||||
menus = self.search(menus_domain)
|
||||
menu_items = menus.read(fields)
|
||||
xmlids = (menu_roots + menus)._get_menuitems_xmlids()
|
||||
|
||||
# add roots at the end of the sequence, so that they will overwrite
|
||||
# equivalent menu items from full menu read when put into id:item
|
||||
# mapping, resulting in children being correctly set on the roots.
|
||||
menu_items.extend(menu_roots_data)
|
||||
|
||||
# set children ids and xmlids
|
||||
menu_items_map = {menu_item["id"]: menu_item for menu_item in menu_items}
|
||||
for menu_item in menu_items:
|
||||
menu_item.setdefault('children', [])
|
||||
|
||||
# set the default icons
|
||||
if icon_policy == "svg_icon":
|
||||
if not menu_item.get('svg_icon'):
|
||||
menu_item["svg_icon"] = svg_icons.get(menu_item["name"], False) or False
|
||||
elif icon_policy == "font_icon":
|
||||
if not menu_item.get('font_icon'):
|
||||
menu_item["font_icon"] = font_icons.get(menu_item["name"], False) or "fa fa-circle-o"
|
||||
|
||||
parent = menu_item['parent_id'] and menu_item['parent_id'][0]
|
||||
menu_item['xmlid'] = xmlids.get(menu_item['id'], "")
|
||||
if parent in menu_items_map:
|
||||
menu_items_map[parent].setdefault(
|
||||
'children', []).append(menu_item['id'])
|
||||
|
||||
all_menus.update(menu_items_map)
|
||||
|
||||
# sort by sequence
|
||||
for menu_id in all_menus:
|
||||
all_menus[menu_id]['children'].sort(key=lambda id: all_menus[id]['sequence'])
|
||||
|
||||
# recursively set app ids to related children
|
||||
def _set_app_id(app_id, menu):
|
||||
menu['app_id'] = app_id
|
||||
for child_id in menu['children']:
|
||||
_set_app_id(app_id, all_menus[child_id])
|
||||
|
||||
for app in menu_roots_data:
|
||||
app_id = app['id']
|
||||
_set_app_id(app_id, all_menus[app_id])
|
||||
|
||||
# filter out menus not related to an app (+ keep root menu)
|
||||
all_menus = {menu['id']: menu for menu in all_menus.values() if menu.get('app_id')}
|
||||
all_menus['root'] = menu_root
|
||||
|
||||
return all_menus
|
||||
|
||||
def _tag_menuitem_extend(self, rec, parent=None):
|
||||
"""
|
||||
extend tag menu item
|
||||
"""
|
||||
rec_id = rec.attrib["id"]
|
||||
self._test_xml_id(rec_id)
|
||||
|
||||
# The parent attribute was specified, if non-empty determine its ID, otherwise
|
||||
# explicitly make a top-level menu
|
||||
values = {
|
||||
'parent_id': False,
|
||||
'active': nodeattr2bool(rec, 'active', default=True),
|
||||
}
|
||||
|
||||
if rec.get('sequence'):
|
||||
values['sequence'] = int(rec.get('sequence'))
|
||||
|
||||
if parent is not None:
|
||||
values['parent_id'] = parent
|
||||
elif rec.get('parent'):
|
||||
values['parent_id'] = self.id_get(rec.attrib['parent'])
|
||||
elif rec.get('web_icon'):
|
||||
values['web_icon'] = rec.attrib['web_icon']
|
||||
|
||||
if rec.get('name'):
|
||||
values['name'] = rec.attrib['name']
|
||||
|
||||
if rec.get('action'):
|
||||
a_action = rec.attrib['action']
|
||||
|
||||
if '.' not in a_action:
|
||||
a_action = '%s.%s' % (self.module, a_action)
|
||||
act = self.env.ref(a_action).sudo()
|
||||
values['action'] = "%s,%d" % (act.type, act.id)
|
||||
|
||||
if not values.get('name') and act.type.endswith(('act_window', 'wizard', 'url', 'client', 'server')) and act.name:
|
||||
values['name'] = act.name
|
||||
|
||||
if not values.get('name'):
|
||||
values['name'] = rec_id or '?'
|
||||
|
||||
groups = []
|
||||
for group in rec.get('groups', '').split(','):
|
||||
if group.startswith('-'):
|
||||
group_id = self.id_get(group[1:])
|
||||
groups.append(odoo.Command.unlink(group_id))
|
||||
elif group:
|
||||
group_id = self.id_get(group)
|
||||
groups.append(odoo.Command.link(group_id))
|
||||
if groups:
|
||||
values['groups_id'] = groups
|
||||
|
||||
# read font icon
|
||||
if rec.get('font_icon'):
|
||||
values['font_icon'] = rec.get('font_icon')
|
||||
|
||||
if rec.get('svg_icon'):
|
||||
values['svg_icon'] = rec.get('svg_icon')
|
||||
|
||||
data = {
|
||||
'xml_id': self.make_xml_id(rec_id),
|
||||
'values': values,
|
||||
'noupdate': self.noupdate,
|
||||
}
|
||||
menu = self.env['ir.ui.menu']._load_records([data], self.mode == 'update')
|
||||
for child in rec.iterchildren('menuitem'):
|
||||
self._tag_menuitem(child, parent=menu.id)
|
||||
|
||||
xml_import._tag_menuitem = _tag_menuitem_extend
|
||||
@@ -1,107 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions
|
||||
import json
|
||||
from odoo.tools import ormcache
|
||||
from odoo.tools.misc import file_path
|
||||
from odoo.tools.config import config
|
||||
|
||||
|
||||
class AnitaModeTemplate(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.mode_template'
|
||||
_description = 'anita Mode Template'
|
||||
|
||||
name = fields.Char(string="Name", required=True)
|
||||
template = fields.Text(string="Template")
|
||||
color_vars = fields.Text(
|
||||
string="Color Vars", help="This is the default var values for this template")
|
||||
template_path = fields.Char(
|
||||
string="Template Path", help="This is the template path of the template")
|
||||
|
||||
_sql_constraints = [('name_unique', 'UNIQUE(name)', "Name Must Be Unique!")]
|
||||
|
||||
@ormcache()
|
||||
def get_templates(self):
|
||||
"""
|
||||
get templates
|
||||
:return:
|
||||
"""
|
||||
records = self.search([])
|
||||
return {record.name: record for record in records}
|
||||
|
||||
@ormcache('self.id')
|
||||
def get_template(self):
|
||||
"""
|
||||
get template
|
||||
:return:
|
||||
"""
|
||||
return json.loads(self.template)
|
||||
|
||||
@ormcache('self.id')
|
||||
def get_color_vars(self):
|
||||
"""
|
||||
get vars
|
||||
:return:
|
||||
"""
|
||||
return json.loads(self.color_vars)
|
||||
|
||||
def refresh_templates(self):
|
||||
"""
|
||||
update template content
|
||||
"""
|
||||
records = self.search([])
|
||||
for record in records:
|
||||
if record.template_path:
|
||||
tmp_path = file_path(record.template_path.lstrip('/'))
|
||||
with open(tmp_path, 'r') as fd:
|
||||
template = fd.read()
|
||||
record.template = template
|
||||
|
||||
def _register_hook(self):
|
||||
"""
|
||||
stuff to do right after the registry is built
|
||||
"""
|
||||
if config.get('debug', False):
|
||||
records = self.search([])
|
||||
records.refresh_templates()
|
||||
|
||||
@api.model
|
||||
def install_template(self, name, template_path, theme_mode_paths):
|
||||
"""
|
||||
install mode
|
||||
:return:
|
||||
"""
|
||||
tmp_path = file_path(template_path.lstrip('/'))
|
||||
if not tmp_path:
|
||||
raise exceptions.ValidationError("Template Path is not valid!")
|
||||
|
||||
with open(tmp_path, 'r') as fd:
|
||||
template = fd.read()
|
||||
if not name:
|
||||
raise exceptions.ValidationError(
|
||||
'Template {template} must has a name!'.format(template=template_path))
|
||||
|
||||
template_record = self.search([('name', '=', name)])
|
||||
if not template_record:
|
||||
self.create([{
|
||||
"name": name,
|
||||
"template": template,
|
||||
"template_path": template_path
|
||||
}])
|
||||
else:
|
||||
template_record.write({
|
||||
"name": name,
|
||||
"template": template,
|
||||
"template_path": template_path
|
||||
})
|
||||
|
||||
for index, mode_path in enumerate(theme_mode_paths):
|
||||
record = self.env['anita_theme_setting.default_mode_data'].install_mode(
|
||||
mode_path, template_record)
|
||||
if index == 0:
|
||||
template_record.write({
|
||||
"color_vars": record.template_var_vals
|
||||
})
|
||||
@@ -1,109 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models
|
||||
import base64
|
||||
from odoo.tools.mimetypes import guess_mimetype
|
||||
from datetime import date, datetime
|
||||
|
||||
|
||||
class AnitaUserSetting(models.TransientModel):
|
||||
'''
|
||||
anita user setting
|
||||
'''
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
theme_setting_mode = fields.Selection(
|
||||
string="theme style mode",
|
||||
config_parameter="anita_theme_setting.theme_setting_mode",
|
||||
selection=[('system', 'system'),
|
||||
('company', 'company'),
|
||||
('user', 'user')],
|
||||
default='system')
|
||||
|
||||
allow_debug = fields.Boolean(
|
||||
string="allow debug",
|
||||
config_parameter="anita_theme_setting.allow_debug",
|
||||
default=False)
|
||||
|
||||
window_default_title = fields.Char(
|
||||
string="login title",
|
||||
config_parameter="anita_theme_setting.window_default_title",
|
||||
default="Odoo")
|
||||
|
||||
powered_by = fields.Char(
|
||||
string="powered by",
|
||||
config_parameter="anita_theme_setting.powered_by",
|
||||
default="Odoo")
|
||||
|
||||
@api.model
|
||||
def get_theme_setting_mode(self):
|
||||
'''
|
||||
get theme setting mode
|
||||
:return:
|
||||
'''
|
||||
config = self.env['ir.config_parameter'].sudo()
|
||||
theme_setting_mode = config.get_param(
|
||||
key='anita_theme_setting.theme_setting_mode', default='system')
|
||||
return theme_setting_mode
|
||||
|
||||
def set_values_company_favicon(self):
|
||||
'''
|
||||
set the favicon of company
|
||||
:return:
|
||||
'''
|
||||
company = self.sudo().env['res.company']
|
||||
records = company.search([])
|
||||
|
||||
if len(records) > 0:
|
||||
for record in records:
|
||||
record.write({'favicon': self._set_web_favicon(original=True)})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_login_style(self):
|
||||
'''
|
||||
get login style
|
||||
:return:
|
||||
'''
|
||||
ir_config = self.env['ir.config_parameter'].sudo()
|
||||
login_style = ir_config.get_param(
|
||||
key='anita_theme_setting.login_style', default='login_style1')
|
||||
return login_style
|
||||
|
||||
def get_setting_record(self):
|
||||
'''
|
||||
get setting record
|
||||
:return:
|
||||
'''
|
||||
record = self.env['anita_theme_setting.setting_base'].search([('owner', '=', False)], limit=1)
|
||||
if not record:
|
||||
record = self.env['anita_theme_setting.setting_base'].create({'owner': False})
|
||||
return record
|
||||
|
||||
@api.model
|
||||
def save_theme_setting(self, settings):
|
||||
"""
|
||||
save settings
|
||||
"""
|
||||
record = self.get_setting_record()
|
||||
record.write(settings)
|
||||
|
||||
@api.model
|
||||
def get_theme_setting(self):
|
||||
"""
|
||||
get theme settings
|
||||
"""
|
||||
record = self.get_setting_record()
|
||||
result = record.read()[0]
|
||||
result["current_theme_mode"] = record.current_theme_mode.id
|
||||
result["current_theme_style"] = record.current_theme_style.id
|
||||
for key, item in result.items():
|
||||
if isinstance(item, datetime):
|
||||
result[key] = fields.Datetime.to_string(item)
|
||||
if isinstance(item, date):
|
||||
result[key] = fields.Date.to_string(item)
|
||||
return result
|
||||
@@ -1,133 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class AnitaThemeSettingBase(models.Model):
|
||||
'''
|
||||
anita Setting Base
|
||||
'''
|
||||
_name = 'anita_theme_setting.setting_base'
|
||||
_description = 'Anita Setting Base'
|
||||
|
||||
owner = fields.Reference(
|
||||
string="Owner",
|
||||
selection=[('res.company', 'res.company'),
|
||||
('res.users', 'res.users')],
|
||||
default=False,
|
||||
help="owner which this theme is create by, if it is false, it mean it is owned to the system!")
|
||||
|
||||
layout_mode = fields.Char(
|
||||
string="Layout Mode",
|
||||
default='Layout1')
|
||||
|
||||
control_panel_mode = fields.Char(
|
||||
string="Control Panel Mode",
|
||||
default='mode1')
|
||||
|
||||
login_style = fields.Char(
|
||||
string="login style",
|
||||
default='login_style1')
|
||||
|
||||
icon_policy = fields.Selection(
|
||||
string="Menu Icon Policy",
|
||||
selection=[
|
||||
('web_icon', 'web_icon'),
|
||||
('font_icon', 'font_icon'),
|
||||
('svg_icon', 'svg_icon')],
|
||||
default='svg_icon')
|
||||
|
||||
current_theme_mode = fields.Many2one(
|
||||
comodel_name='anita_theme_setting.theme_mode',
|
||||
string="Current Theme Mode")
|
||||
|
||||
current_theme_style = fields.Many2one(
|
||||
string="Current Theme Style",
|
||||
comodel_name="anita_theme_setting.theme_style",
|
||||
domain="[('theme_mode', '=', current_theme_mode)]",
|
||||
help="Just use when theme style mode is system")
|
||||
|
||||
dialog_animation_in_style = fields.Char(
|
||||
string="Dialog Animation In Style",
|
||||
default="animate__bounceIn")
|
||||
dialog_animation_out_style = fields.Char(
|
||||
string="Dialog Animation Out Style",
|
||||
compute="_compute_dialog_animation_out_style")
|
||||
|
||||
button_style = fields.Char(
|
||||
string="Button Style",
|
||||
default="style1")
|
||||
|
||||
input_style = fields.Char(
|
||||
string="Input Style",
|
||||
default="style1")
|
||||
|
||||
tab_style = fields.Char(
|
||||
string="Tab Style",
|
||||
default="style1")
|
||||
|
||||
font_name = fields.Char(
|
||||
string="Font Name",
|
||||
default="Roboto", help="Roboto, Helvetica, Verdana, Tahoma, OpenSans, Poppins, NotoSansArabic")
|
||||
|
||||
loading_style = fields.Char(
|
||||
string="Loading Style",
|
||||
default="style1")
|
||||
|
||||
loading_svg = fields.Char(string="Loading SVG", compute="_compute_loading_svg")
|
||||
|
||||
checkbox_style = fields.Char(
|
||||
string="Checkbox Style",
|
||||
default="style1")
|
||||
|
||||
radio_style = fields.Char(
|
||||
string="Radio Style",
|
||||
default="style1")
|
||||
|
||||
separator_style = fields.Char(
|
||||
string="Separator Style",
|
||||
default="style1")
|
||||
|
||||
rtl_mode = fields.Boolean(
|
||||
string="RTL MODE",
|
||||
default=False)
|
||||
|
||||
allow_debug = fields.Boolean(
|
||||
string="Allow Debug",
|
||||
default=True)
|
||||
|
||||
@api.onchange('current_theme_mode')
|
||||
def on_current_theme_mode_change(self):
|
||||
"""
|
||||
change the style
|
||||
:return:
|
||||
"""
|
||||
if self.current_theme_mode.theme_styles:
|
||||
self.current_theme_style = \
|
||||
self.current_theme_mode.theme_styles[0].id
|
||||
else:
|
||||
self.current_theme_style = False
|
||||
|
||||
@api.depends('loading_style')
|
||||
def _compute_loading_svg(self):
|
||||
"""
|
||||
compute loading svg
|
||||
:return:
|
||||
"""
|
||||
for rec in self:
|
||||
if rec.loading_style:
|
||||
rec.loading_svg = "/anita_theme_setting/static/loadings/loading_%s.svg" % rec.loading_style
|
||||
else:
|
||||
rec.loading_svg = False
|
||||
|
||||
@api.depends('dialog_animation_in_style')
|
||||
def _compute_dialog_animation_out_style(self):
|
||||
'''
|
||||
compute the dialog animation out style
|
||||
:return:
|
||||
'''
|
||||
for record in self:
|
||||
if record.dialog_animation_in_style:
|
||||
record.dialog_animation_out_style = record.dialog_animation_in_style.replace('In', 'Out')
|
||||
else:
|
||||
record.dialog_animation_out_style = 'animate__bounceOut'
|
||||
@@ -1,314 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from inspect import trace
|
||||
from math import fabs
|
||||
from odoo import models, api, tools, fields, _
|
||||
from odoo.tools.config import config
|
||||
import traceback
|
||||
import datetime
|
||||
from datetime import date, datetime
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaThemeSettingManager(models.AbstractModel):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.setting_manager'
|
||||
_description = 'theme setting manager'
|
||||
|
||||
def _register_hook(self):
|
||||
"""
|
||||
stuff to do right after the registry is built
|
||||
"""
|
||||
# check theme mode data
|
||||
try:
|
||||
# check mode data
|
||||
owner = self.get_current_owner()
|
||||
force = False
|
||||
if config.get('debug', False):
|
||||
self.env['anita_theme_setting.default_mode_data'].sudo().refresh_default_mode()
|
||||
force = True
|
||||
self.env["anita_theme_setting.theme_mode"].check_default_mode_data(owner, force)
|
||||
except Exception as e:
|
||||
traceback.print_stack(e)
|
||||
_logger.error(e)
|
||||
|
||||
@api.model
|
||||
def get_user_setting(self, get_style=True):
|
||||
'''
|
||||
get user setting
|
||||
:return:
|
||||
'''
|
||||
rst = dict()
|
||||
|
||||
# just get the setting data
|
||||
theme_setting_mode = \
|
||||
self.env["res.config.settings"].sudo().get_theme_setting_mode()
|
||||
|
||||
# check mode data
|
||||
owner = self.get_current_owner()
|
||||
|
||||
# get the setting data
|
||||
if theme_setting_mode == 'system':
|
||||
theme_settings = self.env["res.config.settings"].get_theme_setting()
|
||||
elif theme_setting_mode == 'company':
|
||||
theme_settings = self.env["res.company"].get_theme_setting()
|
||||
else:
|
||||
theme_settings = self.env["res.users"].get_theme_setting()
|
||||
|
||||
# deal the datetime data
|
||||
for key, item in theme_settings.items():
|
||||
if isinstance(item, datetime):
|
||||
theme_settings[key] = fields.Datetime.to_string(item)
|
||||
if isinstance(item, date):
|
||||
theme_settings[key] = fields.Date.to_string(item)
|
||||
|
||||
all_modes = self.env["anita_theme_setting.theme_mode"].search(
|
||||
[('owner', '=', owner)])
|
||||
|
||||
cur_style_id = theme_settings["current_theme_style"]
|
||||
cur_mode_id = theme_settings["current_theme_mode"]
|
||||
|
||||
theme_mode = self.env["anita_theme_setting.theme_mode"].search(
|
||||
[('id', '=', cur_mode_id)])
|
||||
theme_style = self.env["anita_theme_setting.theme_style"].search(
|
||||
[('id', '=', cur_style_id)])
|
||||
cur_mode_name = theme_mode.name
|
||||
|
||||
if not cur_mode_id \
|
||||
or cur_mode_id not in all_modes.ids:
|
||||
if all_modes:
|
||||
cur_mode_id = all_modes[0].id
|
||||
cur_style_id = all_modes[0].theme_styles[0].id if all_modes[0].theme_styles else False
|
||||
cur_mode_name = all_modes[0].name
|
||||
theme_mode = all_modes[0]
|
||||
theme_style = all_modes[0].theme_styles[0] if all_modes[0].theme_styles else False
|
||||
else:
|
||||
cur_mode_id = False
|
||||
cur_style_id = False
|
||||
cur_mode_name = False
|
||||
theme_mode = self.env["anita_theme_setting.theme_mode"]
|
||||
|
||||
if not cur_style_id:
|
||||
theme_styles = theme_mode.theme_styles
|
||||
theme_style = theme_styles[0] if theme_styles else self.env["anita_theme_setting.theme_style"]
|
||||
cur_style_id = theme_style.id if theme_style else False
|
||||
|
||||
rst['settings'] = theme_settings
|
||||
rst['cur_mode_id'] = cur_mode_id
|
||||
rst['cur_style_id'] = cur_style_id
|
||||
|
||||
# used to set the body class
|
||||
rst['cur_mode_name'] = cur_mode_name
|
||||
rst['theme_setting_mode'] = theme_setting_mode
|
||||
|
||||
rst["window_default_title"] = self.env['ir.config_parameter'].sudo().get_param(
|
||||
"anita_theme_setting.window_default_title", "Odoo")
|
||||
rst["powered_by"] = self.env['ir.config_parameter'].sudo().get_param(
|
||||
"anita_theme_setting.powered_by", "Odoo")
|
||||
|
||||
# check the user is anita
|
||||
rst['is_anita'] = self.env.user._is_admin()
|
||||
|
||||
# add font info
|
||||
font_name = theme_settings.get("font_name", False)
|
||||
if font_name:
|
||||
font_type_css = "*:not(.fa):not(.oi) {font-family: " + font_name + ", sans-serif !important;}"
|
||||
else:
|
||||
font_type_css = ""
|
||||
|
||||
rst['theme_modes'] = self.get_all_mode_data(owner)
|
||||
|
||||
# get the style
|
||||
if get_style:
|
||||
if theme_style:
|
||||
style_datas = [font_type_css]
|
||||
if theme_style:
|
||||
style_datas += theme_style.get_styles_txt()
|
||||
|
||||
style_txt = '\n'.join(style_datas)
|
||||
rst['style_txt'] = style_txt
|
||||
|
||||
if not theme_style.background_image:
|
||||
# get from theme mode
|
||||
rst['mode_style_css'] = theme_mode.get_mode_style_css()
|
||||
else:
|
||||
# get from style
|
||||
rst['mode_style_css'] = theme_style.get_mode_style_css()
|
||||
|
||||
else:
|
||||
rst['style_txt'] = ""
|
||||
rst['mode_style_css'] = ""
|
||||
|
||||
return rst
|
||||
|
||||
@api.model
|
||||
def get_font_link(self):
|
||||
"""
|
||||
get font link
|
||||
:return:
|
||||
"""
|
||||
theme_settings = self.get_theme_setting()
|
||||
result = ""
|
||||
if theme_settings.get('font_name', False):
|
||||
result = '/anita_theme_setting/static/fonts/{font_name}/fonts.css'.format(font_name=theme_settings.get('font_name'))
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def get_theme_setting(self):
|
||||
"""
|
||||
get theme setting
|
||||
:return:
|
||||
"""
|
||||
# just get the setting data
|
||||
theme_setting_mode = \
|
||||
self.env["res.config.settings"].sudo().get_theme_setting_mode()
|
||||
|
||||
if theme_setting_mode == 'system':
|
||||
theme_settings = self.env["res.config.settings"].get_theme_setting()
|
||||
elif theme_setting_mode == 'company':
|
||||
theme_settings = self.env["res.company"].get_theme_setting()
|
||||
else:
|
||||
theme_settings = self.env["res.users"].get_theme_setting()
|
||||
|
||||
return theme_settings
|
||||
|
||||
@api.model
|
||||
def get_font_name(self):
|
||||
"""
|
||||
get font name
|
||||
"""
|
||||
theme_settings = self.get_theme_setting()
|
||||
return theme_settings.get('font_name')
|
||||
|
||||
@api.model
|
||||
def update_cur_style(self, mode_id, style_id):
|
||||
'''
|
||||
update user cur mode
|
||||
:return:
|
||||
'''
|
||||
ir_config = self.env['ir.config_parameter'].sudo()
|
||||
setting_mode = self.env["res.config.settings"].get_theme_setting_mode()
|
||||
if setting_mode == "system":
|
||||
ir_config.set_param("anita_theme_setting.current_theme_mode", mode_id)
|
||||
ir_config.set_param("anita_theme_setting.current_theme_style", style_id)
|
||||
elif setting_mode == "company":
|
||||
company = self.env.user.company_id
|
||||
company.setting_id.current_theme_mode = mode_id
|
||||
company.setting_id.current_theme_style = style_id
|
||||
elif setting_mode == "user":
|
||||
user_id = self.env.user.id
|
||||
user = self.env["res.users"].browse(user_id)
|
||||
user.setting_id.current_theme_mode = mode_id
|
||||
user.setting_id.current_theme_style = style_id
|
||||
else:
|
||||
assert False
|
||||
|
||||
@api.model
|
||||
def save_style_data(self, style_id, style_data, owner):
|
||||
'''
|
||||
save style datas
|
||||
:param style_id:
|
||||
:param style_data:
|
||||
:param owner:
|
||||
:return:
|
||||
'''
|
||||
theme_style = self.env["anita_theme_setting.theme_style"].browse(style_id)
|
||||
theme_style.ensure_one()
|
||||
|
||||
theme_mode = theme_style.theme_mode
|
||||
# save current style just when type is same
|
||||
mode_id = theme_mode.id
|
||||
if owner:
|
||||
if owner.startswith('res.users'):
|
||||
parts = owner.split(',')
|
||||
user = self.env["res.users"].browse(int(parts[1].strip()))
|
||||
if user:
|
||||
user.ensure_setting_id()
|
||||
user.setting_id.current_theme_mode = mode_id
|
||||
user.setting_id.current_theme_style = style_id
|
||||
elif owner.startswith('res.company'):
|
||||
parts = owner.split(',')
|
||||
company = self.env["res.company"].browse(int(parts[1].strip()))
|
||||
if company:
|
||||
company.ensure_setting_id()
|
||||
company.setting_id.current_theme_mode = mode_id
|
||||
company.setting_id.current_theme_style = style_id
|
||||
else:
|
||||
system_setting = self.env["res.config.settings"].get_setting_record()
|
||||
system_setting.current_theme_mode = mode_id
|
||||
system_setting.current_theme_style = style_id
|
||||
|
||||
# as the image is direct change from database so not need to update it
|
||||
var_infos = theme_style.mapped('groups.sub_groups.style_items.vars')
|
||||
var_cache = {var_info.id: var_info for var_info in var_infos}
|
||||
for data in style_data:
|
||||
var_id = data["id"]
|
||||
tmp_var = var_cache[var_id]
|
||||
tmp_var.write({
|
||||
"color": data.get('color', False)
|
||||
})
|
||||
|
||||
if theme_style.background_image:
|
||||
mode_style_cs = theme_style.get_mode_style_css()
|
||||
else:
|
||||
mode_style_cs = theme_mode.get_mode_style_css()
|
||||
|
||||
# return the style data
|
||||
return {
|
||||
'theme_style': theme_style.get_style(),
|
||||
'mode_style_css': mode_style_cs
|
||||
}
|
||||
|
||||
def get_current_owner(self):
|
||||
"""
|
||||
get current owner
|
||||
:return:
|
||||
"""
|
||||
theme_setting_mode = \
|
||||
self.env["res.config.settings"].sudo().get_theme_setting_mode()
|
||||
|
||||
# check mode data
|
||||
owner = False
|
||||
if theme_setting_mode == 'system':
|
||||
owner = False
|
||||
elif theme_setting_mode == 'company':
|
||||
owner = 'res.company, {company_id}'.format(
|
||||
company_id=self.env.user.company_id.id)
|
||||
elif theme_setting_mode == 'user':
|
||||
owner = 'res.users, {user_id}'.format(user_id=self.env.user.id)
|
||||
|
||||
return owner
|
||||
|
||||
@tools.ormcache('owner')
|
||||
@api.model
|
||||
def get_all_mode_data(self, owner):
|
||||
"""
|
||||
get all mode data
|
||||
"""
|
||||
all_modes = self.env["anita_theme_setting.theme_mode"].search([('owner', '=', owner)])
|
||||
result = []
|
||||
for mode in all_modes:
|
||||
result.append(mode.get_mode_data()[0])
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def save_settings(self, settings):
|
||||
"""
|
||||
save the settings
|
||||
"""
|
||||
# get the setting mode
|
||||
setting_mode = self.env["res.config.settings"].get_theme_setting_mode()
|
||||
if setting_mode == "system":
|
||||
self.env["res.config.settings"].save_theme_setting(settings)
|
||||
elif setting_mode == "company":
|
||||
self.env["res.company"].save_theme_setting(settings)
|
||||
elif setting_mode == "user":
|
||||
self.env["res.users"].save_theme_setting(settings)
|
||||
else:
|
||||
assert False
|
||||
|
||||
return self.get_theme_setting()
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions
|
||||
import json
|
||||
from odoo.tools import ormcache
|
||||
|
||||
|
||||
class AnitaTemplateVarVal(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.template_var_val'
|
||||
_description = 'anita Mode Template'
|
||||
|
||||
theme_mode = fields.Many2one(string="Theme Mode", comodel_name="anita_theme_setting.theme_mode")
|
||||
name = fields.Char(string="Name", required=True)
|
||||
group = fields.Char(string="Group", required=True)
|
||||
value = fields.Char(string="Value", required=True)
|
||||
disable_opacity = fields.Boolean(string="Disable Opacity", default=False)
|
||||
description = fields.Char(string="Description")
|
||||
|
||||
@@ -1,588 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions, tools
|
||||
import json
|
||||
import re
|
||||
from odoo.tools.misc import file_path
|
||||
from odoo.tools.config import config
|
||||
from .anita_utility import apply_alpha
|
||||
|
||||
try:
|
||||
import sass as libsass
|
||||
except ImportError:
|
||||
libsass = None
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaThemeMode(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.theme_mode'
|
||||
_description = 'anita theme mode'
|
||||
|
||||
name = fields.Char(string="Name", required=True)
|
||||
sequence = fields.Integer(string="Mode Sequence", default=0)
|
||||
|
||||
theme_styles = fields.One2many(
|
||||
string="Theme Styles",
|
||||
comodel_name="anita_theme_setting.theme_style",
|
||||
inverse_name="theme_mode")
|
||||
|
||||
mode_template = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.mode_template",
|
||||
string="Mode Template")
|
||||
|
||||
template_var_vals = fields.One2many(
|
||||
comodel_name="anita_theme_setting.template_var_val",
|
||||
inverse_name="theme_mode",
|
||||
string="Template Vars")
|
||||
|
||||
# if owner is False, so the style own to global
|
||||
owner = fields.Reference(
|
||||
string="Owner",
|
||||
selection=[('res.company', 'res.company'),
|
||||
('res.users', 'res.users')],
|
||||
default=False,
|
||||
help="owner which this theme is create by, if it is false, it mean it is owned to the system!")
|
||||
mode_style_css = fields.Text(string="Mode Style Css")
|
||||
compiled_mode_style_css = fields.Text(string="Compiled mode style css")
|
||||
|
||||
is_default = fields.Boolean(string="Is Default", default=False)
|
||||
version = fields.Char(
|
||||
string="Version", help="use version to check update or not")
|
||||
|
||||
_sql_constraints = [('theme_mode_name_unique', 'UNIQUE(name, owner)',
|
||||
"Theme mode name and owner must unique")]
|
||||
|
||||
def get_mode_preview_data(self, style_id=False):
|
||||
"""
|
||||
get mode preview data
|
||||
:return:
|
||||
"""
|
||||
if self.mode_template:
|
||||
if style_id:
|
||||
theme_style = self.env['anita_theme_setting.theme_style'].browse(
|
||||
style_id)
|
||||
if theme_style.is_semi_transparent():
|
||||
result = theme_style.get_mode_style_css()
|
||||
else:
|
||||
result = self.get_mode_style_css()
|
||||
else:
|
||||
result = self.get_mode_style_css()
|
||||
else:
|
||||
result = ""
|
||||
|
||||
mode_name = 'body.{name}'.format(name=self.name)
|
||||
# replace mode_name with body.preview
|
||||
result = result.replace(mode_name, 'body.preview')
|
||||
return result
|
||||
|
||||
@api.onchange('mode_template')
|
||||
def on_mode_template_changed(self):
|
||||
"""
|
||||
compute mode style
|
||||
:return:
|
||||
"""
|
||||
# check id is instance of NewId
|
||||
if self.mode_template:
|
||||
scss_vars = self.mode_template.get_color_vars()
|
||||
template_var_vals = [(5, 0, 0)]
|
||||
for group_name in scss_vars:
|
||||
var_items = scss_vars[group_name]
|
||||
for var_item in var_items:
|
||||
template_var_vals.append((0, 0, {
|
||||
"name": var_item["name"],
|
||||
"group": group_name,
|
||||
"value": var_item["value"],
|
||||
"disable_opacity": var_item.get("disable_opacity", False),
|
||||
"description": var_item["description"]
|
||||
}))
|
||||
self.template_var_vals = template_var_vals
|
||||
else:
|
||||
self.template_var_vals = [(5, 0, 0)]
|
||||
|
||||
def check_template_vars(self):
|
||||
"""
|
||||
check style css
|
||||
:return:
|
||||
"""
|
||||
return
|
||||
|
||||
@tools.ormcache('self.id')
|
||||
def get_scss_vars_cache(self):
|
||||
"""
|
||||
get scss vars
|
||||
:return:
|
||||
"""
|
||||
records = self.template_var_vals.read(['name', 'value'])
|
||||
return {record['name']: record['value'] for record in records}
|
||||
|
||||
@tools.ormcache('self.id')
|
||||
def get_scss_var_record_cache(self):
|
||||
"""
|
||||
get scss vars
|
||||
:return:
|
||||
"""
|
||||
records = self.template_var_vals
|
||||
return {record.name: {
|
||||
"name": record.name,
|
||||
"value": record.value,
|
||||
"disable_opacity": record.disable_opacity,
|
||||
} for record in records}
|
||||
|
||||
def _compile_scss(self, alpha=False):
|
||||
"""
|
||||
This code will compile valid scss into css.
|
||||
Simply copied and adapted slightly
|
||||
"""
|
||||
self.ensure_one()
|
||||
scss_source = self.mode_template.template
|
||||
|
||||
scss_vars = []
|
||||
var_val_cache = self.get_scss_var_record_cache()
|
||||
for var_name in var_val_cache:
|
||||
var_record = var_val_cache[var_name]
|
||||
name = var_record.get("name")
|
||||
if alpha and alpha < 1:
|
||||
color = apply_alpha(var_record.get('name'), var_record.get('value'), alpha)
|
||||
else:
|
||||
color = var_record.get('value')
|
||||
# new color
|
||||
scss_vars.append('{name}: {value};'.format(
|
||||
name=name, value=color))
|
||||
# origin color, some element disable alpha
|
||||
scss_vars.append('{name}: {value};'.format(
|
||||
name=name.replace('$', '$_'), value=var_record.get('value')))
|
||||
|
||||
precision = 8
|
||||
output_style = 'expanded'
|
||||
|
||||
bootstrap_scss_path = file_path('web/static/lib/bootstrap/scss')
|
||||
|
||||
mode_selectors = "$mode_selectors: 'body.{name}';".format(name=self.name)
|
||||
extra_vars = [
|
||||
mode_selectors,
|
||||
"$compile_normal: 1;",
|
||||
"$semi_transparent: {semi_trans};".format(semi_trans=True if alpha < 1.0 else False),
|
||||
]
|
||||
|
||||
total_vars = extra_vars + scss_vars
|
||||
normal_source = '\n'.join(total_vars) + '\n' + scss_source if scss_source else ''
|
||||
|
||||
# compile normal
|
||||
results = []
|
||||
try:
|
||||
result = libsass.compile(
|
||||
string=normal_source,
|
||||
include_paths=[bootstrap_scss_path],
|
||||
output_style=output_style,
|
||||
precision=precision)
|
||||
# remove the import bootstrap
|
||||
index = result.find("body.{name}".format(name=self.name))
|
||||
result = result[index:]
|
||||
results.append(result)
|
||||
except libsass.CompileError as e:
|
||||
_logger.error(e.args[0])
|
||||
|
||||
# compile for background
|
||||
if alpha < 1.0:
|
||||
scss_vars = []
|
||||
for var_name in var_val_cache:
|
||||
var_record = var_val_cache[var_name]
|
||||
name = var_record.get("name")
|
||||
# new color
|
||||
scss_vars.append('{name}: {value};'.format(name=name, value=var_record.get('value')))
|
||||
# origin color
|
||||
scss_vars.append('{name}: {value};'.format(name=name.replace('$', '$_'), value=var_record.get('value')))
|
||||
|
||||
mode_selectors = "$mode_selectors: 'body.{name} .o_dialog_container';".format(name=self.name)
|
||||
extra_vars = [
|
||||
mode_selectors,
|
||||
"$compile_normal: 0;",
|
||||
"$semi_transparent: {semi_trans};".format(semi_trans=1 if alpha < 1.0 else 0),
|
||||
]
|
||||
total_vars = extra_vars + scss_vars
|
||||
normal_source = '\n'.join(total_vars) + '\n' + str(scss_source)
|
||||
try:
|
||||
result = libsass.compile(
|
||||
string=normal_source,
|
||||
include_paths=[bootstrap_scss_path],
|
||||
output_style=output_style,
|
||||
precision=precision)
|
||||
# remove the import bottstrap
|
||||
index = result.find('body.{name} .o_dialog_container'.format(name=self.name))
|
||||
result = result[index:]
|
||||
results.append(result)
|
||||
except libsass.CompileError as e:
|
||||
_logger.error(e.args[0])
|
||||
|
||||
return '\n'.join(results)
|
||||
|
||||
@api.model
|
||||
def compute_mode_style_url(self):
|
||||
"""
|
||||
compute mode style url
|
||||
:return:
|
||||
"""
|
||||
for record in self:
|
||||
record.mode_style_url = \
|
||||
'/web/image/anita_theme_setting.anita_theme_setting/{var_id}/icon128x128'.format(
|
||||
var_id=record.id)
|
||||
|
||||
def get_default_mode_style_text(self, theme_mode_info):
|
||||
"""
|
||||
get default mode data
|
||||
:return:
|
||||
"""
|
||||
if self.name == 'normal':
|
||||
return
|
||||
|
||||
color_vars = theme_mode_info["template_vars"]
|
||||
mode_template = theme_mode_info["mode_template"]
|
||||
if not mode_template:
|
||||
raise exceptions.ValidationError('Can not find the theme template!')
|
||||
|
||||
template = mode_template.template
|
||||
# css template data
|
||||
template = template.replace('$mode_name', self.name)
|
||||
|
||||
# replace the var
|
||||
for var_nam in color_vars:
|
||||
template = str(template).replace(var_nam, color_vars[var_nam])
|
||||
|
||||
return template
|
||||
|
||||
@api.model
|
||||
def check_default_mode_data(self, owner=False, force = False):
|
||||
'''
|
||||
check default mode data
|
||||
:return:
|
||||
'''
|
||||
modes = self.search([('owner', '=', owner)])
|
||||
olde_mode_cache = {mode.name: mode for mode in modes}
|
||||
|
||||
default_modes = \
|
||||
self.env["anita_theme_setting.default_mode_data"].search([])
|
||||
|
||||
# create new mode if the mode do not exits
|
||||
for default_mode in default_modes:
|
||||
|
||||
if not default_mode.data:
|
||||
continue
|
||||
|
||||
# need to reload the mode data
|
||||
mode_data = default_mode.get_mode_data()
|
||||
if default_mode.name not in olde_mode_cache:
|
||||
mode_data["name"] = default_mode["name"]
|
||||
|
||||
# create new mode use the mode data
|
||||
record = self.create_mode(mode_data, owner)
|
||||
if not record.mode_template:
|
||||
continue
|
||||
|
||||
# check the templates
|
||||
templates = self.env["anita_theme_setting.mode_template"].get_templates(
|
||||
)
|
||||
template = mode_data.get('mode_template', False)
|
||||
if template and template not in templates:
|
||||
raise exceptions.ValidationError(
|
||||
'{template} do not exits!'.format(template=mode_data["template"]))
|
||||
|
||||
template = templates[mode_data["mode_template"]]
|
||||
record.mode_template = template.id
|
||||
|
||||
# check vars are defined in the template vars
|
||||
record.check_template_vars()
|
||||
else:
|
||||
# check version, if the version is different
|
||||
mode = olde_mode_cache[default_mode.name]
|
||||
if force or mode.version != mode_data["version"]:
|
||||
# update template vars
|
||||
temlate_var_vals = mode_data["template_var_vals"]
|
||||
mode.update_template_var_values(temlate_var_vals)
|
||||
# update template
|
||||
mode_templates = self.env["anita_theme_setting.mode_template"].get_templates(
|
||||
)
|
||||
if mode_data.get("mode_template", False):
|
||||
template_name = mode_data["mode_template"]
|
||||
mode.mode_template = \
|
||||
mode_templates.get(template_name, False)
|
||||
mode.version = mode_data["version"]
|
||||
mode.check_mode_data()
|
||||
# recompile the template
|
||||
mode.compiled_mode_style_css = False
|
||||
|
||||
# unlink the invalid mode
|
||||
for default_mode in default_modes:
|
||||
if not default_mode.data:
|
||||
default_mode.unlink()
|
||||
|
||||
def create_mode(self, mode_config, owner=False, is_default=True):
|
||||
'''
|
||||
create mode
|
||||
:param mode style txt:
|
||||
:param mode_data:
|
||||
:param owner:
|
||||
:param is_default:
|
||||
:return:
|
||||
'''
|
||||
mode_templates = \
|
||||
self.env["anita_theme_setting.mode_template"].get_templates()
|
||||
mode_template = mode_config.get('mode_template', False)
|
||||
if mode_template and mode_template not in mode_templates:
|
||||
raise exceptions.ValidationError('Can not find mode template {mode_template}'.format(
|
||||
mode_template=mode_template))
|
||||
|
||||
# convert to cache data
|
||||
template_var_vals = mode_config.get('template_var_vals', {})
|
||||
var_val_cache = {}
|
||||
for group in template_var_vals:
|
||||
var_items = template_var_vals[group]
|
||||
for var_item in var_items:
|
||||
var_val_cache[var_item["name"]] = var_item["value"]
|
||||
|
||||
theme_styles = mode_config['theme_styles']
|
||||
theme_style_array = []
|
||||
for theme_style in theme_styles:
|
||||
groups = theme_style["groups"]
|
||||
group_datas = []
|
||||
for group_index, var_items in enumerate(groups):
|
||||
sub_groups = var_items["sub_groups"]
|
||||
sub_group_array = []
|
||||
for sub_group_index, sub_group in enumerate(sub_groups):
|
||||
# create sub group
|
||||
item_array = []
|
||||
style_items = sub_group["style_items"]
|
||||
for item_index, style_item in enumerate(style_items):
|
||||
var_val_array = []
|
||||
var_infos = style_item["vars"]
|
||||
for var_index, var_info in enumerate(var_infos):
|
||||
color = var_info.get("color", False)
|
||||
ref_var = var_info.get("ref_var", False)
|
||||
if ref_var:
|
||||
if ref_var not in var_val_cache:
|
||||
color = False
|
||||
_logger.info(
|
||||
"Can not find ref var {ref_var}".format(ref_var=ref_var))
|
||||
else:
|
||||
color = var_val_cache[ref_var]
|
||||
|
||||
var_val_array.append((0, 0, {
|
||||
"name": var_info["name"],
|
||||
"type": var_info["type"],
|
||||
"sequence": var_index,
|
||||
"is_default": is_default,
|
||||
"color": color,
|
||||
"image": var_info.get('image', False),
|
||||
"image_url": var_info.get('image_url', False),
|
||||
"svg": var_info.get('svg', False),
|
||||
"identity": var_info.get('identity', False)
|
||||
}))
|
||||
|
||||
item_array.append((0, 0, {
|
||||
"name": style_item["name"],
|
||||
"is_default": is_default,
|
||||
"sequence": item_index,
|
||||
"val_template": style_item["val_template"],
|
||||
"sub_group": sub_group["name"],
|
||||
"vars": var_val_array,
|
||||
"selectors": json.dumps(style_item["selectors"])
|
||||
}))
|
||||
|
||||
sub_group_array.append((0, 0, {
|
||||
"name": sub_group["name"],
|
||||
"sequence": sub_group_index,
|
||||
"style_items": item_array,
|
||||
"is_default": is_default
|
||||
}))
|
||||
|
||||
group_datas.append((0, 0, {
|
||||
"name": var_items["name"],
|
||||
"sequence": group_index,
|
||||
"is_default": is_default,
|
||||
"sub_groups": sub_group_array
|
||||
}))
|
||||
|
||||
theme_style_array.append((0, 0, {
|
||||
"name": theme_style["name"],
|
||||
"is_default": is_default,
|
||||
"groups": group_datas
|
||||
}))
|
||||
|
||||
vals = []
|
||||
template_var_vals = mode_config.get('template_var_vals', {})
|
||||
for group_name in template_var_vals:
|
||||
var_items = template_var_vals[group_name]
|
||||
for var_item in var_items:
|
||||
vals.append((0, 0, {
|
||||
"name": var_item["name"],
|
||||
"group": group_name,
|
||||
"value": var_item["value"],
|
||||
"disable_opacity": var_item.get("disable_opacity", False),
|
||||
"description": var_item["description"]
|
||||
}))
|
||||
|
||||
mode_template = mode_config.get("mode_template", False)
|
||||
return self.env["anita_theme_setting.theme_mode"].create([{
|
||||
"name": mode_config["name"],
|
||||
"theme_styles": theme_style_array,
|
||||
"is_default": is_default,
|
||||
"version": mode_config["version"],
|
||||
"mode_template": mode_templates[mode_template].id if mode_template else False,
|
||||
"owner": owner,
|
||||
"template_var_vals": vals
|
||||
}])
|
||||
|
||||
def get_mode_data(self):
|
||||
'''
|
||||
get the mode data
|
||||
:return:
|
||||
'''
|
||||
self.ensure_theme_style()
|
||||
rst = []
|
||||
for record in self:
|
||||
tmp_data = record.read(
|
||||
['name', 'is_default', 'sequence', 'version'])[0]
|
||||
tmp_data['theme_styles'] = record.theme_styles.get_styles()
|
||||
rst.append(tmp_data)
|
||||
return rst
|
||||
|
||||
def delete_mode(self):
|
||||
"""
|
||||
delete the mode
|
||||
:return:
|
||||
"""
|
||||
self.unlink()
|
||||
|
||||
def check_style_css(self):
|
||||
"""
|
||||
check style css
|
||||
:return:
|
||||
"""
|
||||
if self.mode_template and not self.compiled_mode_style_css:
|
||||
self.compiled_mode_style_css = self._compile_scss()
|
||||
|
||||
def update_template_var_values(self, template_var_vals):
|
||||
"""
|
||||
update template var values
|
||||
:param temlate_var_vals:
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
vals = [(5, 0, 0)]
|
||||
for group_name in template_var_vals:
|
||||
var_items = template_var_vals[group_name]
|
||||
for var_item in var_items:
|
||||
vals.append((0, 0, {
|
||||
"name": var_item["name"],
|
||||
"group": group_name,
|
||||
"value": var_item["value"],
|
||||
"description": var_item.get("description")
|
||||
}))
|
||||
self.template_var_vals = vals
|
||||
|
||||
def get_mode_style_css(self):
|
||||
"""
|
||||
get mode style css
|
||||
"""
|
||||
if config.get('debug', False) or not self.compiled_mode_style_css:
|
||||
self.compiled_mode_style_css = self._compile_scss()
|
||||
|
||||
return self.compiled_mode_style_css
|
||||
|
||||
def check_mode_data(self):
|
||||
'''
|
||||
check the mode data
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
default_modes = self.env[
|
||||
"anita_theme_setting.default_mode_data"].search([])
|
||||
default_mode_cache = {
|
||||
default_mode.name: default_mode for default_mode in default_modes}
|
||||
|
||||
mode_name = self.name
|
||||
if mode_name not in default_mode_cache:
|
||||
return
|
||||
|
||||
theme_styles = self.theme_styles
|
||||
theme_style_cache = {
|
||||
theme_style.name: theme_style for theme_style in theme_styles if theme_style.is_default}
|
||||
|
||||
default_mode = default_mode_cache[mode_name]
|
||||
default_mode_data = default_mode.get_mode_data()
|
||||
|
||||
theme_styles = default_mode_data['theme_styles']
|
||||
for theme_style in theme_styles:
|
||||
# create new style
|
||||
style_name = theme_style['name']
|
||||
if style_name not in theme_style_cache:
|
||||
self.env["anita_theme_setting.theme_style"].create_style(
|
||||
self.id, theme_style)
|
||||
else:
|
||||
# check the style data, may be one more style with the same name
|
||||
tmp_style = theme_style_cache[style_name]
|
||||
tmp_style.check_groups(default_mode, theme_style["groups"])
|
||||
|
||||
def get_mode_edit_action(self, style_id):
|
||||
"""
|
||||
get theme mode edit action
|
||||
"""
|
||||
self.ensure_one()
|
||||
form_id = self.env.ref("anita_theme_setting.edit_theme_mode_form").id
|
||||
|
||||
var_vals = []
|
||||
for var_val in self.template_var_vals:
|
||||
var_vals.append((0, 0, {
|
||||
"name": var_val.name,
|
||||
"group": var_val.group,
|
||||
"value": var_val.value,
|
||||
"disable_opacity": var_val.disable_opacity,
|
||||
"description": var_val.description
|
||||
}))
|
||||
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "anita_theme_setting.theme_mode",
|
||||
"views": [[form_id, "form"]],
|
||||
"res_id": self.id,
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
"context": {
|
||||
"default_name": self.name,
|
||||
"default_mode_template": self.mode_template.id,
|
||||
"sequnce": self.sequence,
|
||||
"template_var_vals": var_vals,
|
||||
"default_style_id": style_id
|
||||
}
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_new_mode_action(self):
|
||||
"""
|
||||
get create new mode action
|
||||
"""
|
||||
form_id = self.env.ref("anita_theme_setting.new_theme_mode_form").id
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "anita_theme_setting.theme_mode",
|
||||
"views": [[form_id, "form"]],
|
||||
"view_mode": "form",
|
||||
"target": "new"
|
||||
}
|
||||
|
||||
def ensure_theme_style(self):
|
||||
"""
|
||||
create a empty theme style
|
||||
"""
|
||||
if not self.theme_styles:
|
||||
self.env["anita_theme_setting.theme_style"].create_style(
|
||||
self.id, {
|
||||
"name": "style1",
|
||||
"is_default": False,
|
||||
"groups": []
|
||||
})
|
||||
@@ -1,506 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, _, exceptions, tools
|
||||
import json
|
||||
import base64
|
||||
from odoo.tools.config import config
|
||||
from .anita_utility import apply_alpha
|
||||
import traceback
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaThemeStyle(models.Model):
|
||||
'''
|
||||
Theme style
|
||||
'''
|
||||
_name = 'anita_theme_setting.theme_style'
|
||||
_description = 'awesome theme style'
|
||||
_order = 'sequence asc, id asc'
|
||||
|
||||
name = fields.Char(string="style name", required=True)
|
||||
theme_mode = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.theme_mode",
|
||||
string="theme mode",
|
||||
ondelete="cascade")
|
||||
mode_css = fields.Text(string="mode style scss")
|
||||
style_css = fields.Text(string="style css")
|
||||
|
||||
is_default = fields.Boolean(string="is default", default=False)
|
||||
|
||||
background_image = fields.Binary(string="background image")
|
||||
opacity = fields.Float(string="opacity", default=1.0)
|
||||
|
||||
owner = fields.Reference(
|
||||
string="owner",
|
||||
selection=[
|
||||
('res.company', 'res.company'),
|
||||
('res.users', 'res.users')],
|
||||
default=False,
|
||||
help="Owner which this theme is create by")
|
||||
|
||||
sequence = fields.Integer(string="style sequence", default=0)
|
||||
|
||||
groups = fields.One2many(
|
||||
string="Groups",
|
||||
comodel_name="anita_theme_setting.style_item_group",
|
||||
inverse_name="theme_style")
|
||||
|
||||
side_bar_style = fields.Selection(
|
||||
string="side bar style",
|
||||
selection=[
|
||||
('style1', 'style1'),
|
||||
('style2', 'style2'),
|
||||
('style3', 'style3')])
|
||||
|
||||
sidebar_width = fields.Integer(string="sidebar width", default=80)
|
||||
sidebar_menu_width = fields.Integer(
|
||||
string="sidebar menu width", default=220)
|
||||
|
||||
@tools.ormcache('self.id')
|
||||
def get_style(self):
|
||||
'''
|
||||
get simple style data
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
result = self.read(['id', 'name', 'is_default'])[0]
|
||||
result["groups"] = self.groups.get_group_data()
|
||||
result["theme_mode"] = self.theme_mode.name
|
||||
# get background image ulr
|
||||
if self.background_image:
|
||||
result["background_image"] = self.get_background_image_url()
|
||||
else:
|
||||
result["background_image"] = False
|
||||
result["opacity"] = self.opacity
|
||||
identities = dict()
|
||||
groups = result["groups"]
|
||||
# collect identity
|
||||
for group in groups:
|
||||
sub_groups = group["sub_groups"]
|
||||
for sub_group in sub_groups:
|
||||
style_items = sub_group["style_items"]
|
||||
for item in style_items:
|
||||
if not item["vars"]:
|
||||
continue
|
||||
# collect the identity
|
||||
var_infos = item["vars"]
|
||||
for var in var_infos:
|
||||
if var.get('identity', False):
|
||||
if self.is_semi_transparent():
|
||||
identities[var['identity']] = apply_alpha(var['name'], var['color'], self.opacity, var.get('color_type'))
|
||||
else:
|
||||
identities[var['identity']] = var['color']
|
||||
|
||||
result["identities"] = identities
|
||||
return result
|
||||
|
||||
def get_styles(self):
|
||||
'''
|
||||
get mode datas
|
||||
:return:
|
||||
'''
|
||||
results = []
|
||||
for record in self:
|
||||
results.append(record.get_style())
|
||||
return results
|
||||
|
||||
def _get_style_data(self):
|
||||
'''
|
||||
get mode datas
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
results = []
|
||||
result = self.read(['name', 'is_default', 'sequence'])[0]
|
||||
if self.background_image:
|
||||
result["background_image"] = self.get_background_image_url()
|
||||
result["groups"] = self.groups.get_group_data()
|
||||
results.append(result)
|
||||
return results
|
||||
|
||||
def get_styles_txt(self):
|
||||
'''
|
||||
get style txt
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
mode_prefix = 'body.' + self.theme_mode.name + ' '
|
||||
result = []
|
||||
groups = self.groups
|
||||
for group in groups:
|
||||
for sub_group in group.sub_groups:
|
||||
style_items = sub_group.style_items
|
||||
for style_item in style_items:
|
||||
val = style_item.val
|
||||
|
||||
# if the val is same as the ref var, then ignore it
|
||||
if not val or val == "" or not style_item.selectors:
|
||||
continue
|
||||
|
||||
try:
|
||||
selector = mode_prefix + \
|
||||
','.join(json.loads(style_item["selectors"]))
|
||||
tmp_txt = selector + " {" + val + "}"
|
||||
result.append(tmp_txt)
|
||||
except Exception as error:
|
||||
traceback.print_stack(e)
|
||||
_logger.info(error)
|
||||
|
||||
# append the background image
|
||||
if self.background_image:
|
||||
background_image_url = self.get_background_image_url()
|
||||
if background_image_url:
|
||||
result.append(
|
||||
mode_prefix + " {background-image: url('" +
|
||||
background_image_url + "') !important;")
|
||||
|
||||
return result
|
||||
|
||||
def is_semi_transparent(self):
|
||||
'''
|
||||
is semi transparent
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
return self.opacity < 1.0
|
||||
|
||||
def get_mode_style_css(self):
|
||||
'''
|
||||
get mode styles
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
if not self.is_semi_transparent():
|
||||
return self.theme_mode.get_mode_style_css()
|
||||
else:
|
||||
if config.get('debug', False) or not self.mode_css:
|
||||
self.mode_css = self.theme_mode._compile_scss(self.opacity or 1)
|
||||
return self.mode_css
|
||||
|
||||
def _compute_var_val(self, style_item_info):
|
||||
'''
|
||||
compute the val, use by the
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
background_image = self.background_image
|
||||
scss_var_cache = self.theme_mode.get_scss_vars_cache()
|
||||
opacity = self.opacity
|
||||
vars_cache = dict()
|
||||
all_color = True
|
||||
all_ref = True
|
||||
all_same = True
|
||||
for var in style_item_info.get('vars', []):
|
||||
name = var.get('name', False)
|
||||
type = var.get('type', False)
|
||||
if type == 'color':
|
||||
# check if has opacity
|
||||
color = var.get('color')
|
||||
ref_var = var.get('ref_var', False)
|
||||
if ref_var:
|
||||
ref_color = scss_var_cache[ref_var]
|
||||
if ref_color != color:
|
||||
all_same = False
|
||||
else:
|
||||
all_ref = False
|
||||
|
||||
try:
|
||||
if background_image:
|
||||
if not var.get('disable_opacity', False):
|
||||
color = apply_alpha(name, color, opacity, var.get('color_type'))
|
||||
except Exception as e:
|
||||
traceback.print_stack(e)
|
||||
_logger.error("apply alpha error {e}".format(e=e))
|
||||
vars_cache[name] = color
|
||||
elif var.type == 'image':
|
||||
vars_cache[name] = var.get('image_file_url')
|
||||
all_color = False
|
||||
elif var.type == 'image_url':
|
||||
vars_cache[name] = var.get('image_url')
|
||||
all_color = False
|
||||
else:
|
||||
vars_cache[name] = var.get('svg')
|
||||
all_color = False
|
||||
|
||||
# if all the color are the original color, them ignore it
|
||||
if all_color and all_ref and all_same:
|
||||
style_item_info['val'] = False
|
||||
else:
|
||||
try:
|
||||
val_template = style_item_info.get('val_template', False)
|
||||
style_item_info['val'] = val_template.format(**vars_cache)
|
||||
except Exception as error:
|
||||
style_item_info['val'] = False
|
||||
traceback.print_stack(e)
|
||||
_logger.info('parse var value error, item name {item_name}->{error}'.format(
|
||||
item_name=style_item_info['name'], error=error))
|
||||
|
||||
def post_deal_preview_data(self, style_item_infos, font_info, preview = False):
|
||||
'''
|
||||
get mode datas
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
if preview:
|
||||
mode_prefix = 'body.preview '
|
||||
else:
|
||||
mode_prefix = 'body.' + self.theme_mode.name + ' '
|
||||
|
||||
result = [font_info]
|
||||
groups = self.groups
|
||||
for group in groups:
|
||||
for sub_group in group.sub_groups:
|
||||
style_items = sub_group.style_items
|
||||
for style_item in style_items:
|
||||
if str(style_item.id) not in style_item_infos:
|
||||
val = style_item.val
|
||||
if val == "":
|
||||
continue
|
||||
if not style_item.selectors:
|
||||
continue
|
||||
try:
|
||||
selector = mode_prefix + \
|
||||
','.join(json.loads(style_item["selectors"]))
|
||||
tmp_txt = selector + " {" + val + "}"
|
||||
result.append(tmp_txt)
|
||||
except Exception as error:
|
||||
traceback.print_stack(e)
|
||||
_logger.info(error)
|
||||
else:
|
||||
style_item = style_item_infos[str(style_item.id)]
|
||||
|
||||
# compute the val
|
||||
self._compute_var_val(style_item)
|
||||
val = style_item.get('val', False)
|
||||
|
||||
# if the val is same as the ref var, then ignore it
|
||||
selectors = style_item.get('selectors', False)
|
||||
if not val or val == "" or not selectors:
|
||||
continue
|
||||
|
||||
try:
|
||||
# check selector is a array
|
||||
if not isinstance(selectors, list):
|
||||
selectors = [selectors]
|
||||
selector = mode_prefix + ','.join(selectors)
|
||||
tmp_txt = selector + " {" + val + "}"
|
||||
result.append(tmp_txt)
|
||||
except Exception as error:
|
||||
traceback.print_stack(error)
|
||||
_logger.info(error)
|
||||
|
||||
# append the background image
|
||||
if self.background_image:
|
||||
background_image_url = self.get_background_image_url()
|
||||
if background_image_url:
|
||||
result.append(
|
||||
mode_prefix + " {background-image: url('" +
|
||||
background_image_url + "') !important;")
|
||||
|
||||
return "\n".join(result)
|
||||
|
||||
@api.model
|
||||
def delete_style(self, style_id):
|
||||
'''
|
||||
delete styel of user
|
||||
:return:
|
||||
'''
|
||||
record = self.search([('id', '=', style_id)])
|
||||
record.unlink()
|
||||
|
||||
def check_groups(self, default_mode, groups):
|
||||
'''
|
||||
check the style group
|
||||
:param groups:
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
new_group_cache = {group["name"]: group for group in groups}
|
||||
old_group_cache = {group.name: group for group in self.groups}
|
||||
|
||||
for group_index, name in enumerate(new_group_cache):
|
||||
default_group = new_group_cache[name]
|
||||
if name not in old_group_cache:
|
||||
self.env['anita_theme_setting.style_item_group']\
|
||||
.create_group_from_default_data(self, default_group, group_index)
|
||||
else:
|
||||
# update the style item
|
||||
tmp_group = old_group_cache[name]
|
||||
tmp_group.write({
|
||||
"name": name,
|
||||
"is_default": True,
|
||||
"sequence": group_index,
|
||||
})
|
||||
# check group item data
|
||||
sub_group_items = default_group["sub_groups"]
|
||||
tmp_group.check_sub_group(default_mode, sub_group_items)
|
||||
|
||||
# delete the style item
|
||||
for tmp_group_name in old_group_cache:
|
||||
if tmp_group_name not in new_group_cache \
|
||||
and old_group_cache[tmp_group_name].is_default:
|
||||
old_group_cache[tmp_group_name].unlink()
|
||||
|
||||
def create_style(
|
||||
self, model_id, style_data, owner=False, is_default=True):
|
||||
'''
|
||||
create theme style from default info
|
||||
:return:
|
||||
'''
|
||||
default_groups = style_data['groups']
|
||||
mode = self.env['anita_theme_setting.theme_mode'].browse(model_id)
|
||||
scss_vars_cache = mode.get_scss_vars_cache()
|
||||
|
||||
group_datas = []
|
||||
for group_index, group in enumerate(default_groups):
|
||||
sub_groups = group["sub_groups"]
|
||||
sub_group_array = []
|
||||
for sub_group_index, sub_group in enumerate(sub_groups):
|
||||
|
||||
style_items = sub_group["style_items"]
|
||||
style_item_datas = []
|
||||
for style_item_index, style_item in enumerate(style_items):
|
||||
selectors = json.dumps(style_item['selectors'])
|
||||
var_vals = []
|
||||
var_infos = style_item['vars']
|
||||
for var_info in var_infos:
|
||||
ref_var = var_info.get('ref', False)
|
||||
if ref_var:
|
||||
if ref_var in scss_vars_cache:
|
||||
raise exceptions.ValidationError(
|
||||
_("The variable %s has been used") % ref_var)
|
||||
else:
|
||||
color = scss_vars_cache[ref_var]
|
||||
else:
|
||||
color = var_info.get('color', False)
|
||||
|
||||
var_vals.append((0, 0, {
|
||||
"name": var_info["name"],
|
||||
"is_default": is_default,
|
||||
"type": var_info["type"],
|
||||
"color": color,
|
||||
"image": var_info.get('image', False),
|
||||
"image_url": var_info.get('image_url', False),
|
||||
"svg": var_info.get('svg', False),
|
||||
"identity": var_info.get('identity', False)
|
||||
}))
|
||||
|
||||
tmp_style_item = dict()
|
||||
tmp_style_item['selectors'] = selectors
|
||||
tmp_style_item['sequence'] = style_item_index
|
||||
tmp_style_item['name'] = style_item["name"]
|
||||
tmp_style_item["is_default"] = is_default
|
||||
tmp_style_item["val_template"] = style_item["val_template"]
|
||||
tmp_style_item["vars"] = var_vals
|
||||
|
||||
style_item_datas.append((0, 0, tmp_style_item))
|
||||
|
||||
sub_group_array.append((0, 0, {
|
||||
"name": sub_group["name"],
|
||||
"sequence": sub_group_index,
|
||||
"style_items": style_item_datas,
|
||||
"is_default": True
|
||||
}))
|
||||
|
||||
group_datas.append((0, 0, {
|
||||
"name": group["name"],
|
||||
"sequence": group_index,
|
||||
"sub_groups": sub_group_array,
|
||||
"is_default": True
|
||||
}))
|
||||
|
||||
return self.env['anita_theme_setting.theme_style'].create({
|
||||
"name": style_data["name"],
|
||||
"theme_mode": model_id,
|
||||
"is_default": is_default,
|
||||
"groups": group_datas,
|
||||
"owner": owner
|
||||
})
|
||||
|
||||
@api.model
|
||||
def add_new_style(
|
||||
self, mode_id, style_data, owner=False, is_default=False):
|
||||
'''
|
||||
add new style, get the first style as the default, call from the front web
|
||||
:return:
|
||||
'''
|
||||
# if not owner, use the current owner
|
||||
if not owner:
|
||||
owner = self.env["anita_theme_setting.setting_manager"].get_current_owner()
|
||||
|
||||
theme_style = self.create_style(
|
||||
mode_id, style_data, owner, is_default)
|
||||
|
||||
# return the new style data
|
||||
style_data = theme_style.get_styles()[0]
|
||||
|
||||
return style_data
|
||||
|
||||
def clone_style(self, owner=False):
|
||||
'''
|
||||
clone style
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
if not owner:
|
||||
owner = self.env["anita_theme_setting.setting_manager"].get_current_owner()
|
||||
|
||||
style_data = self.get_styles()[0]
|
||||
return self.add_new_style(self.theme_mode.id, style_data, owner)
|
||||
|
||||
@api.model
|
||||
def import_new_style(self, mode_id, wizard_id):
|
||||
"""
|
||||
import new style
|
||||
:param mode_id:
|
||||
:param wizard_id:
|
||||
:return:
|
||||
"""
|
||||
wizard = self.env["anita_theme_setting.import_theme_style"].browse(
|
||||
wizard_id)
|
||||
style_data = json.loads(
|
||||
base64.decodebytes(wizard.file).decode('utf-8'))
|
||||
return self.add_new_style(mode_id, style_data)
|
||||
|
||||
def set_bk_image(self, wizard_id):
|
||||
"""
|
||||
set background image
|
||||
"""
|
||||
self.ensure_one()
|
||||
wizard = self.env["anita_theme_setting.background_image_wizard"].browse(
|
||||
wizard_id)
|
||||
|
||||
self.background_image = wizard.data
|
||||
self.opacity = wizard.opacity
|
||||
|
||||
# reutrn the image url
|
||||
url = self.get_background_image_url()
|
||||
|
||||
# clear cache
|
||||
self.clear_caches()
|
||||
|
||||
return url
|
||||
|
||||
def get_background_image_url(self):
|
||||
"""
|
||||
get image url
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.background_image:
|
||||
return 'web/image/anita_theme_setting.theme_style/' + str(self.id) + '/background_image'
|
||||
else:
|
||||
return False
|
||||
|
||||
def clear_style_css(self):
|
||||
"""
|
||||
clear style css when style change
|
||||
"""
|
||||
self.ensure_one()
|
||||
self.style_css = False
|
||||
@@ -1,332 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions
|
||||
import logging
|
||||
import json
|
||||
from .anita_utility import apply_alpha
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaThemeStyleItem(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.style_item'
|
||||
_order = 'sequence asc'
|
||||
_description = "style item"
|
||||
|
||||
sub_group = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.style_item_sub_group", string="sub group")
|
||||
theme_mode = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.theme_mode",
|
||||
related="sub_group.group.theme_style.theme_mode",
|
||||
store=True,
|
||||
string="theme mode")
|
||||
theme_style = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.theme_style",
|
||||
related = "sub_group.group.theme_style",
|
||||
string="theme style")
|
||||
|
||||
name = fields.Char(string="name", required=True, default="var1")
|
||||
val = fields.Char(string="val", compute="_compute_val")
|
||||
sequence = fields.Integer(string="sequence", default=0)
|
||||
mode = fields.Selection(
|
||||
selection=[('override', 'override'), ('primary', 'primary')],
|
||||
string="mode",
|
||||
default="override",
|
||||
help="override or primary, override is override the default style and primary is create a new style")
|
||||
val_template = fields.Char(
|
||||
string="val template",
|
||||
required=True,
|
||||
default="background: {var1}", help="Save the last val")
|
||||
vars = fields.One2many(
|
||||
string="vars",
|
||||
comodel_name="anita_theme_setting.theme_var",
|
||||
inverse_name="style_item")
|
||||
selectors = fields.Char(string="selectors", required=True)
|
||||
is_default = fields.Boolean(string="Is Default", default=True)
|
||||
type = fields.Selection(
|
||||
string="type",
|
||||
selection=[('normal', 'normal'),
|
||||
('login', 'login')],
|
||||
default='normal')
|
||||
login_style = fields.Char(string="login style", default="login_style1")
|
||||
|
||||
|
||||
def check_vars(self, default_mode, sub_group_id, default_vars):
|
||||
'''
|
||||
check vars
|
||||
:return:
|
||||
'''
|
||||
# check vars
|
||||
self.ensure_one()
|
||||
var_val_cache = default_mode.get_var_val_cache()
|
||||
|
||||
# get current var cache
|
||||
old_var_cache = {var.name: var for var in self.vars}
|
||||
default_var_cache = \
|
||||
{default_var["name"]: default_var for default_var in default_vars}
|
||||
|
||||
tmp_vals = []
|
||||
for default_var in default_vars:
|
||||
|
||||
color = default_var.get("color", False)
|
||||
ref_var = default_var.get("ref_var",False)
|
||||
if ref_var:
|
||||
if ref_var not in var_val_cache:
|
||||
color = False
|
||||
_logger.info("ref var {ref_var} not in var cache".format(ref_var=ref_var))
|
||||
else:
|
||||
color = var_val_cache[ref_var]
|
||||
|
||||
# item exits must keep
|
||||
if default_var["name"] not in old_var_cache:
|
||||
tmp_vals.append({
|
||||
"sub_group": sub_group_id,
|
||||
"name": default_var["name"],
|
||||
"style_item": self.id,
|
||||
"type": default_var["type"],
|
||||
"color": color,
|
||||
"image": default_var.get('image', False),
|
||||
"image_url": default_var.get('image_url', False),
|
||||
"svg": default_var.get('svg', False),
|
||||
"is_default": default_var.get('is_default', True),
|
||||
"identity": default_var.get('identity', False),
|
||||
"ref_var": ref_var,
|
||||
"disable_opacity": default_var.get('disable_opacity', False),
|
||||
})
|
||||
else:
|
||||
# keep the val, maybe user will
|
||||
tmp_var = old_var_cache[default_var["name"]]
|
||||
tmp_var.write({
|
||||
"type": default_var["type"],
|
||||
"is_default": default_var.get('is_default', True),
|
||||
"identity": default_var.get('identity', False),
|
||||
"color": color,
|
||||
"image": default_var.get('image', False),
|
||||
"image_url": default_var.get('image_url', False),
|
||||
"svg": default_var.get('svg', False),
|
||||
"ref_var": ref_var,
|
||||
"disable_opacity": default_var.get('disable_opacity', False),
|
||||
})
|
||||
|
||||
# create the missing vars
|
||||
self.env["anita_theme_setting.theme_var"].create(tmp_vals)
|
||||
|
||||
# unlink the old var
|
||||
for name in old_var_cache:
|
||||
if name not in default_var_cache:
|
||||
tmp_var = old_var_cache[name]
|
||||
tmp_var.unlink()
|
||||
|
||||
@api.depends('selectors', 'val_template', "vars")
|
||||
def _compute_val(self):
|
||||
'''
|
||||
compute the val
|
||||
:return:
|
||||
'''
|
||||
# check if mode has background image
|
||||
for record in self:
|
||||
# if every var is color and the color val is same as the ref var
|
||||
background_image = record.theme_style.background_image
|
||||
scss_var_cache = record.theme_mode.get_scss_var_record_cache()
|
||||
opacity = record.theme_style.opacity
|
||||
vars_cache = dict()
|
||||
all_color = True
|
||||
all_ref = True
|
||||
all_same = True
|
||||
for var in record.vars:
|
||||
if var.type == 'color':
|
||||
# check if has opacity
|
||||
color = var.color
|
||||
if var.ref_var and var.ref_var in scss_var_cache:
|
||||
ref_color = scss_var_cache[var.ref_var]
|
||||
if ref_color != color:
|
||||
all_same = False
|
||||
else:
|
||||
all_ref = False
|
||||
|
||||
try:
|
||||
if background_image:
|
||||
if not var.disable_opacity:
|
||||
color = apply_alpha(var.name, color, opacity, var.color_type)
|
||||
except Exception as e:
|
||||
_logger.error("apply alpha error {e}".format(e=e))
|
||||
|
||||
vars_cache[var.name] = color
|
||||
elif var.type == 'image':
|
||||
vars_cache[var.name] = var.image_file_url
|
||||
all_color = False
|
||||
elif var.type == 'image_url':
|
||||
vars_cache[var.name] = var.image_url
|
||||
all_color = False
|
||||
else:
|
||||
vars_cache[var.name] = var.svg
|
||||
all_color = False
|
||||
|
||||
# if all the color are the original color, them ignore it
|
||||
if all_color and all_ref and all_same:
|
||||
record.val = False
|
||||
else:
|
||||
if not record.vars:
|
||||
record.val = False
|
||||
else:
|
||||
try:
|
||||
record.val = str(record.val_template).format(**vars_cache)
|
||||
except Exception as error:
|
||||
record.val = False
|
||||
_logger.info('parse var value error, item name {item_name}->{error}'.format(
|
||||
item_name=record['name'], error=error))
|
||||
|
||||
def get_style_item_data(self):
|
||||
'''
|
||||
get the style item data
|
||||
:return:
|
||||
'''
|
||||
export_style = self.env.context.get('export_style', False)
|
||||
style_items = []
|
||||
for record in self:
|
||||
style_item = record.read(
|
||||
fields=["id", "name", "is_default", "val_template", "val", "selectors"])[0]
|
||||
|
||||
try:
|
||||
style_item['selectors'] = json.loads(style_item['selectors'])
|
||||
except Exception as error:
|
||||
_logger.info('try to decode selectors error!', error)
|
||||
|
||||
if not export_style:
|
||||
# maybe the image is very large, so do not read the image
|
||||
style_item["vars"] = \
|
||||
record.vars.read(
|
||||
["name", "identity", "type", "image_file_url", "image_url", "color", "svg", "color_type"])
|
||||
else:
|
||||
style_item["vars"] = \
|
||||
record.vars.read(
|
||||
["name", "identity", "type", "image", "image_url", "color", "svg", "color_type"])
|
||||
for tmp_var in style_item["vars"]:
|
||||
if tmp_var["image"]:
|
||||
tmp_var["image"] = str(tmp_var["image"])
|
||||
|
||||
style_items.append(style_item)
|
||||
return style_items
|
||||
|
||||
def write(self, vals):
|
||||
"""
|
||||
rewrite to check if the selectors are right
|
||||
:param vals:
|
||||
:return:
|
||||
"""
|
||||
super(AnitaThemeStyleItem, self).write(vals)
|
||||
|
||||
vars_cache = dict()
|
||||
for var in self.vars:
|
||||
if var.type == 'color':
|
||||
vars_cache[var.name] = var.color
|
||||
elif var.type == 'image':
|
||||
vars_cache[var.name] = var.image_file_url
|
||||
elif var.type == 'image_url':
|
||||
vars_cache[var.name] = var.image_url
|
||||
else:
|
||||
vars_cache[var.name] = var.svg
|
||||
|
||||
# check the var
|
||||
try:
|
||||
str(self.val_template).format(**vars_cache)
|
||||
except Exception as error:
|
||||
_logger.info('parse var value error, item name {item_name}->{error}'.format(
|
||||
item_name=self['name'], error=error))
|
||||
|
||||
def get_login_style_data(
|
||||
self, theme_style_id, login_style):
|
||||
"""
|
||||
get login style data
|
||||
:return:
|
||||
"""
|
||||
result = []
|
||||
theme_style = self.env["anita_theme_setting.theme_style"].browse(theme_style_id)
|
||||
sub_group_ids = theme_style.mapped('groups.sub_groups.id')
|
||||
style_items = self.search(
|
||||
[('type', '=', 'login'),
|
||||
('login_style', '=', login_style),
|
||||
('sub_group', 'in', sub_group_ids)])
|
||||
for style_item in style_items:
|
||||
val = style_item.val
|
||||
if val == "":
|
||||
continue
|
||||
if not style_item.selectors:
|
||||
continue
|
||||
try:
|
||||
selector = ','.join(json.loads(style_item["selectors"]))
|
||||
tmp_txt = selector + " {" + val + "}"
|
||||
result.append(tmp_txt)
|
||||
except Exception as error:
|
||||
_logger.info(error)
|
||||
return ";".join(result)
|
||||
|
||||
def delete_style_item(self):
|
||||
'''
|
||||
delete style item
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
self.unlink()
|
||||
|
||||
def create_style_item(
|
||||
self, default_mode, sub_group, style_item, style_item_index):
|
||||
'''
|
||||
create group from default data
|
||||
:return:
|
||||
'''
|
||||
selectors = json.dumps(style_item["selectors"])
|
||||
var_infos = style_item["vars"]
|
||||
var_val_cache = default_mode.get_var_val_cache()
|
||||
|
||||
var_vals = []
|
||||
for var_index, var_info in enumerate(var_infos):
|
||||
|
||||
color = var_info.get("color", False)
|
||||
ref_var = var_info.get("ref_var", False)
|
||||
if ref_var:
|
||||
if ref_var not in var_val_cache:
|
||||
color = False
|
||||
_logger.info("ref var not in var cache", ref_var)
|
||||
else:
|
||||
color = var_val_cache[ref_var]
|
||||
|
||||
var_vals.append((0, 0, {
|
||||
"name": var_info["name"],
|
||||
"type": var_info["type"],
|
||||
"sequence": var_index,
|
||||
"color": color,
|
||||
"image": var_info.get('image', False),
|
||||
"image_url": var_info.get('image_url', False),
|
||||
"svg": var_info.get('svg', False),
|
||||
"identity": var_info.get('identity', False),
|
||||
"ref_var": ref_var
|
||||
}))
|
||||
|
||||
style_item_val = {
|
||||
"name": style_item["name"],
|
||||
"sequence": style_item_index,
|
||||
"val_template": style_item["val_template"],
|
||||
"selectors": selectors,
|
||||
"vars": var_vals,
|
||||
"sub_group": sub_group
|
||||
}
|
||||
|
||||
return self.create([style_item_val])
|
||||
|
||||
def resert_var(self):
|
||||
"""
|
||||
reset var, resut to the derfault value
|
||||
:return:
|
||||
"""
|
||||
if not self.ref_var and self.type != 'color':
|
||||
return False
|
||||
# get mode first
|
||||
var_cache = self.sub_group.group.theme_style.theme_mode.get_scss_vars_cache()
|
||||
for var in self.vars.filtered(lambda x: x.type == 'color'):
|
||||
if var.ref_var:
|
||||
var.color = var_cache.get(var.ref_var, False)
|
||||
return True
|
||||
@@ -1,199 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions, _, exceptions
|
||||
import json
|
||||
|
||||
|
||||
class AnitaStyleGroup(models.Model):
|
||||
'''
|
||||
user theme style group
|
||||
'''
|
||||
_name = 'anita_theme_setting.style_item_group'
|
||||
_order = 'sequence asc'
|
||||
_description = "style item group"
|
||||
|
||||
name = fields.Char(string="name", required=True)
|
||||
is_default = fields.Boolean(string="is default", default=False)
|
||||
theme_style = fields.Many2one(
|
||||
string="theme style",
|
||||
comodel_name="anita_theme_setting.theme_style",
|
||||
ondelete="cascade")
|
||||
sub_groups = fields.One2many(
|
||||
string="sub groups",
|
||||
comodel_name="anita_theme_setting.style_item_sub_group",
|
||||
inverse_name="group")
|
||||
sequence = fields.Integer(string="sequence", default=0)
|
||||
|
||||
def check_sub_group(self, default_mode, sub_groups):
|
||||
'''
|
||||
check group items, if not exsits create it else update it
|
||||
need fixed
|
||||
:return:0
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
old_sub_group_cache = {
|
||||
sub_group.name: sub_group for sub_group in self.sub_groups}
|
||||
default_sub_group_cache = {
|
||||
default_group["name"]: default_group for default_group in sub_groups}
|
||||
|
||||
for sub_group_index, sub_group in enumerate(sub_groups):
|
||||
if sub_group["name"] not in old_sub_group_cache:
|
||||
self.env['anita_theme_setting.style_item_sub_group']\
|
||||
.create_sub_group(default_mode, self.id, sub_group, sub_group_index)
|
||||
else:
|
||||
# update the style item
|
||||
tmp_group = old_sub_group_cache[sub_group["name"]]
|
||||
tmp_group.write({
|
||||
"name": sub_group["name"],
|
||||
"is_default": True,
|
||||
"sequence": sub_group_index,
|
||||
})
|
||||
# check group item data
|
||||
style_items = sub_group["style_items"]
|
||||
tmp_group.check_style_items(default_mode, style_items)
|
||||
|
||||
# delete the style item (just not default, user may add item)
|
||||
for sub_group_name in old_sub_group_cache:
|
||||
if sub_group_name not in default_sub_group_cache \
|
||||
and not old_sub_group_cache[sub_group_name].is_default:
|
||||
old_sub_group_cache[sub_group_name].unlink()
|
||||
|
||||
def create_group_from_default_data(self, theme_style, group, group_index):
|
||||
'''
|
||||
create group from default data
|
||||
:return:
|
||||
'''
|
||||
val = {
|
||||
"name": group["name"],
|
||||
"theme_style": theme_style.id,
|
||||
"sequence": group_index,
|
||||
}
|
||||
|
||||
mode = theme_style.theme_mode # get theme mode
|
||||
scss_vars_cache = mode.get_scss_vars_cache()
|
||||
|
||||
sub_group_array = []
|
||||
sub_groups = group["sub_groups"]
|
||||
for sub_group_index, sub_group in enumerate(sub_groups):
|
||||
|
||||
style_item_vals = []
|
||||
items = sub_group["style_items"]
|
||||
for item_index, item in enumerate(items):
|
||||
selectors = json.dumps(item["selectors"])
|
||||
var_infos = item["vars"]
|
||||
|
||||
var_vals = []
|
||||
for var_index, var_info in enumerate(var_infos):
|
||||
|
||||
ref_var = var_info.get('ref', False)
|
||||
if ref_var:
|
||||
if ref_var in scss_vars_cache:
|
||||
raise exceptions.ValidationError(
|
||||
_("The variable %s has been used") % ref_var)
|
||||
else:
|
||||
color = scss_vars_cache[ref_var]
|
||||
else:
|
||||
color = var_info.get('color', False)
|
||||
|
||||
var_vals.append((0, 0, {
|
||||
"name": var_info["name"],
|
||||
"type": var_info["type"],
|
||||
"sequence": var_index,
|
||||
"color": color,
|
||||
"image": var_info.get('image', False),
|
||||
"image_url": var_info.get('image_url', False),
|
||||
"svg": var_info.get('svg', False),
|
||||
"identity": var_info.get('identity', False)
|
||||
}))
|
||||
|
||||
style_item_vals.append((0, 0, {
|
||||
"name": item["name"],
|
||||
"sequence": item_index,
|
||||
"sub_group": sub_group["name"],
|
||||
"selectors": selectors,
|
||||
"vars": var_vals
|
||||
}))
|
||||
|
||||
sub_group_array.append((0, 0, {
|
||||
"name": sub_group["name"],
|
||||
"sequence": sub_group_index,
|
||||
"style_items": style_item_vals,
|
||||
"is_default": True
|
||||
}))
|
||||
|
||||
val["sub_groups"] = sub_group_array
|
||||
return self.create([val])
|
||||
|
||||
@api.model
|
||||
def get_add_new_group_action(self, style_id):
|
||||
"""
|
||||
get add new group action
|
||||
:param style_id:
|
||||
:return:
|
||||
"""
|
||||
theme_style = self.env["anita_theme_setting.theme_style"].browse(style_id)
|
||||
old_groups = theme_style.groups
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "anita_theme_setting.style_item_group",
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
"context": {
|
||||
"default_theme_style": style_id,
|
||||
"default_sequence": len(old_groups) + 1
|
||||
},
|
||||
"views": [[self.env.ref('anita_theme_setting.style_item_group_form').id, "form"]]
|
||||
}
|
||||
|
||||
def get_group_data(self):
|
||||
"""
|
||||
get sub group data
|
||||
:return:
|
||||
"""
|
||||
group_datas = []
|
||||
for record in self:
|
||||
group_datas.append({
|
||||
"id": record.id,
|
||||
"name": record.name,
|
||||
"is_default": record.is_default,
|
||||
"sequence": record.sequence,
|
||||
"sub_groups": record.sub_groups.get_sub_group_data()
|
||||
})
|
||||
return group_datas
|
||||
|
||||
def get_add_sub_group_action(self):
|
||||
"""
|
||||
get add sub group action
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "anita_theme_setting.style_item_sub_group",
|
||||
"view_mode": 'form',
|
||||
"target": "new",
|
||||
"context": {
|
||||
"default_group": self.id,
|
||||
},
|
||||
"views": [[self.env.ref('anita_theme_setting.style_item_sub_group_form').id, "form"]]
|
||||
}
|
||||
|
||||
def delete_item_group(self):
|
||||
"""
|
||||
delete item group
|
||||
:return:
|
||||
"""
|
||||
if len(self.sub_groups) > 0:
|
||||
raise exceptions.ValidationError(
|
||||
'there has some sub group left, please delete them first!')
|
||||
self.unlink()
|
||||
|
||||
def get_sub_group_data(self):
|
||||
"""
|
||||
get sub group data
|
||||
:return:
|
||||
"""
|
||||
sub_group_datas = []
|
||||
for record in self:
|
||||
sub_group_datas.append(record.get_sub_group_data())
|
||||
return sub_group_datas
|
||||
@@ -1,147 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, exceptions
|
||||
import logging, json
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AnitaStyleSubGroup(models.Model):
|
||||
'''
|
||||
user theme style group
|
||||
'''
|
||||
_name = 'anita_theme_setting.style_item_sub_group'
|
||||
_order = 'sequence asc'
|
||||
_description = "style item group"
|
||||
|
||||
name = fields.Char(string="name", required=True)
|
||||
is_default = fields.Boolean(string="is default", default=False)
|
||||
group = fields.Many2one(string="group",
|
||||
comodel_name="anita_theme_setting.style_item_group",
|
||||
ondelete="cascade")
|
||||
sequence = fields.Integer(string="sequence", default=0)
|
||||
style_items = fields.One2many(
|
||||
string="style items",
|
||||
comodel_name="anita_theme_setting.style_item",
|
||||
inverse_name="sub_group")
|
||||
|
||||
def check_style_items(self, default_mode, style_items):
|
||||
'''
|
||||
check group items, if not exsits create it else update it
|
||||
need fixed
|
||||
:return:0
|
||||
'''
|
||||
self.ensure_one()
|
||||
|
||||
old_style_item_cache = {
|
||||
style_item.name: style_item for style_item in self.style_items if style_item.is_default}
|
||||
new_style_item_cache = {
|
||||
style_item["name"]: style_item for style_item in style_items}
|
||||
|
||||
for item_index, style_item in enumerate(style_items):
|
||||
if style_item["name"] not in old_style_item_cache:
|
||||
self.env["anita_theme_setting.style_item"].create_style_item(
|
||||
default_mode, self.id, style_item, item_index)
|
||||
else:
|
||||
old_style_item = old_style_item_cache[style_item["name"]]
|
||||
old_style_item.write({
|
||||
"selectors": json.dumps(style_item["selectors"]),
|
||||
"name": style_item["name"],
|
||||
"val_template": style_item["val_template"],
|
||||
"sequence": item_index,
|
||||
})
|
||||
item_vars = style_item["vars"]
|
||||
old_style_item.check_vars(default_mode, self.id, item_vars)
|
||||
|
||||
# delete the style item (just not default, user may add item)
|
||||
for tmp_style_item in self.style_items:
|
||||
if tmp_style_item.name not in new_style_item_cache:
|
||||
old_style_item_cache[tmp_style_item.name].unlink()
|
||||
|
||||
def create_sub_group(self, default_mode, group, sub_group, sub_group_index):
|
||||
'''
|
||||
create sub group from default data
|
||||
:return:
|
||||
'''
|
||||
val = {
|
||||
"name": sub_group["name"],
|
||||
"group": group,
|
||||
"sequence": sub_group_index,
|
||||
"is_default": True
|
||||
}
|
||||
|
||||
var_val_cache = default_mode.get_var_val_cache()
|
||||
|
||||
style_item_vals = []
|
||||
style_items = sub_group["style_items"]
|
||||
for item_index, item in enumerate(style_items):
|
||||
try:
|
||||
selectors = json.dumps(item["selectors"])
|
||||
except Exception as error:
|
||||
_logger.info('parse selectors error:', error)
|
||||
continue
|
||||
|
||||
var_infos = item["vars"]
|
||||
var_vals = []
|
||||
for var_index, var_info in enumerate(var_infos):
|
||||
|
||||
color = var_info.get("color", False)
|
||||
ref_var = var_info.get("ref_var", False)
|
||||
if ref_var:
|
||||
if ref_var not in var_val_cache:
|
||||
color = False
|
||||
_logger.info('ref_var not in var_val_cache:', ref_var)
|
||||
else:
|
||||
color = var_val_cache[ref_var]
|
||||
|
||||
var_vals.append((0, 0, {
|
||||
"name": var_info["name"],
|
||||
"type": var_info["type"],
|
||||
"sequence": var_index,
|
||||
"color": color,
|
||||
"image": var_info.get('image', False),
|
||||
"image_url": var_info.get('image_url', False),
|
||||
"svg": var_info.get('svg', False),
|
||||
"identity": var_info.get('identity', False),
|
||||
"ref_var": ref_var
|
||||
}))
|
||||
|
||||
style_item_vals.append((0, 0, {
|
||||
"name": item["name"],
|
||||
"sequence": item_index,
|
||||
"sub_group": sub_group["name"],
|
||||
"selectors": selectors,
|
||||
"vars": var_vals
|
||||
}))
|
||||
|
||||
val["style_items"] = style_item_vals
|
||||
|
||||
return self.create([val])
|
||||
|
||||
def get_sub_group_data(self):
|
||||
'''
|
||||
get group data
|
||||
:return:
|
||||
'''
|
||||
sub_groups = []
|
||||
for record in self:
|
||||
sub_groups.append({
|
||||
"id": record.id,
|
||||
"name": record.name,
|
||||
"style_items": record.style_items.get_style_item_data(),
|
||||
"sequence": record.sequence,
|
||||
"is_default": record.is_default,
|
||||
"expanded": False
|
||||
})
|
||||
return sub_groups
|
||||
|
||||
def delete_sub_group(self):
|
||||
"""
|
||||
delete sub group
|
||||
:return:
|
||||
"""
|
||||
self.ensure_one()
|
||||
if len(self.style_items) > 0:
|
||||
raise exceptions.ValidationError(
|
||||
'there has some style item left, please delete the style item first!')
|
||||
self.unlink()
|
||||
@@ -1,124 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models, fields, api, exceptions
|
||||
import datetime
|
||||
from datetime import date, datetime
|
||||
|
||||
|
||||
class AnitaThemeUsers(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_inherit = "res.users"
|
||||
_description = 'user setting'
|
||||
|
||||
setting_id = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.setting_base",
|
||||
ondelete="cascade",
|
||||
string="settings")
|
||||
|
||||
@api.model
|
||||
def save_theme_setting(self, settings):
|
||||
'''
|
||||
save settings, create if there is no user info
|
||||
just use full when the mode is user wide mode
|
||||
:param settings:
|
||||
:return:
|
||||
'''
|
||||
self.ensure_setting_id()
|
||||
self.setting_id.write(settings)
|
||||
|
||||
def ensure_setting_id(self):
|
||||
'''
|
||||
check setting id
|
||||
:return:
|
||||
'''
|
||||
self.ensure_one()
|
||||
if self.setting_id:
|
||||
return
|
||||
|
||||
# copy use the global default data
|
||||
values = self.env['res.config.settings'].get_theme_values()
|
||||
|
||||
# get fields of anita_theme_setting.setting_base
|
||||
field_names = [name for name, field in self.setting_id._fields.items() if name not in [
|
||||
'id', 'create_uid', 'create_date', 'write_uid', 'write_date']]
|
||||
|
||||
# remove name not in fields
|
||||
values = {k: v for k, v in values.items() if k in field_names}
|
||||
|
||||
del values["current_theme_mode"]
|
||||
del values["current_theme_style"]
|
||||
|
||||
if not self.setting_id:
|
||||
tmp_record = self.env["anita_theme_setting.setting_base"].create(values)
|
||||
self.setting_id = tmp_record.id
|
||||
|
||||
def get_theme_setting(self):
|
||||
'''
|
||||
get settings
|
||||
:return:
|
||||
'''
|
||||
record = self.env.user
|
||||
record.ensure_setting_id()
|
||||
field_names = [name for name, field in record.setting_id._fields.items() if name not in [
|
||||
"icon128x128", "icon144x144", "icon152x152", "icon192x192", "icon256x256", "icon512x512"]]
|
||||
result = record.setting_id.read(fields=field_names)[0]
|
||||
result["current_theme_mode"] = record.setting_id.current_theme_mode.id
|
||||
result["current_theme_style"] = record.setting_id.current_theme_style.id
|
||||
for key, item in result.items():
|
||||
if isinstance(item, datetime):
|
||||
result[key] = fields.Datetime.to_string(item)
|
||||
if isinstance(item, date):
|
||||
result[key] = fields.Date.to_string(item)
|
||||
|
||||
return result
|
||||
|
||||
def check_default_data(self):
|
||||
"""
|
||||
check default data
|
||||
:return:
|
||||
"""
|
||||
# check mode data
|
||||
owner = 'res.users, {user_id}'.format(user_id=self.id)
|
||||
self.env["anita_theme_setting.theme_mode"].check_default_mode_data(owner)
|
||||
|
||||
@api.model
|
||||
def get_user_xml_groups(self):
|
||||
'''
|
||||
:return:
|
||||
'''
|
||||
group_ids = self.env.user.groups_id.ids
|
||||
records = self.env['ir.model.data']\
|
||||
.sudo() \
|
||||
.search_read([('model', '=', 'res.groups'),
|
||||
('res_id', 'in', group_ids)], fields=["complete_name"])
|
||||
return {record["complete_name"]: True for record in records}
|
||||
|
||||
def get_mode_domain(self):
|
||||
"""
|
||||
get mode domain dynamic
|
||||
:return:
|
||||
"""
|
||||
owner = 'res.users, {user_id}'.format(user_id=self.id)
|
||||
return [('owner', '=', owner)]
|
||||
|
||||
@api.model
|
||||
def get_group_infos(self):
|
||||
'''
|
||||
:return:
|
||||
'''
|
||||
group_ids = self.env.user.groups_id.ids
|
||||
records = self.env['ir.model.data']\
|
||||
.sudo() \
|
||||
.search_read(
|
||||
[('model', '=', 'res.groups'),
|
||||
('res_id', 'in', group_ids)],
|
||||
fields=["complete_name"])
|
||||
groups = {record["complete_name"]: True for record in records}
|
||||
|
||||
return {
|
||||
"groups": groups,
|
||||
"group_ids": group_ids,
|
||||
"user_id": self.env.user.id
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class AnitaThemeVar(models.Model):
|
||||
'''
|
||||
user theme style setting
|
||||
'''
|
||||
_name = 'anita_theme_setting.theme_var'
|
||||
_order = 'sequence asc'
|
||||
_description = "Theme Var"
|
||||
|
||||
sequence = fields.Integer(string="sequence", default=0)
|
||||
style_item = fields.Many2one(
|
||||
comodel_name="anita_theme_setting.style_item",
|
||||
string="style_item",
|
||||
required=True,
|
||||
ondelete="cascade")
|
||||
type = fields.Selection(
|
||||
selection=[('color', 'color'),
|
||||
('image', 'image'),
|
||||
('image_url', 'image_url'),
|
||||
('svg', 'svg')],
|
||||
string="type",
|
||||
default="color")
|
||||
|
||||
name = fields.Char(string="Name", default="var1")
|
||||
color = fields.Char(string="Val")
|
||||
color_type = fields.Selection(
|
||||
selection=[
|
||||
('background', 'background'),
|
||||
('text', 'text'),
|
||||
('border', 'border'),
|
||||
('shadow', 'shadow')],
|
||||
default='background', string="color type", help="Type use to apply aphla!")
|
||||
disable_opacity = fields.Boolean(string="Disable Opacity", default=False)
|
||||
image = fields.Binary(string="image", attachment=True)
|
||||
image_file_url = fields.Char(string="Image File Url", compute="_compute_image_file_url")
|
||||
image_url = fields.Char(string="Image Url")
|
||||
svg = fields.Text(string="Svg")
|
||||
ref_var = fields.Char(string="Ref Var Name")
|
||||
opacity = fields.Float(string="Opacity", default=1.0)
|
||||
|
||||
identity = fields.Char(string="identify")
|
||||
is_default = fields.Boolean(string="Is Default", default=False)
|
||||
|
||||
@api.depends('image')
|
||||
def _compute_image_file_url(self):
|
||||
"""
|
||||
compute image file url
|
||||
:return:
|
||||
"""
|
||||
for record in self:
|
||||
if record.type == 'image' and record.image:
|
||||
record.image_file_url = \
|
||||
'/web/image/anita_theme_setting.theme_var/{var_id}/image'.format(
|
||||
var_id=record.id)
|
||||
else:
|
||||
record.image_file_url = False
|
||||
|
||||
def get_edit_var_action(self):
|
||||
"""
|
||||
get edit action
|
||||
:return:
|
||||
"""
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "anita_theme_setting.theme_var",
|
||||
"res_id": self.id,
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
"views": [[self.env.ref(
|
||||
'anita_theme_setting.edit_theme_var_form').id, "form"]]
|
||||
}
|
||||
|
||||
def get_var_data(self):
|
||||
"""
|
||||
get var data
|
||||
:return:
|
||||
"""
|
||||
return self.read(
|
||||
fields=["name", "identity", "type",
|
||||
"image_file_url", "image_url", "color", "svg"])
|
||||
|
||||
def reset_var(self):
|
||||
"""
|
||||
reset var
|
||||
:return:
|
||||
"""
|
||||
if not self.ref_var and self.type != 'color':
|
||||
return False
|
||||
# get mode first
|
||||
var_cache = self.style_item.sub_group.group.theme_style.theme_mode.get_scss_vars_cache()
|
||||
if self.ref_var in var_cache:
|
||||
self.color = var_cache.get(self.ref_var, False)
|
||||
return True
|
||||
@@ -1,132 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from math import fabs
|
||||
from odoo import exceptions
|
||||
|
||||
def apply_alpha(var_name, color, alpha, color_type = False):
|
||||
"""
|
||||
safe alpha
|
||||
:param color:
|
||||
:param alpha:
|
||||
:return:
|
||||
"""
|
||||
if not color:
|
||||
return ''
|
||||
|
||||
color.strip()
|
||||
|
||||
# check var_name endwith text_color
|
||||
if var_name.endswith("text_color") or color_type == "text_color":
|
||||
return color
|
||||
|
||||
# replace white to fff and black to 000 etc
|
||||
if color == "white":
|
||||
color = "fff"
|
||||
elif color == "black":
|
||||
color = "000"
|
||||
elif color == "red":
|
||||
color = "f00"
|
||||
elif color == "green":
|
||||
color = "0f0"
|
||||
elif color == "blue":
|
||||
color = "00f"
|
||||
elif color == "yellow":
|
||||
color = "ff0"
|
||||
|
||||
# check if start with rgba
|
||||
if color.startswith("rgba"):
|
||||
# get the content between ( and )
|
||||
color = color[color.find("(") + 1:color.find(")")]
|
||||
# get the alpha
|
||||
rgba_color = color.split(",")
|
||||
if len(rgba_color) == 4:
|
||||
# convert to float
|
||||
tmp = float(rgba_color[3])
|
||||
# check var_name has shadow in it
|
||||
tmp = alpha * tmp
|
||||
# make rgba string
|
||||
tmp = "rgba({rgba_color[0]},{rgba_color[1]},{rgba_color[2]},{tmp})".format(
|
||||
rgba_color=rgba_color,
|
||||
tmp=tmp)
|
||||
return tmp
|
||||
else:
|
||||
return color
|
||||
# check if start with # and length is 9
|
||||
elif color.startswith("#"):
|
||||
if len(color) == 9 or len(color) == 7:
|
||||
# get red color
|
||||
red = color[1:3]
|
||||
# get green color
|
||||
green = color[3:5]
|
||||
# get blue color
|
||||
blue = color[5:7]
|
||||
# get the old alpha
|
||||
if len(color) == 9:
|
||||
old_alpha = color[7:9]
|
||||
# convert to int
|
||||
old_alpha = int(old_alpha, 16) / 255.0
|
||||
new_alpha = int(old_alpha * alpha)
|
||||
else:
|
||||
new_alpha = alpha
|
||||
|
||||
# make rgba string
|
||||
return "rgba({red},{green},{blue},{new_alpha})".format(
|
||||
red=int(red, 16),
|
||||
green=int(green, 16),
|
||||
blue=int(blue, 16),
|
||||
new_alpha=new_alpha)
|
||||
|
||||
# color like #ffff
|
||||
elif len(color) == 5:
|
||||
# get red color
|
||||
red = color[1:2]
|
||||
# get green color
|
||||
green = color[2:3]
|
||||
# get blue color
|
||||
blue = color[3:4]
|
||||
# alpha
|
||||
old_alpha = color[4:5]
|
||||
# convert to int
|
||||
old_alpha = int(old_alpha + old_alpha, 16) / 255.0
|
||||
new_alpha = old_alpha * alpha
|
||||
|
||||
# make rgba string
|
||||
return "rgba({red},{green},{blue},{alpha})".format(
|
||||
red=int(red + red, 16),
|
||||
green=int(green + green, 16),
|
||||
blue=int(blue + blue, 16),
|
||||
alpha=new_alpha)
|
||||
|
||||
# color like #fff
|
||||
elif len(color) == 4:
|
||||
# get red color
|
||||
red = color[1:2]
|
||||
# get green color
|
||||
green = color[2:3]
|
||||
# get blue color
|
||||
blue = color[3:4]
|
||||
# make rgba string
|
||||
return "rgba({red},{green},{blue},{alpha})".format(
|
||||
red=int(red + red, 16),
|
||||
green=int(green +green, 16),
|
||||
blue=int(blue + blue, 16),
|
||||
alpha=alpha)
|
||||
else:
|
||||
return color
|
||||
else:
|
||||
# check if start with rgb
|
||||
if color.startswith("rgb"):
|
||||
# get the content between ( and )
|
||||
color = color[color.find("(") + 1:color.find(")")]
|
||||
# get the alpha
|
||||
rgb_color = color.split(",")
|
||||
if len(rgb_color) == 3:
|
||||
# make rgba string
|
||||
tmp = "rgba({rgb_color[0]},{rgb_color[1]},{rgb_color[2]},{alpha})".format(
|
||||
rgb_color=rgb_color,
|
||||
alpha=alpha)
|
||||
return tmp
|
||||
else:
|
||||
return color
|
||||
else:
|
||||
return 'rgba({color},{alpha})'.format(color=color, alpha=alpha)
|
||||
@@ -1,16 +0,0 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
anita_theme_setting_style_item,anita_theme_setting.style_item,model_anita_theme_setting_style_item,,1,1,1,1
|
||||
anita_theme_setting_theme_style,anita_theme_setting.theme_style,model_anita_theme_setting_theme_style,,1,1,1,1
|
||||
anita_theme_setting_theme_mode,anita_theme_setting.theme_mode,model_anita_theme_setting_theme_mode,,1,1,1,1
|
||||
anita_theme_setting_style_item_group,anita_theme_setting.style_item_group,model_anita_theme_setting_style_item_group,,1,1,1,1
|
||||
anita_theme_setting_style_item_sub_group,anita_theme_setting.style_item_sub_group,model_anita_theme_setting_style_item_sub_group,,1,1,1,1
|
||||
anita_theme_setting_setting_base,anita_theme_setting.setting_base,model_anita_theme_setting_setting_base,,1,1,1,1
|
||||
anita_theme_setting_theme_var,anita_theme_setting.theme_var,model_anita_theme_setting_theme_var,,1,1,1,1
|
||||
anita_theme_setting_setting_manager,anita_theme_setting.setting_manager,model_anita_theme_setting_setting_manager,,1,1,1,1
|
||||
anita_theme_setting_default_mode_data,anita_theme_setting.default_mode_data,model_anita_theme_setting_default_mode_data,,1,1,1,1
|
||||
anita_theme_setting_mode_template,anita_theme_setting.mode_template,model_anita_theme_setting_mode_template,,1,1,1,1
|
||||
anita_theme_setting_import_theme_style,anita_theme_setting_import_theme_style,model_anita_theme_setting_import_theme_style,,1,1,1,1
|
||||
anita_theme_setting_template_var_val,anita_theme_setting.template_var_val,model_anita_theme_setting_template_var_val,,1,1,1,1
|
||||
access_anita_theme_setting_template_var_val,anita_theme_setting.template_var_val,model_anita_theme_setting_template_var_val,,1,1,1,1
|
||||
access_anita_theme_setting_background_image_wizard,anita_theme_setting.background_image_wizard,model_anita_theme_setting_background_image_wizard,,1,1,1,1
|
||||
access_anita_theme_setting_favorite_menu',anita_theme_setting.favorite_menu,model_anita_theme_setting_favorite_menu,,1,1,1,1
|
||||
|
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 21 MiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 550 KiB |
|
Before Width: | Height: | Size: 8.6 MiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 760 KiB |
|
Before Width: | Height: | Size: 21 MiB |
|
Before Width: | Height: | Size: 50 MiB |
|
Before Width: | Height: | Size: 8.6 MiB |
@@ -1,128 +0,0 @@
|
||||
<div class="container">
|
||||
|
||||
<div class="oe_styling_v8">
|
||||
<div class="shadow row my-5 p-3 pb-2 overflow-hidden">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<p style="width: 100%; font-size: 18px; text-indent:1em;" class="px-sm-4 p-2">
|
||||
Anita theme setting is theme configer for our themes, it support to change theme color, font, background, header, footer, menu, etc. and also support to change theme layout, it is very easy to use and very useful for our themes.
|
||||
</p>
|
||||
</div>
|
||||
<img src="./banner.png" style="width:100%; " />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="w-100 text-center mb-3">(Current Release V16.0.0.1 -- 2022.11.22)</h2>
|
||||
|
||||
<div class="overflow-hidden mb-5 my-3 p-4 deep-3" style="background-color:#fbfcfd; border-radius:20px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Theme Settings</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<h5>
|
||||
there has a theme setting configer, it can set the button style\ input style \ radio style \ checkbox style etc
|
||||
</h5>
|
||||
<img src="./theme_setting.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden mb-5 my-3 p-4 deep-3" style="background-color:#fbfcfd; border-radius:20px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Theme Style</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<h5>
|
||||
there has a theme configer, it can set the theme color\ font\ background\ header\ footer\ menu etc
|
||||
</h5>
|
||||
<img src="./theme_style.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden mb-5 my-3 p-4 deep-3" style="background-color:#fbfcfd; border-radius:20px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Semi Trans Support</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<h5>
|
||||
user can set a bk image and set the alpha for elements, it will be very beautiful
|
||||
</h5>
|
||||
<img src="./transparent.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden mb-5 my-3 p-4 deep-3" style="background-color:#fbfcfd; border-radius:20px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Back Image Support</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<h5>
|
||||
user can set a bk image and set the alpha for elements, it will be very beautiful
|
||||
</h5>
|
||||
<img src="./transparent2.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 style="text-align: center">
|
||||
Our Other Popular Apps
|
||||
</h2>
|
||||
|
||||
<div class="shadow row my-5 p-3 pb-2 overflow-hidden"
|
||||
style="background-color:#fbfcfd; border-radius: 5px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Awesome theme Pro</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<a href="https://apps.odoo.com/apps/themes/15.0/awesome_theme_pro/">
|
||||
<img src="./awesome_theme.gif" class="img img-fluid mt-3" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-hidden mb-5 my-3 p-4 deep-3" style="background-color:#fbfcfd; border-radius:20px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Config Theme Styles</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<h5>
|
||||
there has a theme configer, you can change the color, image and icons of the theme
|
||||
</h5>
|
||||
<img src="./change_background.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shadow row my-5 p-3 pb-2 overflow-hidden"
|
||||
style="background-color:#fbfcfd; border-radius: 5px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Anita Modeler</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<a href="https://apps.odoo.com/apps/modules/14.0/anita_modeler/">
|
||||
<img src="./anita_modeler.png" class="img img-fluid mt-3" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="shadow row my-5 p-3 pb-2 overflow-hidden"
|
||||
style="background-color:#fbfcfd; border-radius: 5px; border: 1px solid #d2caca66;">
|
||||
<div class="container">
|
||||
<h3 style="opacity:1; padding:10px 0px; color:#201111; letter-spacing:0.2em; text-transform:uppercase">
|
||||
<b>Anita List</b>
|
||||
</h3>
|
||||
<hr class="mb-4 mt-0">
|
||||
<a href="https://apps.odoo.com/apps/modules/15.0/anita_list/">
|
||||
<img src="./anita_list.gif" class="img img-fluid mt-3" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 716 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 453 KiB |
|
Before Width: | Height: | Size: 740 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 460 KiB |
|
Before Width: | Height: | Size: 294 KiB |
@@ -1,507 +0,0 @@
|
||||
/***
|
||||
Spectrum Colorpicker v1.8.1
|
||||
https://github.com/bgrins/spectrum
|
||||
Author: Brian Grinstead
|
||||
License: MIT
|
||||
***/
|
||||
|
||||
.sp-container {
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
display:inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
/* https://github.com/bgrins/spectrum/issues/40 */
|
||||
z-index: 9999994;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sp-container.sp-flat {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Fix for * { box-sizing: border-box; } */
|
||||
.sp-container,
|
||||
.sp-container * {
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
|
||||
.sp-top {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
display:inline-block;
|
||||
}
|
||||
.sp-top-inner {
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
bottom:0;
|
||||
right:0;
|
||||
}
|
||||
.sp-color {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
bottom:0;
|
||||
right:20%;
|
||||
}
|
||||
.sp-hue {
|
||||
position: absolute;
|
||||
top:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
left:84%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sp-clear-enabled .sp-hue {
|
||||
top:33px;
|
||||
height: 77.5%;
|
||||
}
|
||||
|
||||
.sp-fill {
|
||||
padding-top: 80%;
|
||||
}
|
||||
.sp-sat, .sp-val {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
}
|
||||
|
||||
.sp-alpha-enabled .sp-top {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.sp-alpha-enabled .sp-alpha {
|
||||
display: block;
|
||||
}
|
||||
.sp-alpha-handle {
|
||||
position:absolute;
|
||||
top:-4px;
|
||||
bottom: -4px;
|
||||
width: 6px;
|
||||
left: 50%;
|
||||
cursor: pointer;
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
opacity: .8;
|
||||
}
|
||||
.sp-alpha {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: -14px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 8px;
|
||||
}
|
||||
.sp-alpha-inner {
|
||||
border: solid 1px #333;
|
||||
}
|
||||
|
||||
.sp-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sp-clear.sp-clear-display {
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.sp-clear-enabled .sp-clear {
|
||||
display: block;
|
||||
position:absolute;
|
||||
top:0px;
|
||||
right:0;
|
||||
bottom:0;
|
||||
left:84%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Don't allow text selection */
|
||||
.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
|
||||
-webkit-user-select:none;
|
||||
-moz-user-select: -moz-none;
|
||||
-o-user-select:none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.sp-container.sp-input-disabled .sp-input-container {
|
||||
display: none;
|
||||
}
|
||||
.sp-container.sp-buttons-disabled .sp-button-container {
|
||||
display: none;
|
||||
}
|
||||
.sp-container.sp-palette-buttons-disabled .sp-palette-button-container {
|
||||
display: none;
|
||||
}
|
||||
.sp-palette-only .sp-picker-container {
|
||||
display: none;
|
||||
}
|
||||
.sp-palette-disabled .sp-palette-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sp-initial-disabled .sp-initial {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
|
||||
.sp-sat {
|
||||
background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
|
||||
background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
|
||||
background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
|
||||
background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
|
||||
background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
|
||||
background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
|
||||
filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
|
||||
}
|
||||
.sp-val {
|
||||
background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
|
||||
background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
|
||||
background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
|
||||
background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
|
||||
background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
|
||||
background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
|
||||
filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
|
||||
}
|
||||
|
||||
.sp-hue {
|
||||
background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
|
||||
background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
|
||||
background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
|
||||
background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
|
||||
background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
|
||||
}
|
||||
|
||||
/* IE filters do not support multiple color stops.
|
||||
Generate 6 divs, line them up, and do two color gradients for each.
|
||||
Yes, really.
|
||||
*/
|
||||
.sp-1 {
|
||||
height:17%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
|
||||
}
|
||||
.sp-2 {
|
||||
height:16%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
|
||||
}
|
||||
.sp-3 {
|
||||
height:17%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
|
||||
}
|
||||
.sp-4 {
|
||||
height:17%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
|
||||
}
|
||||
.sp-5 {
|
||||
height:16%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
|
||||
}
|
||||
.sp-6 {
|
||||
height:17%;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
|
||||
}
|
||||
|
||||
.sp-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Clearfix hack */
|
||||
.sp-cf:before, .sp-cf:after { content: ""; display: table; }
|
||||
.sp-cf:after { clear: both; }
|
||||
.sp-cf { *zoom: 1; }
|
||||
|
||||
/* Mobile devices, make hue slider bigger so it is easier to slide */
|
||||
@media (max-device-width: 480px) {
|
||||
.sp-color { right: 40%; }
|
||||
.sp-hue { left: 63%; }
|
||||
.sp-fill { padding-top: 60%; }
|
||||
}
|
||||
.sp-dragger {
|
||||
border-radius: 5px;
|
||||
height: 5px;
|
||||
width: 5px;
|
||||
border: 1px solid #fff;
|
||||
background: #000;
|
||||
cursor: pointer;
|
||||
position:absolute;
|
||||
top:0;
|
||||
left: 0;
|
||||
}
|
||||
.sp-slider {
|
||||
position: absolute;
|
||||
top:0;
|
||||
cursor:pointer;
|
||||
height: 3px;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
border: 1px solid #000;
|
||||
background: white;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
/*
|
||||
Theme authors:
|
||||
Here are the basic themeable display options (colors, fonts, global widths).
|
||||
See http://bgrins.github.io/spectrum/themes/ for instructions.
|
||||
*/
|
||||
|
||||
.sp-container {
|
||||
border-radius: 0;
|
||||
background-color: #ECECEC;
|
||||
border: solid 1px #f0c49B;
|
||||
padding: 0;
|
||||
}
|
||||
.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear {
|
||||
font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.sp-top {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.sp-color, .sp-hue, .sp-clear {
|
||||
border: solid 1px #666;
|
||||
}
|
||||
|
||||
/* Input */
|
||||
.sp-input-container {
|
||||
float:right;
|
||||
width: 100px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.sp-initial-disabled .sp-input-container {
|
||||
width: 100%;
|
||||
}
|
||||
.sp-input {
|
||||
font-size: 12px !important;
|
||||
border: 1px inset;
|
||||
padding: 4px 5px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
background:transparent;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
}
|
||||
.sp-input:focus {
|
||||
border: 1px solid orange;
|
||||
}
|
||||
.sp-input.sp-validation-error {
|
||||
border: 1px solid red;
|
||||
background: #fdd;
|
||||
}
|
||||
.sp-picker-container , .sp-palette-container {
|
||||
float:left;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
padding-bottom: 300px;
|
||||
margin-bottom: -290px;
|
||||
}
|
||||
.sp-picker-container {
|
||||
width: 172px;
|
||||
border-left: solid 1px #fff;
|
||||
}
|
||||
|
||||
/* Palettes */
|
||||
.sp-palette-container {
|
||||
border-right: solid 1px #ccc;
|
||||
}
|
||||
|
||||
.sp-palette-only .sp-palette-container {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.sp-palette .sp-thumb-el {
|
||||
display: block;
|
||||
position:relative;
|
||||
float:left;
|
||||
width: 24px;
|
||||
height: 15px;
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
border:solid 2px transparent;
|
||||
}
|
||||
.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
|
||||
border-color: orange;
|
||||
}
|
||||
.sp-thumb-el {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
/* Initial */
|
||||
.sp-initial {
|
||||
float: left;
|
||||
border: solid 1px #333;
|
||||
}
|
||||
.sp-initial span {
|
||||
width: 30px;
|
||||
height: 25px;
|
||||
border:none;
|
||||
display:block;
|
||||
float:left;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.sp-initial .sp-clear-display {
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.sp-palette-button-container,
|
||||
.sp-button-container {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* Replacer (the little preview div that shows up instead of the <input>) */
|
||||
.sp-replacer {
|
||||
margin:0;
|
||||
overflow:hidden;
|
||||
cursor:pointer;
|
||||
padding: 4px;
|
||||
display:inline-block;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
border: solid 1px #91765d;
|
||||
background: #eee;
|
||||
color: #333;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sp-replacer:hover, .sp-replacer.sp-active {
|
||||
border-color: #F0C49B;
|
||||
color: #111;
|
||||
}
|
||||
.sp-replacer.sp-disabled {
|
||||
cursor:default;
|
||||
border-color: silver;
|
||||
color: silver;
|
||||
}
|
||||
.sp-dd {
|
||||
padding: 2px 0;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
float:left;
|
||||
font-size:10px;
|
||||
}
|
||||
.sp-preview {
|
||||
position:relative;
|
||||
width:25px;
|
||||
height: 20px;
|
||||
border: solid 1px #222;
|
||||
margin-right: 5px;
|
||||
float:left;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.sp-palette {
|
||||
*width: 220px;
|
||||
max-width: 220px;
|
||||
}
|
||||
.sp-palette .sp-thumb-el {
|
||||
width:16px;
|
||||
height: 16px;
|
||||
margin:2px 1px;
|
||||
border: solid 1px #d0d0d0;
|
||||
}
|
||||
|
||||
.sp-container {
|
||||
padding-bottom:0;
|
||||
}
|
||||
|
||||
|
||||
/* Buttons: http://hellohappy.org/css3-buttons/ */
|
||||
.sp-container button {
|
||||
background-color: #eeeeee;
|
||||
background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
|
||||
background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
|
||||
background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
|
||||
background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
|
||||
background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: 1px solid #bbb;
|
||||
border-radius: 3px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
padding: 5px 4px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #eee;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sp-container button:hover {
|
||||
background-color: #dddddd;
|
||||
background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
|
||||
background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
|
||||
background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
|
||||
background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
|
||||
background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
|
||||
border: 1px solid #bbb;
|
||||
border-bottom: 1px solid #999;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 1px 0 #ddd;
|
||||
}
|
||||
.sp-container button:active {
|
||||
border: 1px solid #aaa;
|
||||
border-bottom: 1px solid #888;
|
||||
-webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
|
||||
-moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
|
||||
-ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
|
||||
-o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
|
||||
box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
|
||||
}
|
||||
.sp-cancel {
|
||||
font-size: 11px;
|
||||
color: #d93f3f !important;
|
||||
margin:0;
|
||||
padding:2px;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
text-decoration:none;
|
||||
|
||||
}
|
||||
.sp-cancel:hover {
|
||||
color: #d93f3f !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.sp-palette span:hover, .sp-palette span.sp-thumb-active {
|
||||
border-color: #000;
|
||||
}
|
||||
|
||||
.sp-preview, .sp-alpha, .sp-thumb-el {
|
||||
position:relative;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
|
||||
}
|
||||
.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner {
|
||||
display:block;
|
||||
position:absolute;
|
||||
top:0;left:0;bottom:0;right:0;
|
||||
}
|
||||
|
||||
.sp-palette .sp-thumb-inner {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
.sp-clear-display {
|
||||
background-repeat:no-repeat;
|
||||
background-position: center;
|
||||
background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||
<circle fill="none" stroke="#fff" stroke-width="6" stroke-miterlimit="15" stroke-dasharray="14.2472,14.2472" cx="50" cy="50" r="47" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="5s"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
<circle fill="none" stroke="#fff" stroke-width="1" stroke-miterlimit="10" stroke-dasharray="10,10" cx="50" cy="50" r="39">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="5s"
|
||||
from="0 50 50"
|
||||
to="-360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
<g fill="#fff">
|
||||
<rect x="30" y="35" width="5" height="30">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1"/>
|
||||
</rect>
|
||||
<rect x="40" y="35" width="5" height="30" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.2"/>
|
||||
</rect>
|
||||
<rect x="50" y="35" width="5" height="30" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.3"/>
|
||||
</rect>
|
||||
<rect x="60" y="35" width="5" height="30" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.4"/>
|
||||
</rect>
|
||||
<rect x="70" y="35" width="5" height="30" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.5"/>
|
||||
</rect>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,14 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
|
||||
<path fill="#fff" d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="1s"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 557 B |
@@ -1,23 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||
<circle fill="none" stroke="#fff" stroke-width="4" stroke-miterlimit="10" cx="50" cy="50" r="48"/>
|
||||
<line fill="none" stroke-linecap="round" stroke="#fff" stroke-width="4" stroke-miterlimit="10" x1="50" y1="50" x2="85" y2="50.5">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="2s"
|
||||
type="rotate"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</line>
|
||||
<line fill="none" stroke-linecap="round" stroke="#fff" stroke-width="4" stroke-miterlimit="10" x1="50" y1="50" x2="49.5" y2="74">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="15s"
|
||||
type="rotate"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</line>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 929 B |
@@ -1,15 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
|
||||
<circle fill="none" stroke="#fff" stroke-width="4" cx="50" cy="50" r="44" style="opacity:0.5;"/>
|
||||
<circle fill="#fff" stroke="#e74c3c" stroke-width="3" cx="8" cy="54" r="6" >
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="2s"
|
||||
type="rotate"
|
||||
from="0 50 48"
|
||||
to="360 50 52"
|
||||
repeatCount="indefinite" />
|
||||
|
||||
</circle>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 564 B |
@@ -1,28 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
|
||||
<circle fill="#fff" stroke="none" cx="6" cy="50" r="6">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
dur="1s"
|
||||
values="0;1;0"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1"/>
|
||||
</circle>
|
||||
<circle fill="#fff" stroke="none" cx="26" cy="50" r="6">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
dur="1s"
|
||||
values="0;1;0"
|
||||
repeatCount="indefinite"
|
||||
begin="0.2"/>
|
||||
</circle>
|
||||
<circle fill="#fff" stroke="none" cx="46" cy="50" r="6">
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
dur="1s"
|
||||
values="0;1;0"
|
||||
repeatCount="indefinite"
|
||||
begin="0.3"/>
|
||||
</circle>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 826 B |
@@ -1,31 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
|
||||
<circle fill="#fff" stroke="none" cx="6" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 15 ; 0 -15; 0 15"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1"/>
|
||||
</circle>
|
||||
<circle fill="#fff" stroke="none" cx="30" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 10 ; 0 -10; 0 10"
|
||||
repeatCount="indefinite"
|
||||
begin="0.2"/>
|
||||
</circle>
|
||||
<circle fill="#fff" stroke="none" cx="54" cy="50" r="6">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1s"
|
||||
type="translate"
|
||||
values="0 5 ; 0 -5; 0 5"
|
||||
repeatCount="indefinite"
|
||||
begin="0.3"/>
|
||||
</circle>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 983 B |
@@ -1,26 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L6" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||
<rect fill="none" stroke="#fff" stroke-width="4" x="25" y="25" width="50" height="50">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="0.5s"
|
||||
from="0 50 50"
|
||||
to="180 50 50"
|
||||
type="rotate"
|
||||
id="strokeBox"
|
||||
attributeType="XML"
|
||||
begin="rectBox.end"/>
|
||||
</rect>
|
||||
<rect x="27" y="27" fill="#fff" width="46" height="50">
|
||||
<animate
|
||||
attributeName="height"
|
||||
dur="1.3s"
|
||||
attributeType="XML"
|
||||
from="50"
|
||||
to="0"
|
||||
id="rectBox"
|
||||
fill="freeze"
|
||||
begin="0s;strokeBox.end"/>
|
||||
</rect>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 756 B |
@@ -1,37 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L7" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||
<path fill="#fff" d="M31.6,3.5C5.9,13.6-6.6,42.7,3.5,68.4c10.1,25.7,39.2,38.3,64.9,28.1l-3.1-7.9c-21.3,8.4-45.4-2-53.8-23.3
|
||||
c-8.4-21.3,2-45.4,23.3-53.8L31.6,3.5z">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="2s"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</path>
|
||||
<path fill="#fff" d="M42.3,39.6c5.7-4.3,13.9-3.1,18.1,2.7c4.3,5.7,3.1,13.9-2.7,18.1l4.1,5.5c8.8-6.5,10.6-19,4.1-27.7
|
||||
c-6.5-8.8-19-10.6-27.7-4.1L42.3,39.6z">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="1s"
|
||||
from="0 50 50"
|
||||
to="-360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</path>
|
||||
<path fill="#fff" d="M82,35.7C74.1,18,53.4,10.1,35.7,18S10.1,46.6,18,64.3l7.6-3.4c-6-13.5,0-29.3,13.5-35.3s29.3,0,35.3,13.5
|
||||
L82,35.7z">
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
attributeType="XML"
|
||||
type="rotate"
|
||||
dur="2s"
|
||||
from="0 50 50"
|
||||
to="360 50 50"
|
||||
repeatCount="indefinite" />
|
||||
</path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,49 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve">
|
||||
<rect fill="#fff" width="3" height="100" transform="translate(0) rotate(180 3 50)">
|
||||
<animate
|
||||
attributeName="height"
|
||||
attributeType="XML"
|
||||
dur="1s"
|
||||
values="30; 100; 30"
|
||||
repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<rect x="17" fill="#fff" width="3" height="100" transform="translate(0) rotate(180 20 50)">
|
||||
<animate
|
||||
attributeName="height"
|
||||
attributeType="XML"
|
||||
dur="1s"
|
||||
values="30; 100; 30"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1s"/>
|
||||
</rect>
|
||||
<rect x="40" fill="#fff" width="3" height="100" transform="translate(0) rotate(180 40 50)">
|
||||
<animate
|
||||
attributeName="height"
|
||||
attributeType="XML"
|
||||
dur="1s"
|
||||
values="30; 100; 30"
|
||||
repeatCount="indefinite"
|
||||
begin="0.3s"/>
|
||||
</rect>
|
||||
<rect x="60" fill="#fff" width="3" height="100" transform="translate(0) rotate(180 58 50)">
|
||||
<animate
|
||||
attributeName="height"
|
||||
attributeType="XML"
|
||||
dur="1s"
|
||||
values="30; 100; 30"
|
||||
repeatCount="indefinite"
|
||||
begin="0.5s"/>
|
||||
</rect>
|
||||
<rect x="80" fill="#fff" width="3" height="100" transform="translate(0) rotate(180 76 50)">
|
||||
<animate
|
||||
attributeName="height"
|
||||
attributeType="XML"
|
||||
dur="1s"
|
||||
values="30; 100; 30"
|
||||
repeatCount="indefinite"
|
||||
begin="0.1s"/>
|
||||
</rect>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,22 +0,0 @@
|
||||
|
||||
<svg version="1.1" id="L9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
|
||||
<rect x="20" y="50" width="4" height="10" fill="#fff">
|
||||
<animateTransform attributeType="xml"
|
||||
attributeName="transform" type="translate"
|
||||
values="0 0; 0 20; 0 0"
|
||||
begin="0" dur="0.6s" repeatCount="indefinite" />
|
||||
</rect>
|
||||
<rect x="30" y="50" width="4" height="10" fill="#fff">
|
||||
<animateTransform attributeType="xml"
|
||||
attributeName="transform" type="translate"
|
||||
values="0 0; 0 20; 0 0"
|
||||
begin="0.2s" dur="0.6s" repeatCount="indefinite" />
|
||||
</rect>
|
||||
<rect x="40" y="50" width="4" height="10" fill="#fff">
|
||||
<animateTransform attributeType="xml"
|
||||
attributeName="transform" type="translate"
|
||||
values="0 0; 0 20; 0 0"
|
||||
begin="0.4s" dur="0.6s" repeatCount="indefinite" />
|
||||
</rect>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 978 B |
@@ -1,20 +0,0 @@
|
||||
/** @odoo-module alias=anita_theme_setting.icons **/
|
||||
|
||||
const { markup, Component, xml } = owl;
|
||||
|
||||
const clone_style = '<svg class="tool-bar-icon clone_style" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M551.9 831.5H280.1c-48.4 0-87.9-39.4-87.9-87.9V471.9c0-48.4 39.4-87.9 87.9-87.9h271.8c48.4 0 87.9 39.4 87.9 87.9v271.8c0 48.4-39.5 87.8-87.9 87.8zM280.1 448c-13.2 0-23.9 10.7-23.9 23.9v271.8c0 13.2 10.7 23.9 23.9 23.9h271.8c13.2 0 23.9-10.7 23.9-23.9V471.9c0-13.2-10.7-23.9-23.9-23.9H280.1z" fill="#9ea4aa"></path><path d="M745 192.5H473.2c-48.5 0-87.8 39.3-87.8 87.8v36.1c0 17.7 14.3 32 32 32s32-14.3 32-32v-36.1c0-13.2 10.7-23.9 23.9-23.9H745c13.2 0 23.9 10.7 23.9 23.9v271.8c0 13.2-10.7 23.8-23.8 23.8h-37.6c-17.7 0-32 14.3-32 32s14.3 32 32 32H745c48.4 0 87.9-39.4 87.9-87.8V280.3c-0.1-48.5-39.4-87.8-87.9-87.8z" fill="#9ea4aa" /></svg>'
|
||||
|
||||
const edit_style = '<svg class="tool-bar-icon edit_style" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="18" height="18"><path d="M883.323539 875.027084c23.223934 0 41.978067 18.803251 41.978067 41.978067 0 23.175839-18.754133 41.97909-41.978067 41.97909L140.676461 958.984241c-23.176862 0-41.97909-18.803251-41.97909-41.97909 0-23.174815 18.803251-41.978067 41.97909-41.978067L883.323539 875.027084z" fill="#9ea4aa"></path><path d="M415.094438 743.309156c-41.105187 0-79.147618-12.997012-110.48637-34.837436L172.839998 840.189647l-48.440246 0 0-48.440246 131.767046-131.767046 0 0L705.7881 210.362079 229.152539 602.602507c-4.859681-16.861016-7.871271-34.618449-7.871271-53.105498 0-106.987686 522.841929-484.482273 629.828592-484.482273 48.490388 0 48.490388 0 48.490388 48.464805C899.599224 220.468251 522.10566 743.309156 415.094438 743.309156z" fill="#9ea4aa"></path></svg>'
|
||||
|
||||
const edit_image = '<svg class="tool-bar-icon set_bk_image" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M509.887896 135.642307c120.842229 0 241.684458-0.038886 362.526687 0.016373 40.700981 0.021489 74.195839 26.924209 82.769099 66.542532 1.329275 6.166443 1.819438 12.627598 1.823532 18.945491 0.093121 185.126324 0.127913 370.247532 0.047072 555.372833-0.020466 48.373731-36.548404 85.290525-84.897575 85.315084-241.300718 0.13917-482.59632 0.135076-723.897038 0-48.357358-0.029676-84.931345-36.953633-84.951811-85.297688-0.076748-185.122231-0.068562-370.246509-0.008186-555.36874 0.017396-49.007157 36.746926-85.518722 85.801155-85.531002 120.264061-0.013303 240.528122 0 360.79116 0l0 0L509.887896 135.642307zM510.517229 191.376679L303.759235 191.376679c-51.355645 0-102.703103 0.037862-154.058748-0.037862-9.831926-0.016373-18.139126 2.981914-24.490787 10.744715-5.233188 6.389524-6.052856 13.873985-6.052856 21.779026 0.059352 183.208649 0.046049 366.413204 0.051165 549.621852 0 2.124384 0.013303 4.247744 0.130983 6.363941 0.485047 8.890484 4.620227 15.791661 11.69025 20.990057 6.241145 4.587482 13.467733 5.207605 20.934798 5.207605 219.883943-0.037862 439.771979-0.029676 659.655921-0.029676 19.882839 0 39.766702-0.030699 59.653635 0.007163 7.379061 0.017396 14.141068-1.731434 19.883863-6.575766 7.876388-6.631024 10.220782-15.364943 10.220782-25.326828-0.071631-183.209672-0.051165-366.413204-0.055259-549.620829 0-22.370497-10.67206-33.123398-32.887015-33.128515L510.521323 191.371562l0 0L510.517229 191.376679zM510.517229 191.376679M753.7346 498.974335c-18.72241 0-37.453007-0.397043-56.167231 0.089028-31.966039 0.837064-59.256592 13.481036-83.756589 33.55421-18.329461 15.019065-36.722366 30.134321-56.298214 43.423999-25.098631 17.041118-52.92642 28.478612-83.245959 32.828686-6.664793 0.957815-13.446243 1.490957-20.178575 1.537006-25.479301 0.177032-50.979068 0.603751-76.44609-0.089028-22.180162-0.599657-42.258453 5.438872-61.283762 16.074094-15.09479 8.429996-28.566616 18.87386-40.413432 31.586392-17.506722 18.780739-35.319413 37.274952-53.28253 55.620785-10.971889 11.20418-28.037567 11.360745-39.111786 0.937348-10.879792-10.242272-12.222369-27.523867-1.744737-38.60525 22.932292-24.26259 45.561685-48.931432 69.972655-71.64883 29.018917-27.004027 63.633273-43.943838 103.526865-48.653093 6.305613-0.743944 12.703323-0.916882 19.064194-0.945535 24.709775-0.101307 49.433876-0.469698 74.131371 0.100284 18.021445 0.418532 34.770921-3.656273 50.958602-10.997472 23.51967-10.663874 45.274136-24.00574 64.312748-41.645492 25.196869-23.350824 52.990888-42.774199 86.667895-51.504024 15.566534-4.03285 31.806403-6.997368 47.824215-7.445576 38.774095-1.081635 77.595262-0.490164 116.40415-0.38374 12.307304 0.033769 21.606087 5.621021 26.539446 17.07898 4.6366 10.751878 2.676969 20.85498-4.975315 29.626761-5.819542 6.67298-13.497409 9.207709-22.261003 9.16473-20.077268-0.101307-40.154535-0.030699-60.231803-0.030699l0 0.334621 0 0L753.7346 498.974335zM753.7346 498.974335M342.600869 303.278281c30.919197 0.030699 56.005548 25.125237 55.917544 55.939033-0.089028 30.800493-25.285896 55.755862-56.216349 55.667857-30.547737-0.089028-55.490825-25.061792-55.638181-55.689347C286.515502 328.441381 311.719534 303.248605 342.600869 303.278281L342.600869 303.278281 342.600869 303.278281zM342.600869 303.278281" fill="#9ea4aa"></path></svg>'
|
||||
|
||||
const delete_style = '<svg class="tool-bar-icon delete_style" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M784 288.96l-56.56-56.576L512.464 447.36 297.504 232.384l-56.56 56.576 214.96 214.96L240.96 718.896l56.56 56.56L512.464 560.48l214.976 214.976L784 718.88 569.04 503.92z" fill="#9ea4aa"></path></svg>'
|
||||
|
||||
export default {
|
||||
icons: {
|
||||
clone_style: markup(clone_style),
|
||||
edit_style: markup(edit_style),
|
||||
edit_image: markup(edit_image),
|
||||
delete_style: markup(delete_style)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { startWebClient } from "@web/start";
|
||||
import { AnitaWebClient } from "./components/webclient/webclient";
|
||||
|
||||
startWebClient(AnitaWebClient);
|
||||
@@ -1,7 +0,0 @@
|
||||
/** @odoo-module alias=anita_theme_setting.theme_setting**/
|
||||
|
||||
let settings = {
|
||||
...odoo.__theme_setting__
|
||||
};
|
||||
|
||||
export default settings
|
||||
@@ -1,29 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, useState } from "@odoo/owl";
|
||||
|
||||
export class AnitaAccordion extends Component {
|
||||
|
||||
setup() {
|
||||
this.state = useState({
|
||||
'collapse': true
|
||||
})
|
||||
}
|
||||
|
||||
toggleDisplay() {
|
||||
this.state.collapse = !this.state.collapse;
|
||||
}
|
||||
};
|
||||
|
||||
AnitaAccordion.props = {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
slots: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
};
|
||||
|
||||
AnitaAccordion.template = "anita_theme_setting.accordion";
|
||||
AnitaAccordion.components = {};
|
||||
@@ -1,59 +0,0 @@
|
||||
.anita_accordion {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.card-title {
|
||||
border: 1px solid #dee2e6 !important;
|
||||
}
|
||||
|
||||
.accordion_button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: .75rem 1.25rem;
|
||||
font-size: 1rem;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
overflow-anchor: none;
|
||||
transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease;
|
||||
|
||||
&::after {
|
||||
flex-shrink: 0;
|
||||
width: 0.75rem;
|
||||
height: 0.75em;
|
||||
margin-left: auto;
|
||||
content: "";
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 0.75rem;
|
||||
transition: transform .2s ease-in-out;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 1px dotted;
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
.accordion-body {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.collapsed) {
|
||||
.accordion_button {
|
||||
color: #0c63e4;
|
||||
background-color: #e7f1ff;
|
||||
&::after {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="anita_theme_setting.accordion" owl="1">
|
||||
<div class="anita_accordion" t-att-class="{'collapsed': state.collapse}">
|
||||
<div class="card-title">
|
||||
<button class="accordion_button" type="button" t-on-click.prevent="toggleDisplay">
|
||||
<t t-esc="props.title"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="accordion-body" >
|
||||
<t t-slot="default"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/** @odoo-module alias=anita_theme_setting.color_picker **/
|
||||
|
||||
import { _lt } from "@web/core/l10n/translation";
|
||||
|
||||
import { Component, useEffect, useState, useRef, onMounted } from "@odoo/owl";
|
||||
|
||||
export default class anitaColorPicker extends Component {
|
||||
|
||||
setup() {
|
||||
this.colorPickerInputRef = useRef("color_picker_input");
|
||||
this.colorValueInputRef = useRef("color_value_input");
|
||||
|
||||
this.state = useState({
|
||||
color_value: this.props.color,
|
||||
});
|
||||
|
||||
onMounted(this.onMounted);
|
||||
|
||||
// when color changed nee reset the color picker val
|
||||
useEffect(
|
||||
() => {
|
||||
window.$(this.colorPickerInputRef.el).spectrum("set", this.state.color_value);
|
||||
},
|
||||
() => [this.state.color_value]
|
||||
);
|
||||
}
|
||||
|
||||
onMounted() {
|
||||
$(this.colorPickerInputRef.el).spectrum({
|
||||
showAlpha: true,
|
||||
showInput: true,
|
||||
preferredFormat: "rgb",
|
||||
color: this.props.color_value,
|
||||
palette: [
|
||||
["#000","#444","#666","#999","#ccc","#eee","#f3f3f3","#fff"],
|
||||
["#f00","#f90","#ff0","#0f0","#0ff","#00f","#90f","#f0f"],
|
||||
["#f4cccc","#fce5cd","#fff2cc","#d9ead3","#d0e0e3","#cfe2f3","#d9d2e9","#ead1dc"],
|
||||
["#ea9999","#f9cb9c","#ffe599","#b6d7a8","#a2c4c9","#9fc5e8","#b4a7d6","#d5a6bd"],
|
||||
["#e06666","#f6b26b","#ffd966","#93c47d","#76a5af","#6fa8dc","#8e7cc3","#c27ba0"],
|
||||
["#c00","#e69138","#f1c232","#6aa84f","#45818e","#3d85c6","#674ea7","#a64d79"],
|
||||
["#900","#b45f06","#bf9000","#38761d","#134f5c","#0b5394","#351c75","#741b47"],
|
||||
["#600","#783f04","#7f6000","#274e13","#0c343d","#073763","#20124d","#4c1130"]
|
||||
],
|
||||
change: (color) => {
|
||||
let current_color = undefined;
|
||||
if (color._a !== 1) {
|
||||
current_color = color.toRgbString();
|
||||
} else {
|
||||
current_color = color.toHexString(); ;
|
||||
}
|
||||
if (this.state.color_value !== current_color) {
|
||||
this.state.color_value = current_color;
|
||||
// notify change the color
|
||||
this.props.onChange(current_color);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// props
|
||||
anitaColorPicker.props = {
|
||||
color: {
|
||||
type: String,
|
||||
optional: 1,
|
||||
},
|
||||
onChange: {
|
||||
type: Function,
|
||||
optional: true,
|
||||
},
|
||||
};
|
||||
|
||||
// default props
|
||||
anitaColorPicker.defaultProps = {
|
||||
color: '#ffffff',
|
||||
};
|
||||
|
||||
anitaColorPicker.template = "anita_theme_setting.color_picker";
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
.style_color_item {
|
||||
|
||||
.sp-preview {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.sp-dd {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sp-preview {
|
||||
width: 18px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.sp-replacer {
|
||||
border-color: #dee2e6;
|
||||
&:hover {
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sp-container {
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<!-- template for color picker -->
|
||||
<t t-name="anita_theme_setting.color_picker" owl="1">
|
||||
<div class="style-item-ver input-group style_color_item mr-3">
|
||||
<input type="text" class="form-control style_item_var" t-model="state.color_value" t-ref="color_value_input" />
|
||||
<span class="input-group-append">
|
||||
<span class="input-group-text colorpicker-input-addon">
|
||||
<input t-ref="color_picker_input" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,447 +0,0 @@
|
||||
.customizer {
|
||||
|
||||
width: 415px;
|
||||
right: -500px;
|
||||
|
||||
padding: 0;
|
||||
background-color: #FFF;
|
||||
z-index: 1001;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 100vh;
|
||||
-webkit-transition: right .4s cubic-bezier(.05, .74, .2, .99);
|
||||
transition: right .4s cubic-bezier(.05, .74, .2, .99);
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
border-left: 1px solid rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 15px 30px 0 rgba(0, 0, 0, .11), 0 5px 15px 0 rgba(0, 0, 0, .08);
|
||||
color: #212529;
|
||||
|
||||
a,
|
||||
label,
|
||||
span,
|
||||
i {
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
i {
|
||||
&:hover {
|
||||
color: #71639e !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
right: -550px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.show {
|
||||
right: 0
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
height: 100% !important;
|
||||
|
||||
.dropdown-toggle {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
&:hover {
|
||||
path {
|
||||
fill: #71639e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_input {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
|
||||
.config_container {
|
||||
overflow: hidden;
|
||||
|
||||
.tabs {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
width: 30px;
|
||||
background-color: #e6e6e6;
|
||||
border-left: 1px solid #e6e6e6;
|
||||
padding-top: 15px;
|
||||
flex-shrink: 0 !important;
|
||||
|
||||
.tab_item {
|
||||
padding: 5px 0px;
|
||||
height: 85px;
|
||||
-webkit-writing-mode: vertical-lr;
|
||||
writing-mode: vertical-lr;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none !important;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-left: 1px solid rgb(73, 80, 87);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab_panes {
|
||||
overflow: hidden;
|
||||
.scroller {
|
||||
height: 100%;
|
||||
margin-right: -16px;
|
||||
padding: 5px 15px 5px 5px;
|
||||
overflow-y: auto;
|
||||
|
||||
.settings_pane, .styles_pane {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.styles_pane {
|
||||
position: relative;
|
||||
|
||||
ul.navbar-nav {
|
||||
li.nav-item {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a.nav-link {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme_mode_tool_bar {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-top: 1px solid #dee2e6;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
background-color: #eee;
|
||||
text-align: center;
|
||||
margin-top: -1px;
|
||||
|
||||
.create_new_theme_mode {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.add_new_style_icon {
|
||||
fill: #dee2e6;
|
||||
}
|
||||
|
||||
.operation-toolbar {
|
||||
height: 30px;
|
||||
background-color: #f2f2f2;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
|
||||
.tool-bar-icon {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.mode_tools {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.style-preview-box {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
background-size: cover;
|
||||
|
||||
&:hover {
|
||||
transform: translate3d(0px, 0px, -2px);
|
||||
}
|
||||
|
||||
.preview-sidebar-box {
|
||||
width: 30px;
|
||||
border-right: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.preview-main-box {
|
||||
.anita_header-box {
|
||||
height: 30px
|
||||
}
|
||||
|
||||
.footer-box {
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-header {
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
}
|
||||
|
||||
.preview-footer {
|
||||
border-top: 1px solid #ebebeb;
|
||||
}
|
||||
|
||||
.nav-pills {
|
||||
|
||||
.nav-link.active,
|
||||
.nav-pills .show {
|
||||
>.nav-link {
|
||||
background-color: transparent !important;
|
||||
border: 1px solid #e3e3e3 !important
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-style-contianer {
|
||||
|
||||
.theme_style_tab {
|
||||
padding: 5px;
|
||||
|
||||
box-sizing: border-box;
|
||||
background: transparent;
|
||||
border: 1px solid;
|
||||
border-color: transparent;
|
||||
margin: 1px;
|
||||
|
||||
&:hover {
|
||||
border-color: #5A8DEE !important;
|
||||
|
||||
.operation-toolbar {
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: transparent;
|
||||
border: 1px solid darken($color: #5A8DEE, $amount: 10) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.theme_style_tool_bar {
|
||||
.theme_style_tab {
|
||||
.tool_bar_item {
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
color: black;
|
||||
border-color: #5A8DEE !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.style_tool_bar {
|
||||
background-color: #eee;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.style_item_container {
|
||||
.style_item_group {
|
||||
.style_items {
|
||||
.style_item {
|
||||
margin-left: 10px;
|
||||
width: 220px;
|
||||
|
||||
.title {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.color-container {
|
||||
|
||||
.style_color_item {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.pickr {
|
||||
margin: 5px;
|
||||
|
||||
.pcr-button:before {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.color_value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: left;
|
||||
}
|
||||
}
|
||||
|
||||
.colorpicker-input-addon {
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.style_item_group_name {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-top: 1px solid #dee2e6;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
background-color: #eee;
|
||||
text-align: center;
|
||||
margin-top: -1px;
|
||||
|
||||
.style_group_dropdown {
|
||||
&.dropdown-toggle {
|
||||
font-size: 0.75rem;
|
||||
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.style_sub_group_name {
|
||||
color: #182139;
|
||||
background: rgba(238, 238, 238, 0.51);
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding-left: 10px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
|
||||
.sub_group_toggler i {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-group-body {
|
||||
.style_group_dropdown {
|
||||
&.dropdown-toggle {
|
||||
font-size: 0.75rem;
|
||||
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sub_group_panel {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
}
|
||||
|
||||
.add_style_item {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.add_item_group_container {
|
||||
margin-bottom: -1.5rem;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.add_new_group {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-top: none;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
background-color: #eee;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.customizer-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
padding: 7px;
|
||||
width: auto;
|
||||
z-index: 10;
|
||||
color: #727E8C;
|
||||
|
||||
i {
|
||||
font-size: 1.3rem
|
||||
}
|
||||
|
||||
.customizer-toggle {
|
||||
background: #5A8DEE;
|
||||
color: #FFF;
|
||||
display: block;
|
||||
box-shadow: -3px 0 8px rgba(0, 0, 0, .1);
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
left: -39px;
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.color-box {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: .5rem;
|
||||
border-radius: .3rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.selected {
|
||||
box-shadow: 0 0 0 3px rgba(52, 144, 220, .5)
|
||||
}
|
||||
}
|
||||
|
||||
ul.list-inline li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-bottom: .5rem;
|
||||
font-family: Rubik, Helvetica, Arial, serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
color: #475F7B;
|
||||
}
|
||||
|
||||
.custom-control-label {
|
||||
padding-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.top_title {
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
box-shadow: -1px 2px 25px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.customizer-footer {
|
||||
align-self: flex-end;
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid rgb(235, 235, 235);
|
||||
background-color: rgba(255, 255, 255, 0.733);
|
||||
box-shadow: 0 -4px 25px 0 rgba(0, 0, 0, .1);
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<!-- style setting -->
|
||||
<div t-name="anita_theme_setting.customizer" class="dropdown" owl="1">
|
||||
<a role="button" href="#" class="customizer-toggle dropdown-toggle o-no-caret o-dropdown--narrow" t-on-click="_on_toggle_click">
|
||||
<span class="fa fa-cog fa-fw fa-lg"/>
|
||||
</a>
|
||||
<t t-if="state.inited">
|
||||
<t t-call="anita_theme_setting.customizer.panel"/>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<!-- customizer -->
|
||||
<div t-name="anita_theme_setting.customizer.panel" class="customizer d-flex flex-column" t-att-class="{'show': state.show_panel}" owl='1' ref="panel">
|
||||
<a class="customizer-close" href="#" t-on-click="_on_close_click">
|
||||
<i class="fa fa-close"/>
|
||||
</a>
|
||||
<div class="d-flex flex-column flex-fill" style="overflow: hidden;">
|
||||
|
||||
<div class="top_title">
|
||||
<h4 class="text-uppercase mb-0">Theme Customizer</h4>
|
||||
</div>
|
||||
|
||||
<div class="config_container flex-fill d-flex flex-row">
|
||||
|
||||
<!-- tab panes -->
|
||||
<div class="tab_panes flex-fill">
|
||||
<div class="scroller">
|
||||
|
||||
<!-- themem settings -->
|
||||
<div class="settings_pane flex-fill d-flex flex-column" style="overflow-y: hidden" t-att-class="{'d-none': state.acitve_item == 'style'}">
|
||||
<ThemeSettings bus="setting_component_bus" />
|
||||
</div>
|
||||
|
||||
<!-- theme style -->
|
||||
<div class="styles_pane flex-fill d-flex flex-column" style="overflow-y: hidden" t-att-class="{'d-none': state.acitve_item == 'setting'}">
|
||||
|
||||
<div class="theme_modes d-flex flex-column flex-fill" style="overflow-y: auto">
|
||||
<t t-set="theme_modes" t-value="user_setting.theme_modes"/>
|
||||
<div class="theme_modes d-flex flex-column">
|
||||
|
||||
<!-- tab item -->
|
||||
<!-- <div class="custom-controls-stacked theme_mode_name_container p-4">
|
||||
<t t-call="anita_theme_setting.theme_mode_tab" />
|
||||
</div> -->
|
||||
|
||||
<!-- create new theme mode -->
|
||||
<!-- <div class="theme_mode_tool_bar">
|
||||
<a class="create_new_theme_mode" href="#" t-on-click.prevent="_create_new_mode">
|
||||
<i class="fa fa-plus-square ml-1 mr-1"/>
|
||||
Create New Mode
|
||||
</a>
|
||||
</div> -->
|
||||
|
||||
<!-- theme mode content -->
|
||||
<div class="theme-modes-container d-flex flex-column flex-fill">
|
||||
<t t-foreach="theme_modes || []" t-as="theme_mode" t-key="theme_mode.id">
|
||||
<t t-call="anita_theme_setting.theme_mode" />
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tab items -->
|
||||
<!-- <div class="tabs d-flex flex-column">
|
||||
<div class="tab_item" data-tab-id="columns" t-att-class="{'active': state.acitve_item == 'style'}" t-on-click.prevent="() => { this.state.acitve_item = 'style' }">
|
||||
<i class="fa fa-list-ul"></i>
|
||||
<span>Sytle</span>
|
||||
</div>
|
||||
<div class="tab_item" data-tab-id="settings" t-att-class="{'active': state.acitve_item == 'setting'}" t-on-click.prevent="() => { this.state.acitve_item = 'setting' }">
|
||||
<i class="fa fa-filter"></i>
|
||||
<span>Settings</span>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
<!-- button for theme style -->
|
||||
<div class="customizer-footer d-flex align-items-center justify-content-left" >
|
||||
<t t-if="state.acitve_item == 'style'">
|
||||
<button class="preview-btn btn btn-primary ml-2" t-on-click="_on_preview_click">Preview</button>
|
||||
<button class="reset-preview-btn btn btn-primary ml-2" t-on-click='_on_reset_click' >Reset</button>
|
||||
<button class="save-btn btn btn-primary ml-2" t-on-click='_on_save_click'>Save</button>
|
||||
</t>
|
||||
<t t-if="state.acitve_item == 'setting'">
|
||||
<button class="save-btn btn btn-primary ml-2" t-on-click='_on_save_setting_click'>Save</button>
|
||||
<button class="reset-btn btn btn-primary ml-2" t-on-click='_on_cancel_setting_click'>Cancel</button>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Theme mode tab -->
|
||||
<t t-name="anita_theme_setting.theme_mode_tab" owl="1">
|
||||
<div class="form-check my-3" t-foreach="theme_modes || []" t-as="theme_mode" t-key="theme_mode.id">
|
||||
<input t-if="theme_mode.id == state.cur_mode_id" type="radio" name="theme_mode_radio" t-on-change.prevent="() => this._on_change_mode(theme_mode)" class="form-check-input" t-attf-id="theme-mode-{{theme_mode.id}}" checked="true" />
|
||||
<input t-else="" type="radio" name="theme_mode_radio" t-on-change.prevent="() => this._on_change_mode(theme_mode)" class="form-check-input" t-attf-id="theme-mode-{{theme_mode.id}}" />
|
||||
<label class="form-check-label" t-attf-for="theme-mode-{{theme_mode.id}}">
|
||||
<t t-esc="theme_mode.name" />
|
||||
</label>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- theme mode tab pane -->
|
||||
<t t-name="anita_theme_setting.theme_mode" owl="1">
|
||||
<div class="theme-mode-container flex-column flex-fill" t-att-class="{'d-none': theme_mode.id != state.cur_mode_id}">
|
||||
<div class="theme_styles d-flex flex-column">
|
||||
<t t-set="theme_styles" t-value="theme_mode.theme_styles"/>
|
||||
<!-- <t t-call="anita_theme_setting.theme_style_tool_bar" /> -->
|
||||
<ul class="nav nav-pills theme-style-contianer p-3">
|
||||
<t t-foreach="theme_styles" t-as="theme_style" t-key="theme_style.id">
|
||||
<t t-call="anita_theme_setting.theme_style_tab" />
|
||||
</t>
|
||||
<!-- add new theme style -->
|
||||
<li class="nav-item theme-style theme_style_tool_bar">
|
||||
<div class="theme_style_tab">
|
||||
<div class="style-preview-box d-flex flex-column align-items-center justify-content-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<a class="add_new_style tool_bar_item" t-on-click="(event)=>this._on_add_new_style(theme_mode.id, event)" href="#">
|
||||
<i class="fa fa-plus-circle" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content flex-fill" style="margin:10px 0px; border-color: #3b425c">
|
||||
<t t-foreach="theme_styles" t-as="theme_style" t-key="theme_style.id">
|
||||
<t t-call="anita_theme_setting.theme_style_tab_pane" />
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.theme_style_tab_pane" owl="1">
|
||||
<div class="tab-pane theme-style-tab-pane fade p-15" t-att-class="{'active': theme_style.id == state.cur_style_id, 'show': theme_style.id == state.cur_style_id}">
|
||||
<div class="style_item_container d-flex justify-content-between flex-wrap flex-column">
|
||||
<!-- style item group -->
|
||||
<div class="style_item_group d-flex flex-column" t-foreach="theme_style.groups || []" t-as="group" t-key="group.id">
|
||||
<t t-call="anita_theme_setting.style_item_group" />
|
||||
</div>
|
||||
<!-- <div class="style_item_group_name d-flex">
|
||||
<button type="button" t-on-click="() => this._add_new_group(theme_style)" class="btn btn-default btn-sm add_new_group d-flex flex-fill align-items-center justify-content-center">
|
||||
<i class="fa fa-plus-square mr-1"/>
|
||||
Add Group
|
||||
</button>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.style_item_group" owl="1">
|
||||
<div class="style_item_group_name d-flex align-items-center">
|
||||
<span class="flex-fill">
|
||||
<t t-esc="group.name"/>
|
||||
</span>
|
||||
<!-- <Dropdown>
|
||||
<t t-set-slot="toggler">
|
||||
<i class="fa fa-caret-square-o-down mr-1" />
|
||||
</t>
|
||||
<DropdownItem class="'o_menu_item'" onSelected="() => this._add_sub_group(group)">
|
||||
Add Sub Group
|
||||
</DropdownItem>
|
||||
<DropdownItem class="'o_menu_item'" onSelected="() => this._delete_group(theme_style, group)">
|
||||
Delete Group
|
||||
</DropdownItem>
|
||||
</Dropdown> -->
|
||||
</div>
|
||||
<t t-foreach="group.sub_groups or []" t-as="sub_group" t-key="sub_group.id">
|
||||
<t t-call="anita_theme_setting.sub_group" />
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<!-- do not render the body initial -->
|
||||
<t t-name='anita_theme_setting.sub_group' owl='1'>
|
||||
<div class="sub-group-body d-flex flex-column flex-wrap style_items">
|
||||
<div class="style_sub_group_name d-flex align-items-center">
|
||||
<a type="button" class="btn btn-default btn-sm p-1 sub_group_toggle" t-on-click="() => this._toggle_sub_group(sub_group)">
|
||||
<i class="fa mr-1" t-att-class="{'fa-plus-square': !sub_group.expanded, 'fa-minus-square': sub_group.expanded}" />
|
||||
</a>
|
||||
<t t-esc="sub_group.name"/>
|
||||
<span class="flex-fill"/>
|
||||
<!-- <Dropdown>
|
||||
<t t-set-slot="toggler">
|
||||
<i class="fa fa-caret-square-o-down mr-1" />
|
||||
</t>
|
||||
<DropdownItem class="'o_menu_item'" onSelected="() => this._add_style_item(theme_style, group, sub_group)">
|
||||
Add
|
||||
</DropdownItem>
|
||||
<DropdownItem class="'o_menu_item'" onSelected="() => this._delete_sub_group(group, sub_group)">
|
||||
Delete
|
||||
</DropdownItem>
|
||||
</Dropdown> -->
|
||||
</div>
|
||||
<div t-if="sub_group.expanded" class="flex-wrap p-4 sub_group_panel">
|
||||
<t t-foreach="sub_group.style_items or []" t-as="style_item" t-key="style_item.id">
|
||||
<t t-call="anita_theme_setting.style_item" />
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.theme_style_tab" owl="1">
|
||||
<li class="nav-item theme-style-nav-item">
|
||||
<a class="nav-link theme_style_tab" data-toggle="tab" t-attf-href="#theme_style_tab_{{theme_style.id}}" role="tab" t-att-class="{'active': state.cur_style_id == theme_style.id}" t-on-click.prevent="() => this._on_click_style_nav_item(theme_style)">
|
||||
<div class="style-preview-box d-flex flex-row" t-attf-style="{{theme_style.background_image? 'background-image:url('+ theme_style.background_image +')': ''}}">
|
||||
<div class="sidebar-box" t-attf-style="background-color: {{theme_style.identities.side_bar_background}}; width:20px" />
|
||||
<div class="main-box d-flex flex-column flex-fill">
|
||||
<div class="preview-body-box flex-fill" t-attf-style="background-color: {{theme_style.identities.body_background}}"/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.style_item" owl='1'>
|
||||
<div class="style_item">
|
||||
<div class="title">
|
||||
<t t-esc="style_item.name" />
|
||||
<!-- <button t-if="!style_item.is_default" type="button" class="btn btn-default btn-sm p-1 delete_style_item" t-on-click='() => this._on_delete_style_item(style_ite.id)'>
|
||||
<i class="fa fa-trash-o"/>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm p-1 edit_style_item" t-on-click="() => this._on_edit_style_item()">
|
||||
<i class="fa fa-pencil-square-o"/>
|
||||
</button> -->
|
||||
</div>
|
||||
<div class="color-container d-flex flex-row">
|
||||
<t t-foreach="style_item.vars or []" t-as="style_item_var" t-key="style_item_var.id">
|
||||
<t t-call="anita_theme_setting.style_item_var" />
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.style_item_var" owl='1'>
|
||||
<AnitaColorPicker t-if="style_item_var.type == 'color'" color="style_item_var.color" onChange="(color) => this._on_change_color(theme_style, style_item, style_item_var, color)" />
|
||||
|
||||
<div t-if="style_item_var.type == 'image'" class="style-item-var input-group mr-3">
|
||||
<input type="text" t-model="style_item_var.image" class="form-control style_item_var" />
|
||||
<span class="input-group-append">
|
||||
<span class="input-group-text fa fa-picture-o edit_item_var" t-on-click="()=>this._on_edit_item_var(style_item, style_item_var)">
|
||||
<i/>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div t-elif="style_item_var.type == 'svg'" class="style-item-var input-group mr-3">
|
||||
<input type="text" t-model="style_item_var.svg" class="form-control style_item_var"/>
|
||||
<span class="input-group-append">
|
||||
<span class="input-group-text fa fa-gg edit_item_var" t-on-click="()=>this._on_edit_item_var(style_item, style_item_var)">
|
||||
<i/>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div t-elif="style_item_var.type == 'image_url'" class="style-item-var input-group mr-3">
|
||||
<input type="text" t-model="style_item_var.image_url" class="form-control style_item_var"/>
|
||||
<span class="input-group-append">
|
||||
<span class="input-group-text fa fa-link edit_item_var" t-on-click="()=>this._on_edit_item_var(style_item, style_item_var)">
|
||||
<i/>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="anita_theme_setting.theme_style_tool_bar" owl="1">
|
||||
<div class="operation-toolbar d-flex align-items-center justify-content-center">
|
||||
|
||||
<!-- colone_style -->
|
||||
<span class="clone_style" t-on-click="() => this._clone_style()">
|
||||
<t t-out="icons.clone_style"/>
|
||||
</span>
|
||||
|
||||
<!-- eidt -->
|
||||
<span class="edit_style" t-on-click="() => this._edit_style()">
|
||||
<t t-out="icons.edit_style"/>
|
||||
</span>
|
||||
|
||||
<!-- delete -->
|
||||
<span t-if="state.show_delete_style" class="delete_style" t-on-click="() => this._delete_style()">
|
||||
<t t-out="icons.delete_style"/>
|
||||
</span>
|
||||
|
||||
<!-- image -->
|
||||
<span class="set_bk_image" t-on-click="() => this._set_bk_image()">
|
||||
<t t-out="icons.edit_image"/>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,42 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { Dialog } from "@web/core/dialog/dialog"
|
||||
|
||||
import theme_setting from "anita_theme_setting.theme_setting";
|
||||
const settings = theme_setting.settings
|
||||
|
||||
// patch dialog
|
||||
// patch(Dialog.prototype, {
|
||||
// setup() {
|
||||
// super.setup(...arguments);
|
||||
|
||||
// this.dialog_animation_in_style = settings.dialog_animation_in_style;
|
||||
// if (this.dialog_animation_in_style) {
|
||||
// this.dialog_animation_in_style = 'animate__animated' + ' ' + this.dialog_animation_in_style;
|
||||
// this.props.contentClass = this.props.contentClass + ' ' + this.dialog_animation_in_style;
|
||||
// }
|
||||
|
||||
// // wrap close
|
||||
// let _close = this.data.close;
|
||||
// this.data.close = async () => {
|
||||
// if (this.modalRef && this.modalRef.el) {
|
||||
// let dialog_animation_out_style = settings.dialog_animation_out_style;
|
||||
// if (dialog_animation_out_style) {
|
||||
// // find modal-content
|
||||
// const modalContent = this.modalRef.el.querySelector(".modal-content");
|
||||
// if (modalContent) {
|
||||
// // add out animation
|
||||
// modalContent.classList.add('animate__animated', dialog_animation_out_style);
|
||||
// // wait for animation end
|
||||
// await new Promise((resolve) => {
|
||||
// modalContent.addEventListener("animationend", resolve);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // call original close
|
||||
// _close.call(this);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<!-- extend web.Dialog -->
|
||||
<t t-name="web.ActionDialog" t-inherit="web.ActionDialog" t-inherit-mode="extension" owl="1">
|
||||
<xpath expr="//div[hasclass('modal-content')]" position="attributes">
|
||||
<attribute name="t-attf-class">modal-content crax {{dialog_animation_in_style? dialog_animation_in_style: ''}}</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,31 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { BlockUI } from "@web/core/ui/block_ui";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
import { xml } from "@odoo/owl";
|
||||
|
||||
import theme_setting from "anita_theme_setting.theme_setting";
|
||||
|
||||
patch(BlockUI.prototype, {
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
this.user_setting = theme_setting.settings;
|
||||
this.loading_style = this.user_setting.loading_svg || '/web/static/img/spin.svg';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
BlockUI.template = xml`
|
||||
<div t-att-class="state.blockUI ? 'o_blockUI fixed-top d-flex justify-content-center align-items-center flex-column vh-100 bg-black-50' : ''">
|
||||
<t t-if="state.blockUI">
|
||||
<div class="o_spinner mb-4">
|
||||
<img t-att-src="this.loading_style" alt="Loading..."/>
|
||||
</div>
|
||||
<div class="o_message text-center px-4">
|
||||
<t t-esc="state.line1"/> <br/>
|
||||
<t t-esc="state.line2"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>`;
|
||||
@@ -1,49 +0,0 @@
|
||||
|
||||
.theme_settings {
|
||||
button.button_style1 {
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// rounded style
|
||||
body.button_style2:not(.setting_preview), .theme_settings .button_style2 {
|
||||
button {
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
// slant style
|
||||
body.button_style3:not(.setting_preview), .theme_settings .button_style3 {
|
||||
/* CSS */
|
||||
button {
|
||||
border-radius: 0;
|
||||
border-bottom-left-radius: 50px;
|
||||
border-top-right-radius: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
// slant style
|
||||
body.button_style4:not(.setting_preview), .theme_settings .button_style4 {
|
||||
button {
|
||||
border-radius: 0;
|
||||
border-bottom-right-radius: 50px;
|
||||
border-top-left-radius: 50px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body.button_style5:not(.setting_preview), .theme_settings .button_style5 {
|
||||
button {
|
||||
border-radius: 0;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
body.button_style6:not(.setting_preview), .theme_settings .button_style6 {
|
||||
button {
|
||||
border-radius: 0;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
// check box style2
|
||||
body.checkbox_style1:not(.setting_preview),
|
||||
.theme_settings .checkbox_style2 {
|
||||
|
||||
.form-check-input[type=checkbox] {
|
||||
&:checked {
|
||||
background-color: transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' %3E%3Cpath d='M822.464 265.344a28.256 28.256 0 0 0-43.072 1.312l-352.96 417.664-181.92-212.992a28.288 28.288 0 0 0-43.104-1.088 37.12 37.12 0 0 0-0.96 48.256l204.096 238.944c5.76 6.752 13.696 10.56 22.016 10.56h0.096a29.088 29.088 0 0 0 22.048-10.656L823.68 313.6c11.52-13.728 11.008-35.328-1.216-48.256' p-id='3837'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check box style3
|
||||
body.checkbox_style3:not(.setting_preview),
|
||||
.theme_settings .checkbox_style3 {
|
||||
.form-check-input[type=checkbox] {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
// check box style4
|
||||
body.checkbox_style4:not(.setting_preview),
|
||||
.theme_settings .checkbox_style4 {
|
||||
.form-check-input[type=checkbox] {
|
||||
border-radius: 50%;
|
||||
|
||||
&:checked {
|
||||
background-color: transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' %3E%3Cpath d='M822.464 265.344a28.256 28.256 0 0 0-43.072 1.312l-352.96 417.664-181.92-212.992a28.288 28.288 0 0 0-43.104-1.088 37.12 37.12 0 0 0-0.96 48.256l204.096 238.944c5.76 6.752 13.696 10.56 22.016 10.56h0.096a29.088 29.088 0 0 0 22.048-10.656L823.68 313.6c11.52-13.728 11.008-35.328-1.216-48.256' p-id='3837'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check box style
|
||||
body.checkbox_style5:not(.setting_preview),
|
||||
.theme_settings .checkbox_style5 {
|
||||
.form-check-input[type=checkbox] {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// check box style6
|
||||
// check box style4
|
||||
body.checkbox_style6:not(.setting_preview),
|
||||
.theme_settings .checkbox_style6 {
|
||||
.form-check-input[type=checkbox] {
|
||||
border-radius: 0;
|
||||
|
||||
&:checked {
|
||||
background-color: transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' %3E%3Cpath d='M822.464 265.344a28.256 28.256 0 0 0-43.072 1.312l-352.96 417.664-181.92-212.992a28.288 28.288 0 0 0-43.104-1.088 37.12 37.12 0 0 0-0.96 48.256l204.096 238.944c5.76 6.752 13.696 10.56 22.016 10.56h0.096a29.088 29.088 0 0 0 22.048-10.656L823.68 313.6c11.52-13.728 11.008-35.328-1.216-48.256' p-id='3837'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
|
||||
|
||||
// input style1 normal
|
||||
body.input_style1:not(.setting_preview), .theme_settings .input_style1 {
|
||||
input.o_input, .o_input {
|
||||
border-width: 1px;
|
||||
border-radius: 5px;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input style1 square
|
||||
body.input_style2:not(.setting_preview), .theme_settings .input_style2 {
|
||||
input.o_input, .o_input {
|
||||
border-width: 1px;
|
||||
border-radius: 0;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input style3 line
|
||||
body.input_style3:not(.setting_preview), .theme_settings .input_style3 {
|
||||
input.o_input, .o_input {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
background: transparent;
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input style4 slant
|
||||
body.input_style4:not(.setting_preview), .theme_settings .input_style4 {
|
||||
input.o_input, .o_input {
|
||||
border-bottom-left-radius: 50px;
|
||||
border-top-right-radius: 50px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15p;
|
||||
}
|
||||
}
|
||||
|
||||
// input style5 slant reverse
|
||||
body.input_style5:not(.setting_preview), .theme_settings .input_style5 {
|
||||
input.o_input, .o_input {
|
||||
border-bottom-right-radius: 50px;
|
||||
border-top-left-radius: 50px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15p;
|
||||
}
|
||||
}
|
||||
|
||||
// input style6 round
|
||||
body.input_style6:not(.setting_preview), .theme_settings .input_style6 {
|
||||
input.o_input, .o_input {
|
||||
border-radius: 50px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
// input style7 line
|
||||
body.input_style7:not(.setting_preview), .theme_settings .input_style7 {
|
||||
input.o_input, .o_input {
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15p;
|
||||
}
|
||||
}
|
||||
|
||||
// input style4 slant
|
||||
body.input_style8:not(.setting_preview), .theme_settings .input_style8 {
|
||||
input.o_input, .o_input {
|
||||
border-bottom-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15p;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { Component, useState, useRef } from "@odoo/owl";
|
||||
|
||||
|
||||
export class MaskChecker extends Component {
|
||||
|
||||
setup() {
|
||||
super.setup();
|
||||
this.root = useRef('root');
|
||||
}
|
||||
|
||||
/**
|
||||
* on mask click
|
||||
* @param {*} ev
|
||||
*/
|
||||
_on_mask_click() {
|
||||
this.props.onChecked();
|
||||
}
|
||||
|
||||
_on_mask_hover() {
|
||||
if (this.props.onEnter) {
|
||||
this.props.onEnter(this.root.el);
|
||||
}
|
||||
}
|
||||
|
||||
_on_mask_leave(event) {
|
||||
if (this.props.onLeave) {
|
||||
this.props.onLeave(this.root.el);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MaskChecker.props = {
|
||||
onChecked: {
|
||||
type: Function,
|
||||
optional: 1,
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
optional: 1,
|
||||
},
|
||||
slots: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
extraClass: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
onEnter: {
|
||||
type: Function,
|
||||
optional: 1,
|
||||
},
|
||||
onLeave: {
|
||||
type: Function,
|
||||
optional: 1,
|
||||
},
|
||||
};
|
||||
|
||||
MaskChecker.template = "anita_theme_setting.mask_checker";
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
.mask_checker {
|
||||
|
||||
position: relative;
|
||||
border: 1px dashed #dee2e6;
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:hover {
|
||||
border-color: #006fe6;
|
||||
background: #006fe60d;
|
||||
}
|
||||
|
||||
.checker_mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
z-index: 1;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<!-- style setting -->
|
||||
<t t-name="anita_theme_setting.mask_checker" owl="1">
|
||||
<div t-attf-class="mask_checker {{props.extraClass}}" t-ref="root">
|
||||
<t t-slot="default"/>
|
||||
<div class="checker_mask d-flex align-items-center justify-content-center" t-on-click.prevent="_on_mask_click" t-on-mouseenter="(event) => this._on_mask_hover(event)" t-on-mouseleave="()=>this._on_mask_leave()">
|
||||
<svg t-if="props.checked" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 85.504c-235.552 0-426.496 190.944-426.496 426.496s190.976 426.496 426.496 426.496c235.552 0 426.496-190.944 426.496-426.496s-190.944-426.496-426.496-426.496zM512 904.384c-216.352 0-392.384-176.032-392.384-392.384s176-392.384 392.384-392.384c216.352 0 392.384 176.032 392.384 392.384s-176 392.384-392.384 392.384z" p-id="2681" fill="#13227a"></path><path d="M725.344 359.712l-273.888 265.568-95.936-95.936c-9.984-9.984-26.176-9.984-36.192 0s-9.984 26.176 0 36.192l113.76 113.76c4.992 4.992 11.552 7.488 18.08 7.488 6.4 0 12.832-2.4 17.792-7.2l291.968-283.136c10.144-9.856 10.4-26.048 0.576-36.192-9.888-10.176-26.048-10.4-36.192-0.576z" p-id="2682" fill="#13227a"></path></svg>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
// radio box style2
|
||||
body.radio_style2, .theme_settings .radio_style2 {
|
||||
|
||||
.form-check-input[type=radio] {
|
||||
border: 1px solid #e6e6e6;
|
||||
border-radius: 30%;
|
||||
background: #fff;
|
||||
&:checked {
|
||||
background-image: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M692.071703 329.86173c-48.372707-48.387034-112.705921-75.023694-181.146687-75.023694-68.440765 0-132.747373 26.63666-181.148733 75.023694-48.400337 48.374754-75.022671 112.707968-75.022671 181.123151s26.623357 132.74635 75.022671 181.146687c48.374754 48.402383 112.707968 75.023694 181.148733 75.023694 68.441788 0 132.745327-26.647917 181.146687-75.023694 48.427966-48.373731 75.024717-112.706945 75.024717-181.146687C767.096421 442.544115 740.447481 378.237507 692.071703 329.86173z' fill='%231296db'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// radio box style3
|
||||
body.radio_style3, .theme_settings .radio_style3 {
|
||||
.form-check-input[type=radio] {
|
||||
border-width: 1px;
|
||||
border-radius: 30%;
|
||||
}
|
||||
}
|
||||
|
||||
// radio box style4
|
||||
body.radio_style4, .theme_settings .radio_style4 {
|
||||
.form-check-input[type=radio] {
|
||||
border-width: 1px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
&:checked {
|
||||
background-image: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath d='M692.071703 329.86173c-48.372707-48.387034-112.705921-75.023694-181.146687-75.023694-68.440765 0-132.747373 26.63666-181.148733 75.023694-48.400337 48.374754-75.022671 112.707968-75.022671 181.123151s26.623357 132.74635 75.022671 181.146687c48.374754 48.402383 112.707968 75.023694 181.148733 75.023694 68.441788 0 132.745327-26.647917 181.146687-75.023694 48.427966-48.373731 75.024717-112.706945 75.024717-181.146687C767.096421 442.544115 740.447481 378.237507 692.071703 329.86173z' fill='%231296db'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
|
||||
// separator style2
|
||||
body.separator_style2, .theme_settings .separator_style2 {
|
||||
hr {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 2px;
|
||||
border: none;
|
||||
border-bottom: 2px dotted rgba($purple, .25);
|
||||
float: left;
|
||||
width: 100vw;
|
||||
~ * {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// separator style3
|
||||
body.separator_style3, .theme_settings .separator_style3 {
|
||||
hr {
|
||||
border-top: 1px solid #8c8b8b;
|
||||
border-bottom: 1px solid #fff;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// separator style
|
||||
body.separator_style4, .theme_settings .separator_style4 {
|
||||
hr {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 2px;
|
||||
border: none;
|
||||
border-bottom: 2px dashed rgba($purple, .25);
|
||||
float: left;
|
||||
~ * {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||