Merge branch 'feature/virtual-rooms-1.2' of github.com:ONLYOFFICE/AppServer into feature/confirm-test
# Conflicts: # yarn.lock
This commit is contained in:
commit
bd7389fe27
@ -31,14 +31,11 @@ if [ "$DOCUMENT_SERVER_INSTALLED" = "false" ]; then
|
||||
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/ds-port select $DS_PORT | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/db-pwd select $DS_DB_PWD | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/db-user $DS_DB_USER | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/db-name $DS_DB_NAME | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-de $DS_COMMON_NAME/jwt-enabled select ${DS_JWT_ENABLED} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-de $DS_COMMON_NAME/jwt-secret select ${DS_JWT_SECRET} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-de $DS_COMMON_NAME/jwt-header select ${DS_JWT_HEADER} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-ee $DS_COMMON_NAME/jwt-enabled select ${DS_JWT_ENABLED} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-ee $DS_COMMON_NAME/jwt-secret select ${DS_JWT_SECRET} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver-ee $DS_COMMON_NAME/jwt-header select ${DS_JWT_HEADER} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/db-user select $DS_DB_USER | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/db-name select $DS_DB_NAME | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/jwt-enabled select ${DS_JWT_ENABLED} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/jwt-secret select ${DS_JWT_SECRET} | sudo debconf-set-selections
|
||||
echo ${package_sysname}-documentserver $DS_COMMON_NAME/jwt-header select ${DS_JWT_HEADER} | sudo debconf-set-selections
|
||||
|
||||
apt-get install -yq ${package_sysname}-documentserver
|
||||
elif [ "$UPDATE" = "true" ] && [ "$DOCUMENT_SERVER_INSTALLED" = "true" ]; then
|
||||
@ -65,55 +62,17 @@ else
|
||||
systemctl reload nginx
|
||||
fi
|
||||
|
||||
APPSERVER_INSTALLED_VERSION=$(apt-cache policy ${product} | awk 'NR==2{print $2}')
|
||||
APPSERVER_LATEST_VERSION=$(apt-cache policy ${product} | awk 'NR==3{print $2}')
|
||||
if [ "$APPSERVER_INSTALLED_VERSION" != "$APPSERVER_LATEST_VERSION" ]; then
|
||||
APPSERVER_NEED_UPDATE="true"
|
||||
fi
|
||||
|
||||
if [ "$APPSERVER_INSTALLED" = "false" ]; then
|
||||
echo ${product} ${product}/db-pwd select $MYSQL_SERVER_PASS | sudo debconf-set-selections
|
||||
echo ${product} ${product}/db-user select $MYSQL_SERVER_USER | sudo debconf-set-selections
|
||||
echo ${product} ${product}/db-name select $MYSQL_SERVER_DB_NAME | sudo debconf-set-selections
|
||||
|
||||
apt-get install -y ${product} || true #Fix error 'Failed to fetch'
|
||||
apt-get install -y ${product}
|
||||
elif [ "$APPSERVER_NEED_UPDATE" = "true" ]; then
|
||||
ENVIRONMENT="$(cat /lib/systemd/system/${product}-api.service | grep -oP 'ENVIRONMENT=\K.*')"
|
||||
USER_CONNECTIONSTRING=$(json -f /etc/onlyoffice/${product}/appsettings.$ENVIRONMENT.json ConnectionStrings.default.connectionString)
|
||||
MYSQL_SERVER_HOST=$(echo $USER_CONNECTIONSTRING | grep -oP 'Server=\K.*' | grep -o '^[^;]*')
|
||||
MYSQL_SERVER_DB_NAME=$(echo $USER_CONNECTIONSTRING | grep -oP 'Database=\K.*' | grep -o '^[^;]*')
|
||||
MYSQL_SERVER_USER=$(echo $USER_CONNECTIONSTRING | grep -oP 'User ID=\K.*' | grep -o '^[^;]*')
|
||||
MYSQL_SERVER_PORT=$(echo $USER_CONNECTIONSTRING | grep -oP 'Port=\K.*' | grep -o '^[^;]*')
|
||||
MYSQL_SERVER_PASS=$(echo $USER_CONNECTIONSTRING | grep -oP 'Password=\K.*' | grep -o '^[^;]*')
|
||||
|
||||
elif [ "$UPDATE" = "true" ] && [ "$APPSERVER_INSTALLED" = "true" ]; then
|
||||
apt-get install -o DPkg::options::="--force-confnew" -y --only-upgrade ${product} elasticsearch=${ELASTIC_VERSION}
|
||||
fi
|
||||
|
||||
if [ "${APPSERVER_INSTALLED}" = "false" ] || [ "${APPSERVER_NEED_UPDATE}" = "true" ]; then
|
||||
expect << EOF
|
||||
set timeout -1
|
||||
log_user 1
|
||||
|
||||
if { "${UPDATE}" == "true" } {
|
||||
spawn ${product}-configuration.sh -e ${ENVIRONMENT}
|
||||
} else {
|
||||
spawn ${product}-configuration.sh
|
||||
}
|
||||
|
||||
expect -re "Database host:"
|
||||
send "\025$MYSQL_SERVER_HOST\r"
|
||||
|
||||
expect -re "Database name:"
|
||||
send "\025$MYSQL_SERVER_DB_NAME\r"
|
||||
|
||||
expect -re "Database user:"
|
||||
send "\025$MYSQL_SERVER_USER\r"
|
||||
|
||||
expect -re "Database password:"
|
||||
send "\025$MYSQL_SERVER_PASS\r"
|
||||
|
||||
expect eof
|
||||
EOF
|
||||
APPSERVER_INSTALLED="true";
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$RES_INSTALL_SUCCESS"
|
||||
echo "$RES_QUESTIONS"
|
||||
|
@ -65,7 +65,7 @@ if [ "$(ls "$PRODUCT_DIR/services/kafka" 2> /dev/null)" == "" ]; then
|
||||
KAFKA_ARCHIVE=$(curl https://downloads.apache.org/kafka/$KAFKA_VERSION/ | grep -Eo "kafka_2.[0-9][0-9]-$KAFKA_VERSION.tgz" | tail -1)
|
||||
curl https://downloads.apache.org/kafka/$KAFKA_VERSION/$KAFKA_ARCHIVE -O
|
||||
tar xzf $KAFKA_ARCHIVE --strip 1 && rm -rf $KAFKA_ARCHIVE
|
||||
chown -R kafka ${PRODUCT_DIR}/services/kafka
|
||||
chown -R kafka ${PRODUCT_DIR}/services/kafka/
|
||||
cd -
|
||||
fi
|
||||
|
||||
@ -83,6 +83,7 @@ Restart=on-abnormal
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
END
|
||||
systemctl start zookeeper
|
||||
fi
|
||||
|
||||
if [ ! -e /lib/systemd/system/kafka.service ]; then
|
||||
@ -99,6 +100,7 @@ Restart=on-abnormal
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
END
|
||||
systemctl start kafka
|
||||
fi
|
||||
|
||||
if ! dpkg -l | grep -q "mysql-server"; then
|
||||
|
@ -97,6 +97,13 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
;;
|
||||
|
||||
-ess | --elasticsheme )
|
||||
if [ "$2" != "" ]; then
|
||||
ELK_SHEME=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
-esh | --elastichost )
|
||||
if [ "$2" != "" ]; then
|
||||
ELK_HOST=$2
|
||||
@ -106,7 +113,7 @@ while [ "$1" != "" ]; do
|
||||
|
||||
-esp | --elasticport )
|
||||
if [ "$2" != "" ]; then
|
||||
ELK_HOST=$2
|
||||
ELK_PORT=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
@ -118,6 +125,34 @@ while [ "$1" != "" ]; do
|
||||
fi
|
||||
;;
|
||||
|
||||
-mysqlh | --mysqlhost )
|
||||
if [ "$2" != "" ]; then
|
||||
DB_HOST=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
-mysqld | --mysqldatabase )
|
||||
if [ "$2" != "" ]; then
|
||||
DB_NAME=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
-mysqlu | --mysqluser )
|
||||
if [ "$2" != "" ]; then
|
||||
DB_USER=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
-mysqlp | --mysqlpassword )
|
||||
if [ "$2" != "" ]; then
|
||||
DB_PWD=$2
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
-? | -h | --help )
|
||||
echo " Usage: bash ${PRODUCT}-configuration.sh [PARAMETER] [[PARAMETER], ...]"
|
||||
echo
|
||||
@ -132,6 +167,10 @@ while [ "$1" != "" ]; do
|
||||
echo " -zkp, --zookeeperport zookeeper port (default 2181)"
|
||||
echo " -esh, --elastichost elasticsearch ip"
|
||||
echo " -esp, --elasticport elasticsearch port (default 9200)"
|
||||
echo " -mysqlh, --mysqlhost mysql server host"
|
||||
echo " -mysqld, --mysqldatabase ${PRODUCT} database name"
|
||||
echo " -mysqlu, --mysqluser ${PRODUCT} database user"
|
||||
echo " -mysqlp, --mysqlpassword ${PRODUCT} database password"
|
||||
echo " -e, --environment environment (default 'production')"
|
||||
echo " -?, -h, --help this help"
|
||||
echo
|
||||
@ -203,24 +242,10 @@ input_db_params(){
|
||||
local def_DB_NAME=$(echo $user_connectionString | grep -oP 'Database=\K.*' | grep -o '^[^;]*')
|
||||
local def_DB_USER=$(echo $user_connectionString | grep -oP 'User ID=\K.*' | grep -o '^[^;]*')
|
||||
|
||||
read -e -p "Database host: " -i "$DB_HOST" DB_HOST
|
||||
read -e -p "Database name: " -i "$DB_NAME" DB_NAME
|
||||
read -e -p "Database user: " -i "$DB_USER" DB_USER
|
||||
read -e -p "Database password: " -s DB_PWD
|
||||
|
||||
if [ -z $DB_HOST ]; then
|
||||
DB_HOST="${def_DB_HOST}";
|
||||
fi
|
||||
|
||||
if [ -z $DB_NAME ]; then
|
||||
DB_NAME="${def_DB_NAME}";
|
||||
fi
|
||||
|
||||
if [ -z $DB_USER ]; then
|
||||
DB_USER="${def_DB_USER}";
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ -z $def_DB_HOST ] && [ -z $DB_HOST ]; then read -e -p "Database host: " -i "$DB_HOST" DB_HOST; fi
|
||||
if [ -z $def_DB_NAME ] && [ -z $DB_NAME ]; then read -e -p "Database name: " -i "$DB_NAME" DB_NAME; fi
|
||||
if [ -z $def_DB_USER ] && [ -z $DB_USER ]; then read -e -p "Database user: " -i "$DB_USER" DB_USER; fi
|
||||
if [ -z $DB_PWD ]; then read -e -p "Database password: " -i "$DB_PWD" DB_PWD; fi
|
||||
}
|
||||
|
||||
establish_mysql_conn(){
|
||||
@ -575,10 +600,6 @@ elif command -v apt >/dev/null 2>&1; then
|
||||
DIST="Debian"
|
||||
PACKAGE_MANAGER="dpkg -l"
|
||||
MYSQL_PACKAGE="mysql"
|
||||
mkdir -p /var/log/onlyoffice/appserver/ /etc/onlyoffice/appserver/.private/
|
||||
chown -R onlyoffice:onlyoffice /var/www/appserver/ /var/log/onlyoffice/appserver/ /etc/onlyoffice/appserver/
|
||||
chown -R kafka /var/www/appserver/services/kafka/
|
||||
systemctl restart kafka zookeeper
|
||||
fi
|
||||
|
||||
install_json
|
||||
|
2
build/install/deb/debian/appserver-common.dirs
Normal file
2
build/install/deb/debian/appserver-common.dirs
Normal file
@ -0,0 +1,2 @@
|
||||
/var/log/onlyoffice/appserver
|
||||
/etc/onlyoffice/appserver/.private
|
@ -18,3 +18,4 @@ if ! cat /etc/passwd | grep -q "nginx:"; then
|
||||
fi
|
||||
|
||||
usermod -aG onlyoffice,nginx onlyoffice
|
||||
chown onlyoffice:onlyoffice /var/log/onlyoffice/appserver /var/www/appserver /etc/onlyoffice/appserver
|
||||
|
24
build/install/deb/debian/config
Normal file
24
build/install/deb/debian/config
Normal file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
db_input medium appserver/environment || true
|
||||
db_input medium appserver/host || true
|
||||
db_input medium appserver/port || true
|
||||
db_input medium appserver/kafka-host || true
|
||||
db_input medium appserver/kafka-port || true
|
||||
db_input medium appserver/zookeeper-host || true
|
||||
db_input medium appserver/zookeeper-port || true
|
||||
db_input medium appserver/elasticsearch-sheme || true
|
||||
db_input medium appserver/elasticsearch-host || true
|
||||
db_input medium appserver/elasticsearch-port || true
|
||||
|
||||
db_input medium appserver/db-host || true
|
||||
db_input medium appserver/db-name || true
|
||||
db_input medium appserver/db-user || true
|
||||
db_go
|
||||
|
||||
db_input critical appserver/db-pwd || true
|
||||
db_go
|
3
build/install/deb/debian/configure
vendored
3
build/install/deb/debian/configure
vendored
@ -1,3 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
set -e
|
65
build/install/deb/debian/postinst
Normal file
65
build/install/deb/debian/postinst
Normal file
@ -0,0 +1,65 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
db_get appserver/environment || true
|
||||
ENVIRONMENT="$RET"
|
||||
db_get appserver/host || true
|
||||
APP_HOST="$RET"
|
||||
db_get appserver/port || true
|
||||
APP_PORT="$RET"
|
||||
|
||||
db_get appserver/db-host || true
|
||||
DB_HOST="$RET"
|
||||
db_get appserver/db-name || true
|
||||
DB_NAME="$RET"
|
||||
db_get appserver/db-user || true
|
||||
DB_USER="$RET"
|
||||
db_get appserver/db-pwd || true
|
||||
DB_PWD="$RET"
|
||||
|
||||
db_get appserver/kafka-host || true
|
||||
KAFKA_HOST="$RET"
|
||||
db_get appserver/kafka-port || true
|
||||
KAFKA_PORT="$RET"
|
||||
|
||||
db_get appserver/zookeeper-host || true
|
||||
ZOOKEEPER_HOST="$RET"
|
||||
db_get appserver/zookeeper-port || true
|
||||
ZOOKEEPER_PORT="$RET"
|
||||
|
||||
db_get appserver/elasticsearch-sheme || true
|
||||
ELK_SHEME="$RET"
|
||||
db_get appserver/elasticsearch-host || true
|
||||
ELK_HOST="$RET"
|
||||
db_get appserver/elasticsearch-port || true
|
||||
ELK_PORT="$RET"
|
||||
|
||||
db_get onlyoffice/db-host || true
|
||||
DOCUMENT_SERVER_HOST="$RET"
|
||||
db_get onlyoffice/ds-port || true
|
||||
DOCUMENT_SERVER_PORT="$RET"
|
||||
|
||||
bash /usr/bin/appserver-configuration.sh -e $ENVIRONMENT -mysqlh $DB_HOST -mysqld $DB_NAME -mysqlu $DB_USER -mysqlp $DB_PWD -ash $APP_HOST -asp $APP_PORT \
|
||||
-dsh $DOCUMENT_SERVER_HOST -dsp $DOCUMENT_SERVER_PORT -kh $KAFKA_HOST -kp $KAFKA_PORT -zkh $ZOOKEEPER_HOST -zkp $ZOOKEEPER_PORT -ess $ELK_SHEME -esh $ELK_HOST -esp $ELK_PORT
|
||||
;;
|
||||
|
||||
abort-upgrade|abort-remove|abort-deconfigure)
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "postinst called with unknown argument \`$1'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
exit 0
|
@ -38,8 +38,13 @@ override_dh_auto_build:
|
||||
sed -i "s@var/www@var/www/${PRODUCT}@g" ${SRC_PATH}/config/nginx/*.conf
|
||||
sed -i "s@var/www@var/www/${PRODUCT}@g" ${SRC_PATH}/config/nginx/includes/*.conf
|
||||
|
||||
override_dh_fixperms:
|
||||
dh_fixperms
|
||||
|
||||
override_dh_auto_install:
|
||||
dh_installinit
|
||||
dh_systemd_enable
|
||||
dh_systemd_start --no-start
|
||||
|
||||
override_dh_strip:
|
||||
# dh_strip --exclude=/site-packages/
|
||||
|
@ -1,3 +1,18 @@
|
||||
Template: appserver/environment
|
||||
Type: string
|
||||
Default: production
|
||||
Description: Select environment for AppServer configuration:
|
||||
|
||||
Template: appserver/host
|
||||
Type: string
|
||||
Default: localhost
|
||||
Description: AppServer host:
|
||||
|
||||
Template: appserver/port
|
||||
Type: string
|
||||
Default: 80
|
||||
Description: AppServer port:
|
||||
|
||||
Template: appserver/db-host
|
||||
Type: string
|
||||
Default: localhost
|
||||
@ -17,23 +32,39 @@ Type: string
|
||||
Default: onlyoffice
|
||||
Description: MySQL database name:
|
||||
|
||||
Template: appserver/remove-db
|
||||
Type: boolean
|
||||
Default: false
|
||||
Description: Remove database?
|
||||
This operation will remove the database which contain all data. It is recommended to take backup before removing the database.
|
||||
|
||||
Template: appserver/ds-jwt-enabled
|
||||
Type: boolean
|
||||
Default: false
|
||||
Description: To enabled Document Server JWT?:
|
||||
|
||||
Template: appserver/ds-jwt-secret
|
||||
Template: appserver/kafka-host
|
||||
Type: string
|
||||
Default: {{package_sysname}}
|
||||
Description: Document Server JWT Secret:
|
||||
Default: localhost
|
||||
Description: Kafka host:
|
||||
|
||||
Template: appserver/ds-jwt-secret-header
|
||||
Template: appserver/kafka-port
|
||||
Type: string
|
||||
Default: AuthorizationJwt
|
||||
Description: Document Server Secret Header:
|
||||
Default: 9092
|
||||
Description: Kafka port:
|
||||
|
||||
Template: appserver/zookeeper-host
|
||||
Type: string
|
||||
Default: localhost
|
||||
Description: Zookeeper host:
|
||||
|
||||
Template: appserver/zookeeper-port
|
||||
Type: string
|
||||
Default: 2181
|
||||
Description: Zookeeper port:
|
||||
|
||||
Template: appserver/elasticsearch-sheme
|
||||
Type: select
|
||||
Choices: http, https
|
||||
Default: http
|
||||
Description: Elasticsearch sheme:
|
||||
|
||||
Template: appserver/elasticsearch-host
|
||||
Type: string
|
||||
Default: localhost
|
||||
Description: Elasticsearch host:
|
||||
|
||||
Template: appserver/elasticsearch-port
|
||||
Type: string
|
||||
Default: 9200
|
||||
Description: Elasticsearch port:
|
||||
|
||||
|
@ -173,6 +173,26 @@ namespace ASC.Web.Api.Models
|
||||
return lambda;
|
||||
}
|
||||
|
||||
public EmployeeWraperFull GetSimple(UserInfo userInfo)
|
||||
{
|
||||
var result = new EmployeeWraperFull
|
||||
{
|
||||
FirstName = userInfo.FirstName,
|
||||
LastName = userInfo.LastName,
|
||||
};
|
||||
|
||||
FillGroups(result, userInfo);
|
||||
|
||||
var photoData = UserPhotoManager.GetUserPhotoData(userInfo.ID, UserPhotoManager.BigFotoSize);
|
||||
|
||||
if (photoData != null)
|
||||
{
|
||||
result.Avatar = "data:image/png;base64," + Convert.ToBase64String(photoData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public EmployeeWraperFull GetFull(UserInfo userInfo)
|
||||
{
|
||||
var result = new EmployeeWraperFull
|
||||
@ -223,23 +243,7 @@ namespace ASC.Web.Api.Models
|
||||
}
|
||||
|
||||
FillConacts(result, userInfo);
|
||||
|
||||
if (Context.Check("groups") || Context.Check("department"))
|
||||
{
|
||||
var groups = UserManager.GetUserGroups(userInfo.ID)
|
||||
.Select(x => new GroupWrapperSummary(x, UserManager))
|
||||
.ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
result.Groups = groups;
|
||||
result.Department = string.Join(", ", result.Groups.Select(d => d.Name.HtmlEncode()));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Department = "";
|
||||
}
|
||||
}
|
||||
FillGroups(result, userInfo);
|
||||
|
||||
var userInfoLM = userInfo.LastModified.GetHashCode();
|
||||
|
||||
@ -269,6 +273,28 @@ namespace ASC.Web.Api.Models
|
||||
return result;
|
||||
}
|
||||
|
||||
private void FillGroups(EmployeeWraperFull result, UserInfo userInfo)
|
||||
{
|
||||
if (!Context.Check("groups") && !Context.Check("department"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var groups = UserManager.GetUserGroups(userInfo.ID)
|
||||
.Select(x => new GroupWrapperSummary(x, UserManager))
|
||||
.ToList();
|
||||
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
result.Groups = groups;
|
||||
result.Department = string.Join(", ", result.Groups.Select(d => d.Name.HtmlEncode()));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Department = "";
|
||||
}
|
||||
}
|
||||
|
||||
private void FillConacts(EmployeeWraperFull employeeWraperFull, UserInfo userInfo)
|
||||
{
|
||||
if (userInfo.ContactsList == null) return;
|
||||
|
@ -89,7 +89,7 @@ namespace ASC.ElasticSearch
|
||||
private bool IsExist { get; set; }
|
||||
private Client Client { get; }
|
||||
private ILog Log { get; }
|
||||
private TenantManager TenantManager { get; }
|
||||
protected TenantManager TenantManager { get; }
|
||||
private BaseIndexerHelper BaseIndexerHelper { get; }
|
||||
private Settings Settings { get; }
|
||||
private IServiceProvider ServiceProvider { get; }
|
||||
@ -116,7 +116,8 @@ namespace ASC.ElasticSearch
|
||||
|
||||
internal void Index(T data, bool immediately = true)
|
||||
{
|
||||
CreateIfNotExist(data);
|
||||
if (!BeforeIndex(data)) return;
|
||||
|
||||
Client.Instance.Index(data, idx => GetMeta(idx, data, immediately));
|
||||
}
|
||||
|
||||
@ -124,7 +125,7 @@ namespace ASC.ElasticSearch
|
||||
{
|
||||
if (data.Count == 0) return;
|
||||
|
||||
CreateIfNotExist(data[0]);
|
||||
if (!CheckExist(data[0])) return;
|
||||
|
||||
if (data[0] is ISearchItemDocument)
|
||||
{
|
||||
@ -135,7 +136,9 @@ namespace ASC.ElasticSearch
|
||||
for (var i = 0; i < data.Count; i++)
|
||||
{
|
||||
var t = data[i];
|
||||
var runBulk = i == data.Count - 1;
|
||||
var runBulk = i == data.Count - 1;
|
||||
|
||||
BeforeIndex(t);
|
||||
|
||||
if (!(t is ISearchItemDocument wwd) || wwd.Document == null || string.IsNullOrEmpty(wwd.Document.Data))
|
||||
{
|
||||
@ -207,14 +210,19 @@ namespace ASC.ElasticSearch
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
BeforeIndex(item);
|
||||
}
|
||||
|
||||
Client.Instance.Bulk(r => r.IndexMany(data, GetMeta));
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task IndexAsync(List<T> data, bool immediately = true)
|
||||
{
|
||||
CreateIfNotExist(data[0]);
|
||||
if (!CheckExist(data[0])) return;
|
||||
|
||||
if (data is ISearchItemDocument)
|
||||
{
|
||||
@ -227,6 +235,8 @@ namespace ASC.ElasticSearch
|
||||
var t = data[i];
|
||||
var runBulk = i == data.Count - 1;
|
||||
|
||||
await BeforeIndexAsync(t);
|
||||
|
||||
var wwd = t as ISearchItemDocument;
|
||||
|
||||
if (wwd == null || wwd.Document == null || string.IsNullOrEmpty(wwd.Document.Data))
|
||||
@ -298,31 +308,36 @@ namespace ASC.ElasticSearch
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in data)
|
||||
{
|
||||
await BeforeIndexAsync(item);
|
||||
}
|
||||
|
||||
await Client.Instance.BulkAsync(r => r.IndexMany(data, GetMeta));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(T data, bool immediately = true, params Expression<Func<T, object>>[] fields)
|
||||
{
|
||||
CreateIfNotExist(data);
|
||||
if (!CheckExist(data)) return;
|
||||
Client.Instance.Update(DocumentPath<T>.Id(data), r => GetMetaForUpdate(r, data, immediately, fields));
|
||||
}
|
||||
|
||||
internal void Update(T data, UpdateAction action, Expression<Func<T, IList>> fields, bool immediately = true)
|
||||
{
|
||||
CreateIfNotExist(data);
|
||||
if (!CheckExist(data)) return;
|
||||
Client.Instance.Update(DocumentPath<T>.Id(data), r => GetMetaForUpdate(r, data, action, fields, immediately));
|
||||
}
|
||||
|
||||
internal void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, int tenantId, bool immediately = true, params Expression<Func<T, object>>[] fields)
|
||||
{
|
||||
CreateIfNotExist(data);
|
||||
if (!CheckExist(data)) return;
|
||||
Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, immediately, fields));
|
||||
}
|
||||
|
||||
internal void Update(T data, Expression<Func<Selector<T>, Selector<T>>> expression, int tenantId, UpdateAction action, Expression<Func<T, IList>> fields, bool immediately = true)
|
||||
{
|
||||
CreateIfNotExist(data);
|
||||
if (!CheckExist(data)) return;
|
||||
Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, action, fields, immediately));
|
||||
}
|
||||
|
||||
@ -389,8 +404,7 @@ namespace ASC.ElasticSearch
|
||||
|
||||
Log.DebugFormat("Delete {0}", Wrapper.IndexName);
|
||||
Client.Instance.Indices.Delete(Wrapper.IndexName);
|
||||
BaseIndexerHelper.Clear(Wrapper);
|
||||
CreateIfNotExist(Wrapper);
|
||||
BaseIndexerHelper.Clear(Wrapper);
|
||||
}
|
||||
|
||||
internal IReadOnlyCollection<T> Select(Expression<Func<Selector<T>, Selector<T>>> expression, bool onlyId = false)
|
||||
@ -411,6 +425,16 @@ namespace ASC.ElasticSearch
|
||||
return result.Documents;
|
||||
}
|
||||
|
||||
protected virtual bool BeforeIndex(T data)
|
||||
{
|
||||
return CheckExist(data);
|
||||
}
|
||||
|
||||
protected virtual Task<bool> BeforeIndexAsync(T data)
|
||||
{
|
||||
return Task.FromResult(CheckExist(data));
|
||||
}
|
||||
|
||||
public void CreateIfNotExist(T data)
|
||||
{
|
||||
try
|
||||
|
@ -41,6 +41,10 @@
|
||||
"cSpell.words": ["appserver", "browserslist", "debuginfo", "doceditor"]
|
||||
},
|
||||
"extensions": {
|
||||
"recommendations": ["folke.vscode-monorepo-workspace"]
|
||||
"recommendations": [
|
||||
"folke.vscode-monorepo-workspace",
|
||||
"orta.vscode-jest",
|
||||
"firsttris.vscode-jest-runner"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ export const request = function (options) {
|
||||
switch (error.response?.status) {
|
||||
case 401:
|
||||
if (options.skipUnauthorized) return Promise.resolve();
|
||||
if (options.skipLogout) return Promise.reject(errorText || error);
|
||||
|
||||
request({
|
||||
method: "post",
|
||||
|
@ -42,6 +42,23 @@ export function getUser(userName = null) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
export function getUserFromConfirm(userId, confirmKey = null) {
|
||||
const options = {
|
||||
method: "get",
|
||||
url: `/people/${userId}.json`,
|
||||
};
|
||||
|
||||
if (confirmKey) options.headers = { confirm: confirmKey };
|
||||
|
||||
return request(options).then((user) => {
|
||||
if (user && user.displayName) {
|
||||
user.displayName = Encoder.htmlDecode(user.displayName);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
export function getUserPhoto(userId) {
|
||||
return request({
|
||||
method: "get",
|
||||
|
@ -54,3 +54,11 @@ export function getInvitationLinks() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function setPortalRename(alias) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/portal/portalrename.json",
|
||||
data: { alias },
|
||||
});
|
||||
}
|
||||
|
@ -25,6 +25,82 @@ export function getPortalPasswordSettings(confirmKey = null) {
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function setPortalPasswordSettings(
|
||||
minLength,
|
||||
upperCase,
|
||||
digits,
|
||||
specSymbols
|
||||
) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/security/password.json",
|
||||
data: { minLength, upperCase, digits, specSymbols },
|
||||
});
|
||||
}
|
||||
|
||||
export function setMailDomainSettings(data) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/settings/maildomainsettings.json",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function setDNSSettings(dnsName, enable) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/settings/maildomainsettings.json",
|
||||
data: { dnsName, enable },
|
||||
});
|
||||
}
|
||||
|
||||
export function setIpRestrictions(data) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/iprestrictions.json",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function setIpRestrictionsEnable(data) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/iprestrictions/settings.json",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function setMessageSettings(turnOn) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/settings/messagesettings.json",
|
||||
data: { turnOn },
|
||||
});
|
||||
}
|
||||
|
||||
export function setCookieSettings(lifeTime) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/cookiesettings.json",
|
||||
data: { lifeTime },
|
||||
});
|
||||
}
|
||||
|
||||
export function setLifetimeAuditSettings(data) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/security/audit/settings/lifetime.json",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export function getAuditTrailReport() {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/security/audit/login/report.json",
|
||||
});
|
||||
}
|
||||
|
||||
export function getPortalTimezones(confirmKey = null) {
|
||||
const options = {
|
||||
method: "get",
|
||||
@ -79,6 +155,24 @@ export function getLogoUrls() {
|
||||
});
|
||||
}
|
||||
|
||||
export function setWhiteLabelSettings(data) {
|
||||
const options = {
|
||||
method: "post",
|
||||
url: "/settings/whitelabel/save.json",
|
||||
data,
|
||||
};
|
||||
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function restoreWhiteLabelSettings(isDefault) {
|
||||
return request({
|
||||
method: "put",
|
||||
url: "/settings/whitelabel/restore.json",
|
||||
data: { isDefault },
|
||||
});
|
||||
}
|
||||
|
||||
export function getCustomSchemaList() {
|
||||
return request({
|
||||
method: "get",
|
||||
@ -332,6 +426,14 @@ export function getBuildVersion() {
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function getCapabilities() {
|
||||
const options = {
|
||||
method: "get",
|
||||
url: "/capabilities",
|
||||
};
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function getTipsSubscription() {
|
||||
const options = {
|
||||
method: "get",
|
||||
|
@ -10,6 +10,7 @@ export function login(userName, passwordHash, session) {
|
||||
return request({
|
||||
method: "post",
|
||||
url: "/authentication.json",
|
||||
skipLogout: true,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
@ -1,15 +1,10 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Selector from "./sub-components/Selector";
|
||||
import utils from "@appserver/components/utils";
|
||||
import Backdrop from "@appserver/components/backdrop";
|
||||
import DropDown from "@appserver/components/drop-down";
|
||||
import Aside from "@appserver/components/aside";
|
||||
|
||||
import throttle from "lodash/throttle";
|
||||
const { desktop } = utils.device;
|
||||
|
||||
const displayTypes = ["dropdown", "aside", "auto"];
|
||||
const sizes = ["compact", "full"];
|
||||
|
||||
class AdvancedSelector extends React.Component {
|
||||
@ -17,75 +12,15 @@ class AdvancedSelector extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.ref = React.createRef();
|
||||
this.state = {
|
||||
displayType: this.getTypeByWidth(),
|
||||
};
|
||||
|
||||
this.throttledResize = throttle(this.resize, 300);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.isOpen) {
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
}
|
||||
}
|
||||
|
||||
resize = () => {
|
||||
if (this.props.displayType !== "auto") return;
|
||||
|
||||
const type = this.getTypeByWidth();
|
||||
|
||||
if (type === this.state.displayType) return;
|
||||
|
||||
this.setState({ displayType: type });
|
||||
};
|
||||
|
||||
onClose = (e) => {
|
||||
//console.log("onClose");
|
||||
//this.setState({ isOpen: false });
|
||||
this.props.onCancel && this.props.onCancel(e);
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.isOpen !== prevProps.isOpen) {
|
||||
//console.log(`ADSelector componentDidUpdate isOpen=${this.props.isOpen}`);
|
||||
if (this.props.isOpen) {
|
||||
this.resize();
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
} else {
|
||||
this.throttledResize.cancel();
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.displayType !== prevProps.displayType) {
|
||||
//console.log(`ADSelector componentDidUpdate displayType=${this.props.displayType}`);
|
||||
this.setState({ displayType: this.getTypeByWidth() });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.throttledResize) {
|
||||
this.throttledResize && this.throttledResize.cancel();
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
}
|
||||
}
|
||||
|
||||
getTypeByWidth = () => {
|
||||
const displayType =
|
||||
this.props.displayType !== "auto"
|
||||
? this.props.displayType
|
||||
: window.innerWidth < desktop.match(/\d+/)[0]
|
||||
? "aside"
|
||||
: "dropdown";
|
||||
|
||||
//console.log("AdvancedSelector2 displayType", displayType);
|
||||
|
||||
return displayType;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { displayType } = this.state;
|
||||
const {
|
||||
isOpen,
|
||||
id,
|
||||
@ -96,24 +31,10 @@ class AdvancedSelector extends React.Component {
|
||||
smallSectionWidth,
|
||||
} = this.props;
|
||||
|
||||
//console.log(`AdvancedSelector render() isOpen=${isOpen} displayType=${displayType}`);
|
||||
|
||||
return (
|
||||
<div id={id} className={className} style={style}>
|
||||
{displayType === "dropdown" ? (
|
||||
<DropDown
|
||||
forwardedRef={this.ref}
|
||||
open={isOpen}
|
||||
className="selector_dropdown-container"
|
||||
smallSectionWidth={smallSectionWidth}
|
||||
isDefaultMode={isDefaultDisplayDropDown}
|
||||
className="dropdown-container"
|
||||
clickOutsideAction={this.onClose}
|
||||
>
|
||||
<Selector {...this.props} displayType={displayType} />
|
||||
</DropDown>
|
||||
) : withoutAside ? (
|
||||
<Selector {...this.props} displayType={displayType} />
|
||||
{withoutAside ? (
|
||||
<Selector {...this.props} />
|
||||
) : (
|
||||
<>
|
||||
<Backdrop
|
||||
@ -123,7 +44,7 @@ class AdvancedSelector extends React.Component {
|
||||
isAside={true}
|
||||
/>
|
||||
<Aside visible={isOpen} scale={false} className="aside-container">
|
||||
<Selector {...this.props} displayType={displayType} />
|
||||
<Selector {...this.props} />
|
||||
</Aside>
|
||||
</>
|
||||
)}
|
||||
@ -134,7 +55,7 @@ class AdvancedSelector extends React.Component {
|
||||
|
||||
AdvancedSelector.propTypes = {
|
||||
id: PropTypes.string,
|
||||
className: PropTypes.oneOf([PropTypes.string, PropTypes.array]),
|
||||
className: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
style: PropTypes.object,
|
||||
options: PropTypes.array,
|
||||
selectedOptions: PropTypes.array,
|
||||
@ -147,7 +68,6 @@ AdvancedSelector.propTypes = {
|
||||
buttonLabel: PropTypes.string,
|
||||
|
||||
size: PropTypes.oneOf(sizes),
|
||||
displayType: PropTypes.oneOf(displayTypes),
|
||||
|
||||
maxHeight: PropTypes.number,
|
||||
|
||||
@ -178,7 +98,6 @@ AdvancedSelector.defaultProps = {
|
||||
selectAllLabel: "Select all",
|
||||
allowGroupSelection: false,
|
||||
allowAnyClickClose: true,
|
||||
displayType: "auto",
|
||||
options: [],
|
||||
isDefaultDisplayDropDown: true,
|
||||
};
|
||||
|
@ -8,9 +8,9 @@ class Body extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, displayType, className, style } = this.props;
|
||||
const { children, className, style } = this.props;
|
||||
return (
|
||||
<StyledBody displayType={displayType} className={className} style={style}>
|
||||
<StyledBody className={className} style={style}>
|
||||
{children}
|
||||
</StyledBody>
|
||||
);
|
||||
@ -21,7 +21,6 @@ Body.propTypes = {
|
||||
children: PropTypes.any,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
displayType: PropTypes.oneOf(["dropdown", "aside"]),
|
||||
};
|
||||
|
||||
export default Body;
|
||||
|
@ -8,14 +8,9 @@ class Column extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, displayType, className, style, size } = this.props;
|
||||
const { children, className, style, size } = this.props;
|
||||
return (
|
||||
<StyledColumn
|
||||
displayType={displayType}
|
||||
className={className}
|
||||
style={style}
|
||||
size={size}
|
||||
>
|
||||
<StyledColumn className={className} style={style} size={size}>
|
||||
{children}
|
||||
</StyledColumn>
|
||||
);
|
||||
@ -26,7 +21,6 @@ Column.propTypes = {
|
||||
children: PropTypes.any,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
displayType: PropTypes.oneOf(["dropdown", "aside"]),
|
||||
size: PropTypes.oneOf(["compact", "full"]),
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ const Footer = (props) => {
|
||||
<Button
|
||||
className="add_members_btn"
|
||||
primary={true}
|
||||
size="big"
|
||||
size="normal"
|
||||
label={`${selectButtonLabel} ${
|
||||
selectedLength && showCounter ? `(${selectedLength})` : ""
|
||||
}`}
|
||||
|
@ -8,13 +8,9 @@ class Header extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, displayType, className, style } = this.props;
|
||||
const { children, className, style } = this.props;
|
||||
return (
|
||||
<StyledHeader
|
||||
displayType={displayType}
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
<StyledHeader className={className} style={style}>
|
||||
{children}
|
||||
</StyledHeader>
|
||||
);
|
||||
|
@ -9,15 +9,15 @@ import InfiniteLoader from "react-window-infinite-loader";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import ReactTooltip from "react-tooltip";
|
||||
|
||||
import Avatar from "@appserver/components/avatar";
|
||||
import Checkbox from "@appserver/components/checkbox";
|
||||
import Link from "@appserver/components/link";
|
||||
import ComboBox from "@appserver/components/combobox";
|
||||
import SearchInput from "@appserver/components/search-input";
|
||||
import Loader from "@appserver/components/loader";
|
||||
import Text from "@appserver/components/text";
|
||||
import Tooltip from "@appserver/components/tooltip";
|
||||
import Heading from "@appserver/components/heading";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-scrollbars-virtual-list";
|
||||
import HelpButton from "@appserver/components/help-button";
|
||||
|
||||
import StyledSelector from "./StyledSelector";
|
||||
|
||||
@ -45,7 +45,6 @@ const getCurrentGroup = (items) => {
|
||||
|
||||
const Selector = (props) => {
|
||||
const {
|
||||
displayType,
|
||||
groups,
|
||||
selectButtonLabel,
|
||||
isDisabled,
|
||||
@ -70,14 +69,11 @@ const Selector = (props) => {
|
||||
allowGroupSelection,
|
||||
embeddedComponent,
|
||||
showCounter,
|
||||
onArrowClick,
|
||||
headerLabel,
|
||||
} = props;
|
||||
|
||||
//console.log("options", options);
|
||||
//console.log("hasNextPage", hasNextPage);
|
||||
//console.log("isNextPageLoading", isNextPageLoading);
|
||||
|
||||
const listOptionsRef = useRef(null);
|
||||
const listGroupsRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
Object.keys(currentGroup).length === 0 &&
|
||||
@ -94,12 +90,16 @@ const Selector = (props) => {
|
||||
);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
|
||||
const [selectedAll, setSelectedAll] = useState(false);
|
||||
|
||||
const [currentGroup, setCurrentGroup] = useState(
|
||||
getCurrentGroup(convertGroups(groups))
|
||||
);
|
||||
|
||||
const [groupHeader, setGroupHeader] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (groups.length === 1) setGroupHeader(groups[0]);
|
||||
}, [groups]);
|
||||
|
||||
// Every row is loaded except for our loading indicator row.
|
||||
const isItemLoaded = useCallback(
|
||||
(index) => {
|
||||
@ -109,9 +109,9 @@ const Selector = (props) => {
|
||||
);
|
||||
|
||||
const onOptionChange = useCallback(
|
||||
(e) => {
|
||||
const option = options[+e.target.value];
|
||||
const newSelected = e.target.checked
|
||||
(index, isChecked) => {
|
||||
const option = options[index];
|
||||
const newSelected = !isChecked
|
||||
? [option, ...selectedOptionList]
|
||||
: selectedOptionList.filter((el) => el.key !== option.key);
|
||||
setSelectedOptionList(newSelected);
|
||||
@ -121,7 +121,7 @@ const Selector = (props) => {
|
||||
const newSelectedGroups = [];
|
||||
const removedSelectedGroups = [];
|
||||
|
||||
if (e.target.checked) {
|
||||
if (isChecked) {
|
||||
option.groups.forEach((g) => {
|
||||
let index = selectedGroupList.findIndex((sg) => sg.key === g);
|
||||
if (index > -1) {
|
||||
@ -187,69 +187,12 @@ const Selector = (props) => {
|
||||
[options, selectedOptionList, groups, selectedGroupList]
|
||||
);
|
||||
|
||||
const onGroupChange = useCallback(
|
||||
(e) => {
|
||||
const group = convertGroup(groups[+e.target.value]);
|
||||
group.selected = e.target.checked ? group.total : 0;
|
||||
const newSelectedGroups = e.target.checked
|
||||
? [group, ...selectedGroupList]
|
||||
: selectedGroupList.filter((el) => el.key !== group.key);
|
||||
//console.log("onGroupChange", item);
|
||||
setSelectedGroupList(newSelectedGroups);
|
||||
|
||||
onGroupSelect(group);
|
||||
|
||||
if (e.target.checked) {
|
||||
//const newSelectedOptions = [];
|
||||
//options.forEach(o => o.groups.forEach(gKey => group.))
|
||||
//setSelectedOptionList()
|
||||
//TODO: Implement setSelectedOptionList changes
|
||||
}
|
||||
},
|
||||
[groups, selectedGroupList, currentGroup]
|
||||
);
|
||||
|
||||
const resetCache = useCallback(() => {
|
||||
if (listOptionsRef && listOptionsRef.current) {
|
||||
listOptionsRef.current.resetloadMoreItemsCache(true);
|
||||
}
|
||||
}, [listOptionsRef]);
|
||||
|
||||
const onGroupSelect = useCallback(
|
||||
(group) => {
|
||||
if (!currentGroup || !group || currentGroup.key === group.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentGroup(group);
|
||||
onGroupChanged && onGroupChanged(group);
|
||||
|
||||
if (displayType === "aside" && isMultiSelect) {
|
||||
setSelectedAll(isGroupChecked(group));
|
||||
}
|
||||
},
|
||||
[displayType, isMultiSelect, currentGroup]
|
||||
);
|
||||
|
||||
const onSelectAllChange = useCallback(() => {
|
||||
const checked = !selectedAll;
|
||||
//console.log("onSelectAllChange", checked);
|
||||
setSelectedAll(checked);
|
||||
|
||||
if (!currentGroup) return;
|
||||
|
||||
const group = convertGroup(currentGroup);
|
||||
|
||||
if (!group) return;
|
||||
|
||||
group.selected = checked ? group.total : 0;
|
||||
const newSelectedGroups = checked
|
||||
? [group, ...selectedGroupList]
|
||||
: selectedGroupList.filter((el) => el.key !== group.key);
|
||||
|
||||
setSelectedGroupList(newSelectedGroups);
|
||||
}, [selectedAll, currentGroup, selectedGroupList]);
|
||||
|
||||
const onSearchChange = useCallback((value) => {
|
||||
setSearchValue(value);
|
||||
onSearchChanged && onSearchChanged(value);
|
||||
@ -259,10 +202,6 @@ const Selector = (props) => {
|
||||
onSearchChanged && onSearchChange("");
|
||||
});
|
||||
|
||||
const onSelectOptions = (items) => {
|
||||
onSelect && onSelect(items);
|
||||
};
|
||||
|
||||
const isOptionChecked = useCallback(
|
||||
(option) => {
|
||||
const checked =
|
||||
@ -282,12 +221,16 @@ const Selector = (props) => {
|
||||
},
|
||||
[selectedOptionList, selectedGroupList]
|
||||
);
|
||||
const onSelectOptions = (items) => {
|
||||
onSelect && onSelect(items);
|
||||
};
|
||||
|
||||
const onAddClick = useCallback(() => {
|
||||
onSelectOptions(selectedOptionList);
|
||||
}, [selectedOptionList]);
|
||||
|
||||
const onLinkClick = useCallback(
|
||||
(e) => {
|
||||
const index = e.target.dataset.index;
|
||||
if (!index) return;
|
||||
|
||||
(index) => {
|
||||
const option = options[index];
|
||||
|
||||
if (!option) return;
|
||||
@ -297,79 +240,73 @@ const Selector = (props) => {
|
||||
[options]
|
||||
);
|
||||
|
||||
const onAddClick = useCallback(() => {
|
||||
onSelectOptions(selectedOptionList);
|
||||
}, [selectedOptionList]);
|
||||
|
||||
const renderOptionItem = useCallback(
|
||||
(index, style, option, isChecked, tooltipProps) => {
|
||||
return isMultiSelect ? (
|
||||
<div style={style} className="row-option" {...tooltipProps}>
|
||||
<div
|
||||
style={style}
|
||||
className="row-option"
|
||||
value={`${index}`}
|
||||
name={`selector-row-option-${index}`}
|
||||
onClick={() => onOptionChange(index, isChecked)}
|
||||
{...tooltipProps}
|
||||
>
|
||||
<div className="option-info">
|
||||
<Avatar
|
||||
className="option-avatar"
|
||||
role="user"
|
||||
size="min"
|
||||
source={option.avatarUrl}
|
||||
userName={option.label}
|
||||
/>
|
||||
<Text
|
||||
className="option-text"
|
||||
truncate={true}
|
||||
noSelect={true}
|
||||
fontSize="14px"
|
||||
>
|
||||
{option.label}
|
||||
</Text>
|
||||
</div>
|
||||
<Checkbox
|
||||
id={option.key}
|
||||
value={`${index}`}
|
||||
label={option.label}
|
||||
isChecked={isChecked}
|
||||
className="option_checkbox"
|
||||
truncate={true}
|
||||
title={option.label}
|
||||
onChange={onOptionChange}
|
||||
className="option-checkbox"
|
||||
/>
|
||||
{displayType === "aside" && getOptionTooltipContent && (
|
||||
<HelpButton
|
||||
id={`info-${option.key}`}
|
||||
className="option-info"
|
||||
iconName="/static/images/info.react.svg"
|
||||
color="#D8D8D8"
|
||||
getContent={getOptionTooltipContent}
|
||||
place="top"
|
||||
offsetLeft={150}
|
||||
offsetRight={0}
|
||||
offsetTop={60}
|
||||
offsetBottom={0}
|
||||
dataTip={`${index}`}
|
||||
displayType="dropdown"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Link
|
||||
<div
|
||||
key={option.key}
|
||||
data-index={index}
|
||||
isTextOverflow={true}
|
||||
style={style}
|
||||
className="row-option"
|
||||
data-index={index}
|
||||
name={`selector-row-option-${index}`}
|
||||
onClick={() => onLinkClick(index)}
|
||||
{...tooltipProps}
|
||||
onClick={onLinkClick}
|
||||
noHover
|
||||
>
|
||||
{option.label}
|
||||
{displayType === "aside" && getOptionTooltipContent && (
|
||||
<HelpButton
|
||||
id={`info-${option.key}`}
|
||||
className="option-info"
|
||||
iconName="/static/images/info.react.svg"
|
||||
color="#D8D8D8"
|
||||
getContent={getOptionTooltipContent}
|
||||
place="top"
|
||||
offsetLeft={150}
|
||||
offsetRight={0}
|
||||
offsetTop={60}
|
||||
offsetBottom={0}
|
||||
dataTip={`${index}`}
|
||||
displayType="dropdown"
|
||||
<div className="option-info">
|
||||
{" "}
|
||||
<Avatar
|
||||
className="option-avatar"
|
||||
role="user"
|
||||
size="min"
|
||||
source={option.avatarUrl}
|
||||
userName={option.label}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
<Text
|
||||
className="option-text"
|
||||
truncate={true}
|
||||
noSelect={true}
|
||||
fontSize="14px"
|
||||
>
|
||||
{option.label}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[
|
||||
isMultiSelect,
|
||||
onOptionChange,
|
||||
onLinkClick,
|
||||
displayType,
|
||||
getOptionTooltipContent,
|
||||
]
|
||||
[isMultiSelect, onOptionChange, onLinkClick]
|
||||
);
|
||||
|
||||
const renderOptionLoader = useCallback(
|
||||
@ -385,7 +322,9 @@ const Selector = (props) => {
|
||||
marginRight: "10px",
|
||||
}}
|
||||
/>
|
||||
<Text as="span">{loadingLabel}</Text>
|
||||
<Text as="span" noSelect={true}>
|
||||
{loadingLabel}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -407,9 +346,6 @@ const Selector = (props) => {
|
||||
const isChecked = isOptionChecked(option);
|
||||
let tooltipProps = {};
|
||||
|
||||
if (displayType === "dropdown")
|
||||
tooltipProps = { "data-for": "user", "data-tip": index };
|
||||
|
||||
ReactTooltip.rebuild();
|
||||
|
||||
return renderOptionItem(index, style, option, isChecked, tooltipProps);
|
||||
@ -421,7 +357,6 @@ const Selector = (props) => {
|
||||
loadingLabel,
|
||||
options,
|
||||
isOptionChecked,
|
||||
displayType,
|
||||
isMultiSelect,
|
||||
onOptionChange,
|
||||
onLinkClick,
|
||||
@ -429,119 +364,6 @@ const Selector = (props) => {
|
||||
]
|
||||
);
|
||||
|
||||
const isGroupChecked = useCallback(
|
||||
(group) => {
|
||||
const selectedGroup = selectedGroupList.find((g) => g.key === group.key);
|
||||
return !!selectedGroup;
|
||||
},
|
||||
[selectedGroupList]
|
||||
);
|
||||
|
||||
const isGroupIndeterminate = useCallback(
|
||||
(group) => {
|
||||
const selectedGroup = selectedGroupList.find((g) => g.key === group.key);
|
||||
return (
|
||||
selectedGroup &&
|
||||
selectedGroup.selected > 0 &&
|
||||
group.total !== selectedGroup.selected
|
||||
);
|
||||
},
|
||||
[selectedGroupList]
|
||||
);
|
||||
|
||||
const getGroupSelected = useCallback(
|
||||
(group) => {
|
||||
const selectedGroup = selectedGroupList.find((g) => g.key === group.key);
|
||||
return isGroupIndeterminate(group)
|
||||
? selectedGroup.selected
|
||||
: isGroupChecked(group)
|
||||
? group.total
|
||||
: 0;
|
||||
},
|
||||
[selectedGroupList]
|
||||
);
|
||||
|
||||
const getGroupLabel = useCallback(
|
||||
(group) => {
|
||||
const selected = getGroupSelected(group);
|
||||
return isMultiSelect && allowGroupSelection
|
||||
? `${group.label} (${group.total}/${selected})`
|
||||
: group.label;
|
||||
},
|
||||
[isMultiSelect, allowGroupSelection]
|
||||
);
|
||||
|
||||
const getSelectorGroups = useCallback(
|
||||
(groups) => {
|
||||
return groups.map((group) => {
|
||||
return {
|
||||
...group,
|
||||
label: getGroupLabel(group),
|
||||
};
|
||||
});
|
||||
},
|
||||
[groups]
|
||||
);
|
||||
|
||||
const onLinkGroupClick = useCallback(
|
||||
(e) => {
|
||||
const index = e.target.dataset.index;
|
||||
if (!index) return;
|
||||
|
||||
const group = groups[index];
|
||||
|
||||
if (!group) return;
|
||||
|
||||
onGroupSelect(group);
|
||||
},
|
||||
[groups, currentGroup]
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const renderGroup = useCallback(
|
||||
({ index, style }) => {
|
||||
const group = groups[index];
|
||||
|
||||
const isChecked = isGroupChecked(group);
|
||||
const isIndeterminate = isGroupIndeterminate(group);
|
||||
const isSelected = currentGroup.key === group.key;
|
||||
const label = getGroupLabel(group);
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={group.key}
|
||||
data-index={index}
|
||||
isTextOverflow={true}
|
||||
onClick={onLinkGroupClick}
|
||||
title={label}
|
||||
style={style}
|
||||
className={`row-group${isSelected ? " selected" : ""}`}
|
||||
noHover
|
||||
>
|
||||
{isMultiSelect && allowGroupSelection && (
|
||||
<Checkbox
|
||||
id={group.key}
|
||||
value={`${index}`}
|
||||
isChecked={isChecked}
|
||||
isIndeterminate={isIndeterminate}
|
||||
className="group_checkbox"
|
||||
truncate={true}
|
||||
onChange={onGroupChange}
|
||||
/>
|
||||
)}
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
[
|
||||
groups,
|
||||
currentGroup,
|
||||
isMultiSelect,
|
||||
selectedGroupList,
|
||||
allowGroupSelection,
|
||||
]
|
||||
);
|
||||
|
||||
const hasSelected = useCallback(() => {
|
||||
return selectedOptionList.length > 0 || selectedGroupList.length > 0;
|
||||
}, [selectedOptionList, selectedGroupList]);
|
||||
@ -561,18 +383,171 @@ const Selector = (props) => {
|
||||
currentGroup: currentGroup ? currentGroup.key : null,
|
||||
};
|
||||
|
||||
//setLastIndex(startIndex);
|
||||
|
||||
//console.log("loadMoreItems", options);
|
||||
|
||||
loadNextPage && loadNextPage(options);
|
||||
},
|
||||
[isNextPageLoading, searchValue, currentGroup, options]
|
||||
);
|
||||
|
||||
const getGroupSelectedOptions = useCallback(
|
||||
(group) => {
|
||||
const selectedGroup = selectedOptionList.filter(
|
||||
(o) => o.groups && o.groups.indexOf(group) > -1
|
||||
);
|
||||
|
||||
if (group === "all") {
|
||||
selectedGroup.push(...selectedOptionList);
|
||||
}
|
||||
|
||||
return selectedGroup;
|
||||
},
|
||||
[selectedOptionList]
|
||||
);
|
||||
|
||||
const onGroupClick = useCallback(
|
||||
(index) => {
|
||||
const group = groups[index];
|
||||
|
||||
setGroupHeader({ ...group });
|
||||
|
||||
onGroupChanged && onGroupChanged(group);
|
||||
setCurrentGroup(group);
|
||||
},
|
||||
[groups, onGroupChanged]
|
||||
);
|
||||
|
||||
const renderGroup = useCallback(
|
||||
({ index, style }) => {
|
||||
const group = groups[index];
|
||||
|
||||
const selectedOption = getGroupSelectedOptions(group.id);
|
||||
|
||||
const isIndeterminate = selectedOption.length > 0;
|
||||
|
||||
let label = group.label;
|
||||
|
||||
if (isMultiSelect && selectedOption.length > 0) {
|
||||
label = `${group.label} (${selectedOption.length})`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
className="row-option"
|
||||
name={`selector-row-option-${index}`}
|
||||
onClick={() => onGroupClick(index)}
|
||||
>
|
||||
<div className="option-info">
|
||||
<Avatar
|
||||
className="option-avatar"
|
||||
role="user"
|
||||
size="min"
|
||||
source={group.avatarUrl}
|
||||
userName={group.label}
|
||||
/>
|
||||
<Text
|
||||
className="option-text option-text__group"
|
||||
truncate={true}
|
||||
noSelect={true}
|
||||
fontSize="14px"
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</div>
|
||||
{isMultiSelect && (
|
||||
<Checkbox
|
||||
value={`${index}`}
|
||||
isIndeterminate={isIndeterminate}
|
||||
className="option-checkbox"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[
|
||||
isMultiSelect,
|
||||
groups,
|
||||
currentGroup,
|
||||
selectedGroupList,
|
||||
selectedOptionList,
|
||||
getGroupSelectedOptions,
|
||||
]
|
||||
);
|
||||
|
||||
const renderGroupsList = useCallback(() => {
|
||||
if (groups.length === 0) return renderOptionLoader();
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<List
|
||||
className="options_list"
|
||||
height={height - 8}
|
||||
width={width + 8}
|
||||
itemCount={groups.length}
|
||||
itemSize={48}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{renderGroup}
|
||||
</List>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}, [isMultiSelect, groups, selectedOptionList, getGroupSelectedOptions]);
|
||||
|
||||
const renderGroupHeader = useCallback(() => {
|
||||
const selectedOption = getGroupSelectedOptions(groupHeader.id);
|
||||
|
||||
const isIndeterminate = selectedOption.length > 0;
|
||||
|
||||
let label = groupHeader.label;
|
||||
|
||||
if (isMultiSelect && selectedOption.length > 0) {
|
||||
label = `${groupHeader.label} (${selectedOption.length})`;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="row-option row-header">
|
||||
<div className="option-info">
|
||||
<Avatar
|
||||
className="option-avatar"
|
||||
role="user"
|
||||
size="min"
|
||||
source={groupHeader.avatarUrl}
|
||||
userName={groupHeader.label}
|
||||
/>
|
||||
<Text
|
||||
className="option-text option-text__header"
|
||||
truncate={true}
|
||||
noSelect={true}
|
||||
fontSize="14px"
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
</div>
|
||||
{isMultiSelect && (
|
||||
<Checkbox
|
||||
isIndeterminate={isIndeterminate}
|
||||
className="option-checkbox"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="option-separator"></div>
|
||||
</>
|
||||
);
|
||||
}, [isMultiSelect, groupHeader, selectedOptionList, getGroupSelectedOptions]);
|
||||
|
||||
const onArrowClickAction = useCallback(() => {
|
||||
if (groupHeader && groups.length !== 1) {
|
||||
setGroupHeader(null);
|
||||
|
||||
onGroupChanged && onGroupChanged([]);
|
||||
setCurrentGroup([]);
|
||||
return;
|
||||
}
|
||||
onArrowClick && onArrowClick();
|
||||
}, [groups, groupHeader, onArrowClick, onGroupChanged]);
|
||||
|
||||
return (
|
||||
<StyledSelector
|
||||
displayType={displayType}
|
||||
options={options}
|
||||
groups={groups}
|
||||
isMultiSelect={isMultiSelect}
|
||||
@ -580,7 +555,19 @@ const Selector = (props) => {
|
||||
hasSelected={hasSelected()}
|
||||
className="selector-wrapper"
|
||||
>
|
||||
<Column className="column-options" displayType={displayType} size={size}>
|
||||
<div className="header">
|
||||
<IconButton
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
size="17"
|
||||
isFill={true}
|
||||
className="arrow-button"
|
||||
onClick={onArrowClickAction}
|
||||
/>
|
||||
<Heading size="medium" truncate={true}>
|
||||
{headerLabel.replace("()", "")}
|
||||
</Heading>
|
||||
</div>
|
||||
<Column className="column-options" size={size}>
|
||||
<Header className="header-options">
|
||||
<SearchInput
|
||||
className="options_searcher"
|
||||
@ -593,70 +580,49 @@ const Selector = (props) => {
|
||||
onChange={onSearchChange}
|
||||
onClearSearch={onSearchReset}
|
||||
/>
|
||||
{displayType === "aside" && groups && groups.length > 0 && (
|
||||
<>
|
||||
<ComboBox
|
||||
className="options_group_selector"
|
||||
isDisabled={isDisabled}
|
||||
options={getSelectorGroups(groups)}
|
||||
selectedOption={currentGroup}
|
||||
dropDownMaxHeight={220}
|
||||
scaled={true}
|
||||
scaledOptions={true}
|
||||
size="content"
|
||||
isDefaultMode={false}
|
||||
onSelect={onGroupSelect}
|
||||
/>
|
||||
{isMultiSelect &&
|
||||
allowGroupSelection &&
|
||||
options &&
|
||||
options.length > 0 && (
|
||||
<Checkbox
|
||||
className="options_group_select_all"
|
||||
label={selectAllLabel}
|
||||
isChecked={selectedAll}
|
||||
isIndeterminate={false}
|
||||
truncate={true}
|
||||
onChange={onSelectAllChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Header>
|
||||
<Body className="body-options">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<InfiniteLoader
|
||||
ref={listOptionsRef}
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={loadMoreItems}
|
||||
>
|
||||
{({ onItemsRendered, ref }) => (
|
||||
<List
|
||||
className="options_list"
|
||||
height={height}
|
||||
itemCount={itemCount}
|
||||
itemSize={36}
|
||||
onItemsRendered={onItemsRendered}
|
||||
ref={ref}
|
||||
width={width + 8}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{renderOption}
|
||||
</List>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
)}
|
||||
</AutoSizer>
|
||||
|
||||
{!hasNextPage && itemCount === 0 && (
|
||||
<div className="row-option">
|
||||
<Text>
|
||||
{!searchValue ? emptyOptionsLabel : emptySearchOptionsLabel}
|
||||
</Text>
|
||||
</div>
|
||||
{!groupHeader && !searchValue && groups ? (
|
||||
renderGroupsList()
|
||||
) : (
|
||||
<>
|
||||
{!searchValue && renderGroupHeader()}
|
||||
{!hasNextPage && itemCount === 0 ? (
|
||||
<div className="row-option">
|
||||
<Text>
|
||||
{!searchValue ? emptyOptionsLabel : emptySearchOptionsLabel}
|
||||
</Text>
|
||||
</div>
|
||||
) : (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<InfiniteLoader
|
||||
ref={listOptionsRef}
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount}
|
||||
loadMoreItems={loadMoreItems}
|
||||
>
|
||||
{({ onItemsRendered, ref }) => (
|
||||
<List
|
||||
className="options_list"
|
||||
height={height - 25}
|
||||
itemCount={itemCount}
|
||||
itemSize={48}
|
||||
onItemsRendered={onItemsRendered}
|
||||
ref={ref}
|
||||
width={width + 8}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{renderOption}
|
||||
</List>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
)}
|
||||
</AutoSizer>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{getOptionTooltipContent && (
|
||||
<Tooltip
|
||||
id="user"
|
||||
@ -666,48 +632,9 @@ const Selector = (props) => {
|
||||
)}
|
||||
</Body>
|
||||
</Column>
|
||||
{displayType === "dropdown" && groups && groups.length > 0 && (
|
||||
<>
|
||||
<div className="splitter"></div>
|
||||
<Column
|
||||
className="column-groups"
|
||||
displayType={displayType}
|
||||
size={size}
|
||||
>
|
||||
<Header className="header-groups">
|
||||
<Text
|
||||
as="p"
|
||||
className="group_header"
|
||||
fontSize="15px"
|
||||
fontWeight={600}
|
||||
>
|
||||
{groupsHeaderLabel}
|
||||
</Text>
|
||||
</Header>
|
||||
<Body className="body-groups">
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
className="group_list"
|
||||
height={height}
|
||||
width={width + 8}
|
||||
itemSize={32}
|
||||
itemCount={groups.length}
|
||||
itemData={groups}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
ref={listGroupsRef}
|
||||
>
|
||||
{renderGroup}
|
||||
</List>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</Body>
|
||||
</Column>
|
||||
</>
|
||||
)}
|
||||
<Footer
|
||||
className="footer"
|
||||
selectButtonLabel={selectButtonLabel}
|
||||
selectButtonLabel={headerLabel}
|
||||
showCounter={showCounter}
|
||||
isDisabled={isDisabled}
|
||||
isVisible={isMultiSelect && hasSelected()}
|
||||
@ -740,7 +667,6 @@ Selector.propTypes = {
|
||||
loadingLabel: PropTypes.string,
|
||||
|
||||
size: PropTypes.oneOf(["compact", "full"]),
|
||||
displayType: PropTypes.oneOf(["dropdown", "aside"]),
|
||||
|
||||
selectedOptions: PropTypes.array,
|
||||
selectedGroups: PropTypes.array,
|
||||
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
const Container = ({ displayType, ...props }) => <div {...props} />;
|
||||
const Container = ({ ...props }) => <div {...props} />;
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
|
@ -3,29 +3,13 @@ import styled, { css } from "styled-components";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
const Container = ({ displayType, ...props }) => <div {...props} />;
|
||||
const Container = ({ ...props }) => <div {...props} />;
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
const StyledColumn = styled(Container)`
|
||||
${(props) =>
|
||||
props.displayType === "dropdown"
|
||||
? css`
|
||||
${(props) =>
|
||||
props.size === "compact"
|
||||
? css`
|
||||
width: 379px;
|
||||
height: 267px;
|
||||
`
|
||||
: css`
|
||||
width: 345px;
|
||||
height: 544px;
|
||||
`}
|
||||
`
|
||||
: css`
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
`}
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export default StyledColumn;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "../../../../asc-web-components/themes/base";
|
||||
|
||||
const StyledFooter = styled.div`
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid #eceef1;
|
||||
border-top: ${(props) => props.theme.advancedSelector.footerBorder};
|
||||
padding: 16px;
|
||||
height: 69px;
|
||||
|
||||
@ -19,4 +20,6 @@ const StyledFooter = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledFooter.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledFooter;
|
||||
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
const Container = ({ displayType, ...props }) => <div {...props} />;
|
||||
const Container = ({ ...props }) => <div {...props} />;
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
|
||||
import Base from "@appserver/components/themes/base";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
const Container = ({
|
||||
displayType,
|
||||
options,
|
||||
groups,
|
||||
isMultiSelect,
|
||||
@ -16,189 +16,93 @@ const Container = ({
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
const dropdownStyles = css`
|
||||
grid-auto-rows: max-content;
|
||||
const StyledSelector = styled(Container)`
|
||||
display: grid;
|
||||
|
||||
${(props) =>
|
||||
props.groups && props.groups.length > 0
|
||||
? css`
|
||||
grid-template-areas: "column-options splitter column-groups" "footer footer footer";
|
||||
`
|
||||
: css`
|
||||
grid-template-areas: "column-options column-groups" "footer footer";
|
||||
`};
|
||||
|
||||
.column-groups {
|
||||
box-sizing: border-box;
|
||||
grid-area: column-groups;
|
||||
|
||||
display: grid;
|
||||
/* background-color: gold; */
|
||||
padding: 16px 16px 0 16px;
|
||||
grid-row-gap: 2px;
|
||||
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 30px 1fr;
|
||||
grid-template-areas: "header-groups" "body-groups";
|
||||
|
||||
.header-groups {
|
||||
grid-area: header-groups;
|
||||
|
||||
.group_header {
|
||||
line-height: 30px;
|
||||
}
|
||||
/* background-color: white; */
|
||||
}
|
||||
|
||||
.body-groups {
|
||||
grid-area: body-groups;
|
||||
margin-left: -8px;
|
||||
/* background-color: white; */
|
||||
|
||||
.row-group:first-child {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.row-group {
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
cursor: pointer;
|
||||
padding-top: 8px;
|
||||
padding-left: 8px;
|
||||
|
||||
.group_checkbox {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #eceef1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.row-group.selected {
|
||||
background-color: #eceef1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.groups &&
|
||||
props.groups.length > 0 &&
|
||||
css`
|
||||
.splitter {
|
||||
grid-area: splitter;
|
||||
border-left: 1px solid #eceef1;
|
||||
margin-top: 16px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const asideStyles = css`
|
||||
height: 100%;
|
||||
grid-template-columns: 1fr;
|
||||
${(props) =>
|
||||
props.isMultiSelect && props.hasSelected
|
||||
? css`
|
||||
grid-template-rows: 1fr 69px;
|
||||
grid-template-areas: "column-options" "footer";
|
||||
grid-template-rows: 53px 1fr 69px;
|
||||
grid-template-areas: "header" "column-options" "footer";
|
||||
`
|
||||
: css`
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-areas: "column-options";
|
||||
grid-template-rows: 53px 1fr;
|
||||
grid-template-areas: "header" "column-options";
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledSelector = styled(Container)`
|
||||
display: grid;
|
||||
.header {
|
||||
grid-area: header;
|
||||
|
||||
${(props) =>
|
||||
props.displayType === "dropdown" ? dropdownStyles : asideStyles}
|
||||
height: 53px;
|
||||
min-height: 53px;
|
||||
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
|
||||
.arrow-button {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.column-options {
|
||||
grid-area: column-options;
|
||||
box-sizing: border-box;
|
||||
|
||||
display: grid;
|
||||
/* background-color: red; */
|
||||
padding: 16px 16px 0 16px;
|
||||
|
||||
padding: 0;
|
||||
grid-row-gap: 2px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: ${(props) =>
|
||||
props.displayType === "aside"
|
||||
? props.isMultiSelect &&
|
||||
props.allowGroupSelection &&
|
||||
props.options &&
|
||||
props.options.length > 0
|
||||
? props.groups && props.groups.length > 0
|
||||
? "100px"
|
||||
: "30px"
|
||||
: props.groups && props.groups.length > 0
|
||||
? "75px"
|
||||
: "30px"
|
||||
: "30px"} 1fr;
|
||||
grid-template-rows: 30px 1fr;
|
||||
grid-template-areas: "header-options" "body-options";
|
||||
|
||||
.header-options {
|
||||
grid-area: header-options;
|
||||
margin-right: 2px;
|
||||
/* background-color: white; */
|
||||
|
||||
${(props) =>
|
||||
props.displayType === "aside" &&
|
||||
css`
|
||||
display: grid;
|
||||
grid-row-gap: 17px;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 30px 30px ${(props) =>
|
||||
props.isMultiSelect &&
|
||||
props.options &&
|
||||
props.options.length > 0 &&
|
||||
"30px"};
|
||||
${(props) =>
|
||||
props.isMultiSelect && props.options && props.options.length > 0
|
||||
? css`
|
||||
grid-template-areas: "options_searcher" "options_group_selector" "options_group_select_all";
|
||||
`
|
||||
: css`
|
||||
grid-template-areas: "options_searcher" "options_group_selector";
|
||||
`}
|
||||
padding: 0 16px;
|
||||
margin-right: 0px !important;
|
||||
|
||||
.options_searcher {
|
||||
grid-area: options_searcher;
|
||||
}
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 30px;
|
||||
|
||||
.options_group_selector {
|
||||
grid-area: options_group_selector;
|
||||
}
|
||||
grid-template-areas: "options_searcher";
|
||||
|
||||
${(props) =>
|
||||
props.isMultiSelect &&
|
||||
props.options &&
|
||||
props.options.length > 0 &&
|
||||
css`
|
||||
.options_group_select_all {
|
||||
grid-area: options_group_select_all;
|
||||
}
|
||||
`}
|
||||
`}
|
||||
.options_searcher {
|
||||
grid-area: options_searcher;
|
||||
}
|
||||
|
||||
.options_searcher {
|
||||
div:first-child {
|
||||
:hover {
|
||||
border-color: #d0d5da;
|
||||
border-color: ${(props) =>
|
||||
props.theme.advancedSelector.searcher.hoverBorderColor};
|
||||
}
|
||||
|
||||
:focus,
|
||||
:focus-within {
|
||||
border-color: #2da7db;
|
||||
border-color: ${(props) =>
|
||||
props.theme.advancedSelector.searcher.focusBorderColor};
|
||||
}
|
||||
|
||||
& > input::placeholder {
|
||||
color: #a3a9ae;
|
||||
color: ${(props) =>
|
||||
props.theme.advancedSelector.searcher.placeholderColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -206,43 +110,75 @@ const StyledSelector = styled(Container)`
|
||||
|
||||
.body-options {
|
||||
grid-area: body-options;
|
||||
margin-left: -8px;
|
||||
margin-top: 2px;
|
||||
|
||||
@media ${tablet} {
|
||||
width: 290px;
|
||||
}
|
||||
|
||||
/* background-color: white; */
|
||||
margin-top: 8px;
|
||||
|
||||
.row-option {
|
||||
padding-left: 8px;
|
||||
padding-top: 8px;
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
margin-top: 16px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #eceef1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.option_checkbox {
|
||||
width: 265px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
props.theme.advancedSelector.hoverBackgroundColor};
|
||||
}
|
||||
|
||||
.option-info {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 10px;
|
||||
padding: 8px 0 8px 8px;
|
||||
margin-top: -8px;
|
||||
width: calc(100% - 32px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
/* .__react_component_tooltip {
|
||||
left: 8px !important;
|
||||
} */
|
||||
.option-avatar {
|
||||
margin-right: 12px;
|
||||
min-width: 32px;
|
||||
max-width: 32px;
|
||||
}
|
||||
|
||||
.option-text {
|
||||
max-width: 100%;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.option-text__group {
|
||||
width: auto;
|
||||
border-bottom: ${(props) =>
|
||||
props.theme.toggleContent.hoverBorderBottom};
|
||||
}
|
||||
|
||||
.option-text__header {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.option-checkbox {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
min-width: 16px;
|
||||
max-width: 16px;
|
||||
}
|
||||
}
|
||||
.option-separator {
|
||||
height: 1px;
|
||||
background: #dfe2e3;
|
||||
margin: 8px 16px;
|
||||
}
|
||||
|
||||
.row-header {
|
||||
cursor: auto;
|
||||
|
||||
.option-checkbox {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,4 +188,6 @@ const StyledSelector = styled(Container)`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSelector.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledSelector;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React from "react";
|
||||
import PageLayout from "../PageLayout";
|
||||
import Section from "../Section";
|
||||
import Loader from "@appserver/components/loader";
|
||||
|
||||
const AppLoader = () => (
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Section>
|
||||
<Section.SectionBody>
|
||||
<Loader className="pageLoader" type="rombs" size="40px" />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
</Section.SectionBody>
|
||||
</Section>
|
||||
);
|
||||
|
||||
export default AppLoader;
|
||||
|
176
packages/asc-web-common/components/Article/index.js
Normal file
176
packages/asc-web-common/components/Article/index.js
Normal file
@ -0,0 +1,176 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import PropTypes from "prop-types";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import { Resizable } from "re-resizable";
|
||||
|
||||
import {
|
||||
isDesktop as isDesktopUtils,
|
||||
isTablet as isTabletUtils,
|
||||
isMobile as isMobileUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
|
||||
import SubArticleBackdrop from "./sub-components/article-backdrop";
|
||||
import SubArticleHeader from "./sub-components/article-header";
|
||||
import SubArticleMainButton from "./sub-components/article-main-button";
|
||||
import SubArticleBody from "./sub-components/article-body";
|
||||
|
||||
import { StyledArticle } from "./styled-article";
|
||||
|
||||
const enable = {
|
||||
top: false,
|
||||
right: !isMobile,
|
||||
bottom: false,
|
||||
left: false,
|
||||
};
|
||||
|
||||
const Article = ({
|
||||
showText,
|
||||
setShowText,
|
||||
articleOpen,
|
||||
toggleShowText,
|
||||
toggleArticleOpen,
|
||||
children,
|
||||
...rest
|
||||
}) => {
|
||||
const [articleHeaderContent, setArticleHeaderContent] = React.useState(null);
|
||||
const [
|
||||
articleMainButtonContent,
|
||||
setArticleMainButtonContent,
|
||||
] = React.useState(null);
|
||||
const [articleBodyContent, setArticleBodyContent] = React.useState(null);
|
||||
|
||||
const refTimer = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isMobileOnly) {
|
||||
window.addEventListener("popstate", hideText);
|
||||
return () => window.removeEventListener("popstate", hideText);
|
||||
}
|
||||
}, [hideText]);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.addEventListener("resize", sizeChangeHandler);
|
||||
return () => window.removeEventListener("resize", sizeChangeHandler);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
sizeChangeHandler();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
React.Children.forEach(children, (child) => {
|
||||
const childType =
|
||||
child && child.type && (child.type.displayName || child.type.name);
|
||||
|
||||
switch (childType) {
|
||||
case Article.Header.displayName:
|
||||
setArticleHeaderContent(child);
|
||||
break;
|
||||
case Article.MainButton.displayName:
|
||||
setArticleMainButtonContent(child);
|
||||
break;
|
||||
case Article.Body.displayName:
|
||||
setArticleBodyContent(child);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [children]);
|
||||
|
||||
const sizeChangeHandler = React.useCallback(() => {
|
||||
clearTimeout(refTimer.current);
|
||||
|
||||
refTimer.current = setTimeout(() => {
|
||||
if (isMobileOnly || isMobileUtils() || window.innerWidth === 375)
|
||||
setShowText(true);
|
||||
if (
|
||||
((isTabletUtils() && window.innerWidth !== 375) || isMobile) &&
|
||||
!isMobileOnly
|
||||
)
|
||||
setShowText(false);
|
||||
if (isDesktopUtils() && !isMobile) setShowText(true);
|
||||
}, 100);
|
||||
}, [refTimer.current, setShowText]);
|
||||
|
||||
const hideText = React.useCallback((event) => {
|
||||
event.preventDefault;
|
||||
setShowText(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledArticle showText={showText} articleOpen={articleOpen} {...rest}>
|
||||
<Resizable
|
||||
defaultSize={{
|
||||
width: 256,
|
||||
}}
|
||||
enable={enable}
|
||||
className="resizable-block"
|
||||
handleWrapperClass="resizable-border not-selectable"
|
||||
>
|
||||
<SubArticleHeader showText={showText} onClick={toggleShowText}>
|
||||
{articleHeaderContent ? articleHeaderContent.props.children : null}
|
||||
</SubArticleHeader>
|
||||
{articleMainButtonContent ? (
|
||||
<SubArticleMainButton showText={showText}>
|
||||
{articleMainButtonContent.props.children}
|
||||
</SubArticleMainButton>
|
||||
) : null}
|
||||
<SubArticleBody showText={showText}>
|
||||
{articleBodyContent ? articleBodyContent.props.children : null}
|
||||
</SubArticleBody>
|
||||
</Resizable>
|
||||
</StyledArticle>
|
||||
{articleOpen && (isMobileOnly || window.innerWidth <= 375) && (
|
||||
<>
|
||||
<SubArticleBackdrop onClick={toggleArticleOpen} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Article.propTypes = {
|
||||
showText: PropTypes.bool,
|
||||
setShowText: PropTypes.func,
|
||||
articleOpen: PropTypes.bool,
|
||||
toggleArticleOpen: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
Article.Header = () => {
|
||||
return null;
|
||||
};
|
||||
Article.Header.displayName = "Header";
|
||||
|
||||
Article.MainButton = () => {
|
||||
return null;
|
||||
};
|
||||
Article.MainButton.displayName = "MainButton";
|
||||
|
||||
Article.Body = () => {
|
||||
return null;
|
||||
};
|
||||
Article.Body.displayName = "Body";
|
||||
|
||||
export default inject(({ auth }) => {
|
||||
const { settingsStore } = auth;
|
||||
|
||||
const {
|
||||
showText,
|
||||
setShowText,
|
||||
articleOpen,
|
||||
toggleShowText,
|
||||
toggleArticleOpen,
|
||||
} = settingsStore;
|
||||
|
||||
return {
|
||||
showText,
|
||||
setShowText,
|
||||
articleOpen,
|
||||
toggleShowText,
|
||||
toggleArticleOpen,
|
||||
};
|
||||
})(observer(Article));
|
270
packages/asc-web-common/components/Article/styled-article.js
Normal file
270
packages/asc-web-common/components/Article/styled-article.js
Normal file
@ -0,0 +1,270 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { isMobile, isMobileOnly, isTablet } from "react-device-detect";
|
||||
import {
|
||||
mobile,
|
||||
tablet,
|
||||
isMobile as isMobileUtils,
|
||||
isTablet as isTabletUtils,
|
||||
isDesktop as isDesktopUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
|
||||
import Heading from "@appserver/components/heading";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
import MenuIcon from "@appserver/components/public/static/images/menu.react.svg";
|
||||
import CrossIcon from "@appserver/components/public/static/images/cross.react.svg";
|
||||
|
||||
const StyledArticle = styled.article`
|
||||
position: relative;
|
||||
|
||||
background: ${(props) => props.theme.catalog.background};
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
margin-top: 48px;
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
position: fixed;
|
||||
margin-top: 16px;
|
||||
height: calc(100vh - 64px) !important;
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
position: fixed;
|
||||
margin-top: 64px !important;
|
||||
height: calc(100vh - 64px) !important;
|
||||
`}
|
||||
|
||||
z-index: ${(props) =>
|
||||
props.showText && (isMobileOnly || isMobileUtils()) ? "205" : "100"};
|
||||
|
||||
.resizable-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
min-width: ${(props) => (props.showText ? "256px" : "52px")};
|
||||
width: ${(props) => (props.showText ? "256px" : "52px")};
|
||||
|
||||
height: calc(100% - 44px) !important;
|
||||
|
||||
background: ${(props) => props.theme.catalog.background};
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-width: none;
|
||||
padding-bottom: 0px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.resizable-border {
|
||||
div {
|
||||
cursor: ew-resize !important;
|
||||
}
|
||||
}
|
||||
@media ${tablet} {
|
||||
min-width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
max-width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
.resizable-border {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
display: ${(props) => (props.articleOpen ? "flex" : "none")};
|
||||
min-width: 100vw;
|
||||
width: 100vw;
|
||||
height: calc(100vh - 64px) !important;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
${isTablet &&
|
||||
css`
|
||||
min-width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
max-width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
.resizable-border {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
display: ${(props) => (props.articleOpen ? "flex" : "none")};
|
||||
min-width: 100vw !important;
|
||||
width: 100vw;
|
||||
height: calc(100vh - 64px) !important;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 0px;
|
||||
`}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledArticle.defaultProps = { theme: Base };
|
||||
|
||||
const StyledArticleHeader = styled.div`
|
||||
padding: 11px 20px 14px;
|
||||
margin-left: -1px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
@media ${tablet} {
|
||||
padding: 16px 16px 17px;
|
||||
margin: 0;
|
||||
justify-content: ${(props) => (props.showText ? "flex-start" : "center")};
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
border-bottom: ${(props) => props.theme.catalog.header.borderBottom};
|
||||
padding: 12px 16px 12px;
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
|
||||
${isTablet &&
|
||||
css`
|
||||
padding: 16px 16px 17px;
|
||||
justify-content: ${(props) => (props.showText ? "flex-start" : "center")};
|
||||
margin: 0;
|
||||
`}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
border-bottom: ${(props) =>
|
||||
props.theme.catalog.header.borderBottom} !important;
|
||||
padding: 12px 16px 12px !important;
|
||||
margin-bottom: 16px !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledArticleHeader.defaultProps = { theme: Base };
|
||||
|
||||
const StyledHeading = styled(Heading)`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
line-height: 28px;
|
||||
@media ${tablet} {
|
||||
display: ${(props) => (props.showText ? "block" : "none")};
|
||||
margin-left: ${(props) => props.showText && "12px"};
|
||||
}
|
||||
|
||||
${isTablet &&
|
||||
css`
|
||||
display: ${(props) => (props.showText ? "block" : "none")};
|
||||
margin-left: ${(props) => props.showText && "12px"};
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
margin-left: 0 !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledIconBox = styled.div`
|
||||
display: none;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
|
||||
@media ${tablet} {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
display: flex !important;
|
||||
`}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
display: none !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledMenuIcon = styled(MenuIcon)`
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
path {
|
||||
fill: ${(props) => props.theme.catalog.header.iconFill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledMenuIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledArticleMainButton = styled.div`
|
||||
padding: 0px 20px 16px;
|
||||
max-width: 216px;
|
||||
@media ${tablet} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
${isTablet &&
|
||||
css`
|
||||
display: none;
|
||||
`}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
display: none;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledControlContainer = styled.div`
|
||||
background: ${(props) => props.theme.catalog.control.background};
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 10px;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 290;
|
||||
`;
|
||||
|
||||
StyledControlContainer.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCrossIcon = styled(CrossIcon)`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
path {
|
||||
fill: ${(props) => props.theme.catalog.control.fill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledCrossIcon.defaultProps = { theme: Base };
|
||||
|
||||
export {
|
||||
StyledArticle,
|
||||
StyledArticleHeader,
|
||||
StyledHeading,
|
||||
StyledIconBox,
|
||||
StyledMenuIcon,
|
||||
StyledArticleMainButton,
|
||||
StyledControlContainer,
|
||||
StyledCrossIcon,
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Backdrop from "@appserver/components/backdrop";
|
||||
|
||||
import { StyledControlContainer, StyledCrossIcon } from "../styled-article";
|
||||
|
||||
const ArticleBackdrop = ({ onClick, ...rest }) => {
|
||||
return (
|
||||
<>
|
||||
<StyledControlContainer onClick={onClick} {...rest}>
|
||||
<StyledCrossIcon />
|
||||
</StyledControlContainer>
|
||||
<Backdrop visible={true} zIndex={201} withBackground={true} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleBackdrop.propTypes = {
|
||||
showText: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(ArticleBackdrop);
|
@ -0,0 +1,9 @@
|
||||
import React from "react";
|
||||
|
||||
const ArticleBody = ({ children }) => {
|
||||
return <> {children}</>;
|
||||
};
|
||||
|
||||
ArticleBody.displayName = "Body";
|
||||
|
||||
export default React.memo(ArticleBody);
|
@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import {
|
||||
StyledArticleHeader,
|
||||
StyledHeading,
|
||||
StyledIconBox,
|
||||
StyledMenuIcon,
|
||||
} from "../styled-article";
|
||||
|
||||
const ArticleHeader = ({ showText, children, onClick, ...rest }) => {
|
||||
return (
|
||||
<StyledArticleHeader showText={showText} {...rest}>
|
||||
<StyledIconBox name="article-burger">
|
||||
<StyledMenuIcon onClick={onClick} />
|
||||
</StyledIconBox>
|
||||
|
||||
<StyledHeading showText={showText} size="large">
|
||||
{children}
|
||||
</StyledHeading>
|
||||
</StyledArticleHeader>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleHeader.propTypes = {
|
||||
children: PropTypes.any,
|
||||
showText: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
ArticleHeader.displayName = "Header";
|
||||
|
||||
export default React.memo(ArticleHeader);
|
@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
|
||||
import { StyledArticleMainButton } from "../styled-article";
|
||||
|
||||
const ArticleMainButton = (props) => {
|
||||
return <StyledArticleMainButton {...props} />;
|
||||
};
|
||||
|
||||
ArticleMainButton.displayName = "MainButton";
|
||||
|
||||
export default ArticleMainButton;
|
@ -5,10 +5,21 @@ import Headline from "../Headline";
|
||||
import Text from "@appserver/components/text";
|
||||
import Button from "@appserver/components/button";
|
||||
|
||||
import store from "studio/store";
|
||||
|
||||
const theme = store.auth.settingsStore.theme;
|
||||
|
||||
const ErrorContainer = (props) => {
|
||||
//console.log("ErrorContainer render");
|
||||
|
||||
const { headerText, bodyText, buttonText, buttonUrl, ...rest } = props;
|
||||
const {
|
||||
headerText,
|
||||
bodyText,
|
||||
buttonText,
|
||||
buttonUrl,
|
||||
children,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<StyledErrorContainer {...rest}>
|
||||
@ -335,7 +346,7 @@ const ErrorContainer = (props) => {
|
||||
<div id="button-container">
|
||||
<Button
|
||||
id="button"
|
||||
size="big"
|
||||
size="normal"
|
||||
scale
|
||||
primary
|
||||
label={buttonText}
|
||||
@ -343,6 +354,7 @@ const ErrorContainer = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</StyledErrorContainer>
|
||||
);
|
||||
};
|
||||
@ -352,6 +364,7 @@ ErrorContainer.propTypes = {
|
||||
bodyText: PropTypes.string,
|
||||
buttonText: PropTypes.string,
|
||||
buttonUrl: PropTypes.string,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
export default ErrorContainer;
|
||||
|
@ -1,9 +1,11 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledErrorContainer = styled.div`
|
||||
//background: #ffffff;
|
||||
background: ${(props) => props.theme.errorContainer.background};
|
||||
cursor: default;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
overflow-x: hidden;
|
||||
@ -1036,4 +1038,6 @@ const StyledErrorContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledErrorContainer.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledErrorContainer;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Loader from "@appserver/components/loader";
|
||||
import PageLayout from "../PageLayout";
|
||||
import Section from "../Section";
|
||||
|
||||
export class ExternalRedirect extends Component {
|
||||
constructor(props) {
|
||||
@ -15,11 +15,11 @@ export class ExternalRedirect extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<PageLayout>
|
||||
<PageLayout.SectionBody>
|
||||
<Section>
|
||||
<Section.SectionBody>
|
||||
<Loader className="pageLoader" type="rombs" size="40px" />
|
||||
</PageLayout.SectionBody>
|
||||
</PageLayout>
|
||||
</Section.SectionBody>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Base from "@appserver/components/themes/base";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
const StyledFilterInput = styled.div`
|
||||
@ -26,26 +27,41 @@ const StyledFilterInput = styled.div`
|
||||
|
||||
.styled-filter-block {
|
||||
display: flex;
|
||||
.filter-button {
|
||||
#filter-button {
|
||||
svg {
|
||||
height: 25px;
|
||||
path:not(:first-child) {
|
||||
stroke: #a3a9ae;
|
||||
|
||||
path:first-child {
|
||||
fill: ${(props) =>
|
||||
props.theme.filterInput.filterButton.fill} !important;
|
||||
stroke: ${(props) =>
|
||||
props.theme.filterInput.filterButton.stroke} !important;
|
||||
}
|
||||
|
||||
path:not(:first-child) {
|
||||
fill: ${(props) =>
|
||||
props.theme.filterInput.filterButton.fillSecond} !important;
|
||||
/* stroke: ${(props) =>
|
||||
props.theme.filterInput.filterButton.stroke} !important; */
|
||||
}
|
||||
|
||||
/* path:not(:first-child) {
|
||||
stroke: ${(props) => props.theme.filterInput.filterButton.stroke};
|
||||
} */
|
||||
}
|
||||
|
||||
stroke: #a3a9ae;
|
||||
/* stroke: ${(props) => props.theme.filterInput.filterButton.stroke};
|
||||
div:active {
|
||||
svg path:first-child {
|
||||
fill: #eceef1;
|
||||
stroke: #a3a9ae;
|
||||
fill: ${(props) => props.theme.filterInput.filterButton.fill};
|
||||
stroke: ${(props) => props.theme.filterInput.filterButton.stroke};
|
||||
}
|
||||
}
|
||||
div:first-child:hover {
|
||||
svg path:not(:first-child) {
|
||||
stroke: #a3a9ae;
|
||||
stroke: ${(props) => props.theme.filterInput.filterButton.stroke};
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +87,7 @@ const StyledFilterInput = styled.div`
|
||||
padding-left: 4px;
|
||||
}
|
||||
.combo-button-label {
|
||||
color: #333;
|
||||
color: ${(props) => props.theme.filterInput.comboButtonLabelColor};
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +136,7 @@ const StyledFilterInput = styled.div`
|
||||
`}
|
||||
|
||||
.combo-button-label {
|
||||
color: #a3a9ae;
|
||||
color: ${(props) => props.theme.filterInput.comboButtonLabelColorTwo};
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,24 +152,38 @@ const StyledFilterInput = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledFilterInput.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledViewSelector = styled.div`
|
||||
border: 1px solid ${(props) => (props.isDisabled ? "#ECEEF1" : "#D0D5DA")};
|
||||
border: 1px solid
|
||||
${(props) =>
|
||||
props.isDisabled
|
||||
? props.theme.filterInput.viewSelector.disabledBorder
|
||||
: props.theme.filterInput.viewSelector.border};
|
||||
border-radius: 3px;
|
||||
padding: 7px;
|
||||
${(props) => props.isDisabled && "background-color: #F8F9F9;"}
|
||||
${(props) =>
|
||||
props.isDisabled &&
|
||||
`background-color: ${props.theme.filterInput.viewSelector.disabledBackground}`}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #a3a9ae;
|
||||
border-color: #a3a9ae;
|
||||
background-color: ${(props) =>
|
||||
props.theme.filterInput.viewSelector.activeBackground};
|
||||
border-color: ${(props) =>
|
||||
props.theme.filterInput.viewSelector.activeBorder};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
${(props) => !props.isDisabled && "background-color: #A3A9AE;"}
|
||||
${(props) => !props.isDisabled && "border-color: #A3A9AE;"}
|
||||
${(props) =>
|
||||
!props.isDisabled &&
|
||||
`background-color: ${props.theme.filterInput.viewSelector.activeBackground};`}
|
||||
${(props) =>
|
||||
!props.isDisabled &&
|
||||
`border-color: ${props.theme.filterInput.viewSelector.activeBorder};`}
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
@ -169,49 +199,56 @@ export const StyledViewSelector = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledViewSelector.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledFilterItem = styled.div`
|
||||
display: ${(props) => (props.block ? "flex" : "inline-block")};
|
||||
margin-bottom: ${(props) => (props.block ? "8px" : "0")};
|
||||
position: relative;
|
||||
height: 25px;
|
||||
margin-right: 2px;
|
||||
border: 1px solid #eceef1;
|
||||
border: ${(props) => props.theme.filterInput.filterItem.border};
|
||||
border-radius: 3px;
|
||||
background-color: #f8f9f9;
|
||||
background-color: ${(props) =>
|
||||
props.theme.filterInput.filterItem.backgroundColor};
|
||||
padding-right: 22px;
|
||||
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
box-sizing: border-box;
|
||||
color: #555f65;
|
||||
color: ${(props) => props.theme.filterInput.filterItem.color};
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledFilterItem.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledFilterItemContent = styled.div`
|
||||
display: flex;
|
||||
padding: 4px 4px 2px 7px;
|
||||
width: max-content;
|
||||
user-select: none;
|
||||
color: #333;
|
||||
color: ${(props) => props.theme.filterInput.content.color};
|
||||
${(props) =>
|
||||
props.isOpen &&
|
||||
!props.isDisabled &&
|
||||
css`
|
||||
background: #eceef1;
|
||||
background: ${props.theme.filterInput.content.background};
|
||||
`}
|
||||
${(props) =>
|
||||
!props.isDisabled &&
|
||||
css`
|
||||
&:active {
|
||||
background: #eceef1;
|
||||
background: ${props.theme.filterInput.content.background};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledFilterItemContent.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledCloseButtonBlock = styled.div`
|
||||
display: flex;
|
||||
cursor: ${(props) =>
|
||||
@ -220,17 +257,18 @@ export const StyledCloseButtonBlock = styled.div`
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 25px;
|
||||
border-left: 1px solid #eceef1;
|
||||
border-left: ${(props) => props.theme.filterInput.closeButton.borderLeft};
|
||||
right: 0;
|
||||
top: 0;
|
||||
background-color: #f8f9f9;
|
||||
background-color: ${(props) =>
|
||||
props.theme.filterInput.closeButton.background};
|
||||
${(props) =>
|
||||
!props.isDisabled &&
|
||||
css`
|
||||
&:active {
|
||||
background: #eceef1;
|
||||
background: ${props.theme.filterInput.closeButton.activeBackground};
|
||||
svg path:first-child {
|
||||
fill: #a3a9ae;
|
||||
fill: ${props.theme.filterInput.closeButton.activeFill};
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +276,7 @@ export const StyledCloseButtonBlock = styled.div`
|
||||
.styled-close-button {
|
||||
svg {
|
||||
path {
|
||||
fill: #555f65;
|
||||
fill: ${props.theme.filterInput.closeButton.hoverFill};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -246,6 +284,8 @@ export const StyledCloseButtonBlock = styled.div`
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledCloseButtonBlock.defaultProps = { theme: Base };
|
||||
|
||||
export const Caret = styled.div`
|
||||
width: 7px;
|
||||
position: absolute;
|
||||
@ -262,9 +302,9 @@ export const StyledHideFilterButton = styled.div`
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
height: 25px;
|
||||
border: 1px solid #eceef1;
|
||||
border: ${(props) => props.theme.filterInput.hideButton.border};
|
||||
border-radius: 3px;
|
||||
background-color: #f8f9f9;
|
||||
background-color: ${(props) => props.theme.filterInput.hideButton.background};
|
||||
padding: 0 20px 0 9px;
|
||||
margin-right: 2px;
|
||||
cursor: ${(props) => (props.isDisabled ? "default" : "pointer")};
|
||||
@ -272,13 +312,21 @@ export const StyledHideFilterButton = styled.div`
|
||||
font-style: normal;
|
||||
|
||||
:hover {
|
||||
border-color: ${(props) => (props.isDisabled ? "#ECEEF1" : "#A3A9AE")};
|
||||
border-color: ${(props) =>
|
||||
props.isDisabled
|
||||
? props.theme.filterInput.hideButton.disabledHoverBorder
|
||||
: props.theme.filterInput.hideButton.hoverBorder};
|
||||
}
|
||||
:active {
|
||||
background-color: ${(props) => (props.isDisabled ? "#F8F9F9" : "#ECEEF1")};
|
||||
background-color: ${(props) =>
|
||||
props.isDisabled
|
||||
? props.theme.filterInput.hideButton.disabledActiveBackground
|
||||
: props.theme.filterInput.hideButton.activeBackground};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledHideFilterButton.defaultProps = { theme: Base };
|
||||
|
||||
export const StyledIconButton = styled.div`
|
||||
transform: ${(state) => (!state.sortDirection ? "scale(1, -1)" : "scale(1)")};
|
||||
`;
|
||||
|
@ -10,8 +10,6 @@ const CloseButton = (props) => {
|
||||
<div className={`styled-close-button ${className}`}>
|
||||
<IconButton
|
||||
className="close-button"
|
||||
color={"#A3A9AE"}
|
||||
clickColor={"#A3A9AE"}
|
||||
size={10}
|
||||
iconName="/static/images/cross.react.svg"
|
||||
isFill={true}
|
||||
|
@ -7,13 +7,17 @@ import DropDown from "@appserver/components/drop-down";
|
||||
import ExpanderDownIcon from "../../../../../public/images/expander-down.react.svg";
|
||||
import { Caret, StyledHideFilterButton } from "../StyledFilterInput";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledExpanderDownIcon = styled(ExpanderDownIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: "#A3A9AE";
|
||||
fill: ${(props) => props.theme.filterInput.hideButton.expanderFill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledExpanderDownIcon.defaultProps = { theme: Base };
|
||||
|
||||
class HideFilter extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -171,7 +171,6 @@ class SortComboBox extends React.Component {
|
||||
>
|
||||
<StyledIconButton sortDirection={!!sortDirection}>
|
||||
<IconButton
|
||||
color={"#A3A9AE"}
|
||||
iconName={selectorIcon}
|
||||
isDisabled={isDisabled}
|
||||
isFill={true}
|
||||
|
@ -28,6 +28,7 @@ const FloatingButton = ({ id, className, style, ...rest }) => {
|
||||
|
||||
return (
|
||||
<StyledCircleWrap
|
||||
color={color}
|
||||
id={id}
|
||||
className={`${className} not-selectable`}
|
||||
style={style}
|
||||
@ -42,7 +43,7 @@ const FloatingButton = ({ id, className, style, ...rest }) => {
|
||||
<div className="circle__fill"></div>
|
||||
</div>
|
||||
|
||||
<StyledFloatingButton color={color}>
|
||||
<StyledFloatingButton className="circle__background" color={color}>
|
||||
<IconBox>
|
||||
{icon == "upload" ? (
|
||||
<ButtonUploadIcon />
|
||||
|
@ -1,14 +1,14 @@
|
||||
import Base from "@appserver/components/themes/base";
|
||||
import styled, { keyframes, css } from "styled-components";
|
||||
|
||||
const backgroundColor = "none";
|
||||
const color = "#2DA7DB";
|
||||
|
||||
const StyledCircleWrap = styled.div`
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
background: ${backgroundColor};
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: ${(props) =>
|
||||
props.color ? props.color : props.theme.floatingButton.backgroundColor};
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
box-shadow: ${(props) => props.theme.floatingButton.boxShadow};
|
||||
`;
|
||||
|
||||
const rotate360 = keyframes`
|
||||
@ -20,20 +20,31 @@ const rotate360 = keyframes`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledCircleWrap.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCircle = styled.div`
|
||||
.circle__mask,
|
||||
.circle__fill {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.percent > 0
|
||||
? css`
|
||||
.circle__mask {
|
||||
clip: rect(0px, 54px, 54px, 27px);
|
||||
clip: rect(0px, 42px, 42px, 21px);
|
||||
}
|
||||
|
||||
.circle__fill {
|
||||
@ -49,8 +60,8 @@ const StyledCircle = styled.div`
|
||||
`}
|
||||
|
||||
.circle__mask .circle__fill {
|
||||
clip: rect(0px, 27px, 54px, 0px);
|
||||
background-color: ${color};
|
||||
clip: rect(0px, 21px, 42px, 0px);
|
||||
background-color: ${(props) => props.theme.floatingButton.color};
|
||||
}
|
||||
|
||||
.circle__mask.circle__full {
|
||||
@ -69,26 +80,36 @@ const StyledCircle = styled.div`
|
||||
`;
|
||||
|
||||
const StyledFloatingButton = styled.div`
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: ${(props) => (props.color ? props.color : "#fff")};
|
||||
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.13);
|
||||
background: ${(props) =>
|
||||
props.color ? props.color : props.theme.floatingButton.backgroundColor};
|
||||
text-align: center;
|
||||
margin: 3px;
|
||||
margin: 5px;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
StyledFloatingButton.defaultProps = { theme: Base };
|
||||
|
||||
const IconBox = styled.div`
|
||||
padding-top: 12px;
|
||||
// padding-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
IconBox.defaultProps = { theme: Base };
|
||||
|
||||
const StyledAlertIcon = styled.div`
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
left: 26px;
|
||||
top: -10px;
|
||||
left: 19px;
|
||||
top: 0px;
|
||||
`;
|
||||
|
||||
export {
|
||||
|
@ -3,7 +3,7 @@ import PropTypes from "prop-types";
|
||||
import StyledContainer from "./StyledMainButton";
|
||||
import RectangleLoader from "../RectangleLoader";
|
||||
|
||||
const MainButtonLoader = ({ id, className, style, ...rest }) => {
|
||||
const ArticleButtonLoader = ({ id, className, style, ...rest }) => {
|
||||
const {
|
||||
title,
|
||||
width,
|
||||
@ -34,16 +34,16 @@ const MainButtonLoader = ({ id, className, style, ...rest }) => {
|
||||
);
|
||||
};
|
||||
|
||||
MainButtonLoader.propTypes = {
|
||||
ArticleButtonLoader.propTypes = {
|
||||
id: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
MainButtonLoader.defaultProps = {
|
||||
ArticleButtonLoader.defaultProps = {
|
||||
id: undefined,
|
||||
className: undefined,
|
||||
style: undefined,
|
||||
};
|
||||
|
||||
export default MainButtonLoader;
|
||||
export default ArticleButtonLoader;
|
@ -9,7 +9,7 @@ import Loaders from "@appserver/common/components/Loaders";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<Loaders.MainButton />
|
||||
<Loaders.ArticleButton />
|
||||
```
|
||||
|
||||
### Properties
|
@ -0,0 +1,16 @@
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
import { isTablet } from "react-device-detect";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 216px;
|
||||
margin: -9px 0 0;
|
||||
|
||||
@media ${tablet} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
${isTablet && "display: none"}
|
||||
`;
|
||||
|
||||
export default StyledContainer;
|
@ -0,0 +1 @@
|
||||
export default from "./ArticleButtonLoader";
|
@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
StyledContainer,
|
||||
StyledBlock,
|
||||
StyledRectangleLoader,
|
||||
} from "./StyledArticleFolderLoader";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const ArticleFolderLoader = ({ id, className, style, showText, ...rest }) => {
|
||||
return (
|
||||
<StyledContainer
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
showText={showText}
|
||||
>
|
||||
<StyledBlock>
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
</StyledBlock>
|
||||
<StyledBlock>
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
</StyledBlock>
|
||||
<StyledBlock>
|
||||
<StyledRectangleLoader {...rest} />
|
||||
</StyledBlock>
|
||||
<StyledBlock>
|
||||
<StyledRectangleLoader {...rest} />
|
||||
</StyledBlock>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleFolderLoader.propTypes = {
|
||||
id: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
showText: PropTypes.bool,
|
||||
};
|
||||
|
||||
ArticleFolderLoader.defaultProps = {
|
||||
id: undefined,
|
||||
className: undefined,
|
||||
style: undefined,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => ({
|
||||
showText: auth.settingsStore.showText,
|
||||
}))(observer(ArticleFolderLoader));
|
@ -0,0 +1,47 @@
|
||||
import styled from "styled-components";
|
||||
import RectangleLoader from "../RectangleLoader";
|
||||
import { tablet, mobile } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
margin: 0;
|
||||
|
||||
max-width: 216px;
|
||||
padding: 0 20px;
|
||||
|
||||
@media ${tablet} {
|
||||
width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
padding: ${(props) => (props.showText ? "0 16px" : "10px 16px")};
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledBlock = styled.div`
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media ${tablet} {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRectangleLoader = styled(RectangleLoader)`
|
||||
height: 20px;
|
||||
width: 216px;
|
||||
padding: 0 0 16px;
|
||||
|
||||
@media ${tablet} {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 0 0 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledBlock, StyledContainer, StyledRectangleLoader };
|
@ -0,0 +1 @@
|
||||
export default from "./ArticleFolderLoader";
|
@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
StyledContainer,
|
||||
StyledRectangleLoader,
|
||||
} from "./StyledArticleGroupsLoader";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
const ArticleGroupsLoader = ({ id, className, style, showText, ...rest }) => {
|
||||
return (
|
||||
<StyledContainer
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
showText={showText}
|
||||
>
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
<StyledRectangleLoader {...rest} />
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
ArticleGroupsLoader.propTypes = {
|
||||
id: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
showText: PropTypes.bool,
|
||||
};
|
||||
|
||||
ArticleGroupsLoader.defaultProps = {
|
||||
id: undefined,
|
||||
className: undefined,
|
||||
style: undefined,
|
||||
};
|
||||
|
||||
export default inject(({ auth }) => ({
|
||||
showText: auth.settingsStore.showText,
|
||||
}))(observer(ArticleGroupsLoader));
|
@ -0,0 +1,36 @@
|
||||
import styled from "styled-components";
|
||||
import RectangleLoader from "../RectangleLoader";
|
||||
import { tablet, mobile } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
margin: 0;
|
||||
|
||||
max-width: 216px;
|
||||
padding: 0 20px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media ${tablet} {
|
||||
width: ${(props) => (props.showText ? "240px" : "52px")};
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledRectangleLoader = styled(RectangleLoader)`
|
||||
height: 20px;
|
||||
width: 216px;
|
||||
padding: 0 0 16px;
|
||||
|
||||
@media ${tablet} {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 0 0 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledContainer, StyledRectangleLoader };
|
@ -0,0 +1 @@
|
||||
export default from "./ArticleGroupsLoader";
|
@ -1,8 +1,17 @@
|
||||
import { isMobile } from "react-device-detect";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
const StyledContainer = styled.div`
|
||||
padding-top: 13px;
|
||||
padding-bottom: 10px;
|
||||
max-width: 216px;
|
||||
margin-left: 1px;
|
||||
|
||||
@media ${tablet} {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
${isMobile} {
|
||||
margin-left: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledContainer;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledDialogLoader = styled.div`
|
||||
.dialog-loader-header {
|
||||
border-bottom: 1px solid rgb(222, 226, 230);
|
||||
border-bottom: ${(props) => props.theme.dialogLoader.borderBottom};
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
}
|
||||
@ -24,4 +25,6 @@ const StyledDialogLoader = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledDialogLoader.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledDialogLoader;
|
||||
|
@ -37,10 +37,10 @@ const HeaderLoader = ({ id, className, style, ...rest }) => {
|
||||
radius="18"
|
||||
width="36"
|
||||
height="36"
|
||||
backgroundColor="#fff"
|
||||
foregroundColor="#fff"
|
||||
backgroundOpacity={0.25}
|
||||
foregroundOpacity={0.2}
|
||||
backgroundColor={backgroundColor}
|
||||
foregroundColor={foregroundColor}
|
||||
backgroundOpacity={backgroundOpacity}
|
||||
foregroundOpacity={foregroundOpacity}
|
||||
/>
|
||||
</StyledHeader>
|
||||
);
|
||||
|
@ -1,12 +0,0 @@
|
||||
import styled from "styled-components";
|
||||
import { desktop } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 209px;
|
||||
|
||||
@media ${desktop} {
|
||||
width: 225px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledContainer;
|
@ -1 +0,0 @@
|
||||
export default from "./MainButtonLoader";
|
@ -1,5 +1,6 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { smallTablet, tablet, size } from "@appserver/components/utils/device";
|
||||
import Base from "@appserver/components/themes/base";
|
||||
|
||||
const StyledTile = styled.div`
|
||||
position: relative;
|
||||
@ -28,6 +29,8 @@ const StyledTile = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTile.defaultProps = { theme: Base };
|
||||
|
||||
const StyledMainContent = styled.div`
|
||||
height: 172px;
|
||||
`;
|
||||
|
@ -3,6 +3,9 @@ import Circle from "./CircleLoader";
|
||||
import Header from "./HeaderLoader";
|
||||
import SectionHeader from "./SectionHeaderLoader";
|
||||
import ArticleHeader from "./ArticleHeaderLoader";
|
||||
import ArticleButton from "./ArticleButtonLoader";
|
||||
import ArticleFolder from "./ArticleFolderLoader";
|
||||
import ArticleGroup from "./ArticleGroupsLoader";
|
||||
import TreeFolders from "./TreeFolderLoader";
|
||||
import TreeSettingsLoader from "./TreeSettingsLoader";
|
||||
import Row from "./RowLoader";
|
||||
@ -17,7 +20,6 @@ import Tile from "./TileLoader";
|
||||
import Tiles from "./TilesLoader";
|
||||
import DialogLoader from "./DialogLoader";
|
||||
import DialogAsideLoader from "./DialogAsideLoader";
|
||||
import MainButton from "./MainButtonLoader";
|
||||
|
||||
export default {
|
||||
Rectangle,
|
||||
@ -39,5 +41,7 @@ export default {
|
||||
Tiles,
|
||||
DialogLoader,
|
||||
DialogAsideLoader,
|
||||
MainButton,
|
||||
ArticleButton,
|
||||
ArticleFolder,
|
||||
ArticleGroup,
|
||||
};
|
||||
|
@ -513,7 +513,7 @@ class MediaViewer extends React.Component {
|
||||
|
||||
<div>
|
||||
<div className="details" ref={this.detailsContainer}>
|
||||
<Text isBold fontSize="14px" color="#fff" className="title">
|
||||
<Text isBold fontSize="14px" className="title">
|
||||
{title}
|
||||
</Text>
|
||||
<ControlBtn
|
||||
@ -521,7 +521,6 @@ class MediaViewer extends React.Component {
|
||||
className="mediaPlayerClose"
|
||||
>
|
||||
<IconButton
|
||||
color="#fff"
|
||||
iconName="/static/images/cross.react.svg"
|
||||
size={25}
|
||||
isClickable
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledMediaViewer = styled.div`
|
||||
color: #d1d1d1;
|
||||
color: ${(props) => props.theme.mediaViewer.color};
|
||||
display: ${(props) => (props.visible ? "block" : "none")};
|
||||
overflow: hidden;
|
||||
.videoViewerOverlay {
|
||||
@ -20,7 +21,7 @@ const StyledMediaViewer = styled.div`
|
||||
padding-bottom: 14px;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
background-color: rgba(11, 11, 11, 0.7);
|
||||
background-color: ${(props) => props.theme.mediaViewer.backgroundColor};
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@ -43,7 +44,7 @@ const StyledMediaViewer = styled.div`
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.fill};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,7 +54,7 @@ const StyledMediaViewer = styled.div`
|
||||
padding-bottom: 14px;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
background: rgba(17, 17, 17, 0.867);
|
||||
background: ${(props) => props.theme.mediaViewer.background};
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -66,6 +67,7 @@ const StyledMediaViewer = styled.div`
|
||||
width: calc(100% - 50px);
|
||||
padding-left: 16px;
|
||||
box-sizing: border-box;
|
||||
color: ${(props) => props.theme.mediaViewer.titleColor};
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +77,15 @@ const StyledMediaViewer = styled.div`
|
||||
right: 10px;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.mediaViewer.iconColor};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledMediaViewer.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledMediaViewer;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledVideoControlBtn = styled.div`
|
||||
display: inline-block;
|
||||
@ -13,9 +14,13 @@ const StyledVideoControlBtn = styled.div`
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(200, 200, 200, 0.2);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.controlBtn.backgroundColor};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledVideoControlBtn.defaultProps = { theme: Base };
|
||||
|
||||
const ControlBtn = (props) => {
|
||||
return (
|
||||
<StyledVideoControlBtn {...props}>{props.children}</StyledVideoControlBtn>
|
||||
|
@ -14,6 +14,7 @@ import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import MediaScrollButton from "./scroll-button";
|
||||
import ControlBtn from "./control-btn";
|
||||
import equal from "fast-deep-equal/react";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledMediaZoomInIcon = styled(MediaZoomInIcon)`
|
||||
${commonIconsStyles}
|
||||
@ -69,7 +70,8 @@ const StyledViewer = styled(Viewer)`
|
||||
.react-viewer-btn {
|
||||
background-color: transparent;
|
||||
&:hover {
|
||||
background-color: rgba(200, 200, 200, 0.2);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.imageViewer.backgroundColor};
|
||||
}
|
||||
}
|
||||
li[data-key="prev"] {
|
||||
@ -119,7 +121,7 @@ const StyledViewer = styled(Viewer)`
|
||||
|
||||
path,
|
||||
rect {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.imageViewer.fill};
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +134,7 @@ const StyledViewer = styled(Viewer)`
|
||||
|
||||
path,
|
||||
rect {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.imageViewer.fill};
|
||||
}
|
||||
}
|
||||
.scrollBtn {
|
||||
@ -140,11 +142,15 @@ const StyledViewer = styled(Viewer)`
|
||||
opacity: ${(props) => (props.inactive ? "0.2" : "1")};
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
!props.inactive ? "rgba(200, 200, 200, 0.2)" : "rgba(11,11,11,0.7)"};
|
||||
!props.inactive
|
||||
? props.theme.mediaViewer.imageViewer.backgroundColor
|
||||
: props.theme.mediaViewer.imageViewer.inactiveBackgroundColor};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledViewer.defaultProps = { theme: Base };
|
||||
|
||||
var customToolbar = [
|
||||
{
|
||||
key: "zoomIn",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledProgress = styled.div`
|
||||
display: inline-block;
|
||||
@ -11,7 +12,8 @@ const StyledProgress = styled.div`
|
||||
position: relative;
|
||||
width: ${(props) => props.width}px;
|
||||
height: 6px;
|
||||
background: rgba(200, 200, 200, 0.2);
|
||||
background: ${(props) =>
|
||||
props.theme.mediaViewer.progressBar.backgroundColor};
|
||||
margin: 15px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -22,7 +24,7 @@ const StyledProgress = styled.div`
|
||||
|
||||
top: calc(50% - 3px);
|
||||
height: 6px;
|
||||
background: #d1d1d1;
|
||||
background: ${(props) => props.theme.mediaViewer.progressBar.background};
|
||||
border-radius: 2px;
|
||||
}
|
||||
input[type="range"] {
|
||||
@ -104,6 +106,8 @@ const StyledProgress = styled.div`
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledProgress.defaultProps = { theme: Base };
|
||||
const Progress = (props) => {
|
||||
return (
|
||||
<StyledProgress {...props}>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
@ -14,12 +15,13 @@ const ScrollButton = styled.div`
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: rgba(11, 11, 11, 0.7);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.scrollButton.backgroundColor};
|
||||
border-radius: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) =>
|
||||
!props.inactive && "rgba(200, 200, 200, 0.2)"};
|
||||
!props.inactive && props.theme.mediaViewer.scrollButton.background};
|
||||
}
|
||||
|
||||
&:before {
|
||||
@ -27,7 +29,7 @@ const ScrollButton = styled.div`
|
||||
top: 12px;
|
||||
left: ${(props) => (props.orientation == "left" ? "9px;" : "15px;")};
|
||||
position: absolute;
|
||||
border: solid #fff;
|
||||
border: ${(props) => props.theme.mediaViewer.scrollButton.border};
|
||||
border-width: 0 2px 2px 0;
|
||||
display: inline-block;
|
||||
padding: 7px;
|
||||
@ -38,6 +40,8 @@ const ScrollButton = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
ScrollButton.defaultProps = { theme: Base };
|
||||
|
||||
const MediaScrollButton = (props) => {
|
||||
return <ScrollButton {...props} />;
|
||||
};
|
||||
|
@ -13,12 +13,13 @@ import MediaFullScreenIcon from "../../../../../public/images/media.fullscreen.v
|
||||
import MediaMuteIcon from "../../../../../public/images/media.mute.react.svg";
|
||||
import MediaMuteOffIcon from "../../../../../public/images/media.muteoff.react.svg";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const iconsStyles = css`
|
||||
path,
|
||||
stroke,
|
||||
rect {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.videoViewer.fill};
|
||||
}
|
||||
`;
|
||||
|
||||
@ -28,10 +29,14 @@ const StyledControls = styled.div`
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 301;
|
||||
${(props) => !props.isVideo && "background-color: rgba(11,11,11,0.7);"}
|
||||
${(props) =>
|
||||
!props.isVideo &&
|
||||
`background-color: ${props.theme.mediaViewer.videoViewer.backgroundColor};`}
|
||||
top: calc(50% + ${(props) => props.top}px);
|
||||
left: ${(props) => props.left}px;
|
||||
`;
|
||||
|
||||
StyledControls.defaultProps = { theme: Base };
|
||||
const StyledVideoControlBtn = styled.div`
|
||||
display: inline-block;
|
||||
height: 26px;
|
||||
@ -43,7 +48,8 @@ const StyledVideoControlBtn = styled.div`
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
&:hover {
|
||||
background-color: rgba(200, 200, 200, 0.2);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.videoViewer.background};
|
||||
}
|
||||
|
||||
.playBtnContainer {
|
||||
@ -74,36 +80,43 @@ const StyledVideoControlBtn = styled.div`
|
||||
line-height: 19px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledVideoControlBtn.defaultProps = { theme: Base };
|
||||
const StyledMediaPauseIcon = styled(MediaPauseIcon)`
|
||||
${commonIconsStyles}
|
||||
${iconsStyles}
|
||||
`;
|
||||
StyledMediaPauseIcon.defaultProps = { theme: Base };
|
||||
const StyledMediaPlayIcon = styled(MediaPlayIcon)`
|
||||
${commonIconsStyles}
|
||||
${iconsStyles}
|
||||
`;
|
||||
StyledMediaPlayIcon.defaultProps = { theme: Base };
|
||||
const StyledMediaFullScreenIcon = styled(MediaFullScreenIcon)`
|
||||
${commonIconsStyles}
|
||||
${iconsStyles}
|
||||
`;
|
||||
StyledMediaFullScreenIcon.defaultProps = { theme: Base };
|
||||
const StyledMediaMuteIcon = styled(MediaMuteIcon)`
|
||||
${commonIconsStyles}
|
||||
|
||||
path:first-child {
|
||||
stroke: #fff;
|
||||
stroke: ${(props) => props.theme.mediaViewer.videoViewer.stroke};
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.videoViewer.fill};
|
||||
}
|
||||
`;
|
||||
const StyledMediaMuteOffIcon = styled(MediaMuteOffIcon)`
|
||||
${commonIconsStyles}
|
||||
|
||||
path, rect {
|
||||
fill: #fff;
|
||||
fill: ${(props) => props.theme.mediaViewer.videoViewer.fill};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledMediaMuteIcon.defaultProps = { theme: Base };
|
||||
const VideoControlBtn = (props) => {
|
||||
return (
|
||||
<StyledVideoControlBtn {...props}>{props.children}</StyledVideoControlBtn>
|
||||
@ -189,11 +202,14 @@ const StyledDuration = styled.div`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(200, 200, 200, 0.2);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.videoViewer.background};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledValumeContainer.defaultProps = { theme: Base };
|
||||
const StyledVideoViewer = styled.div`
|
||||
color: #d1d1d1;
|
||||
color: ${(props) => props.theme.mediaViewer.videoViewer.color};
|
||||
|
||||
.playerWrapper {
|
||||
display: ${(props) => (props.isVideo ? "block" : "none")};
|
||||
@ -204,7 +220,8 @@ const StyledVideoViewer = styled.div`
|
||||
z-index: 301;
|
||||
position: fixed;
|
||||
padding-bottom: 40px;
|
||||
background-color: rgba(11, 11, 11, 0.7);
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.videoViewer.backgroundColor};
|
||||
|
||||
video {
|
||||
z-index: 300;
|
||||
@ -212,19 +229,24 @@ const StyledVideoViewer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
StyledVideoViewer.defaultProps = { theme: Base };
|
||||
|
||||
const ErrorContainer = styled.div`
|
||||
z-index: 301;
|
||||
display: block;
|
||||
position: fixed;
|
||||
left: calc(50% - 110px);
|
||||
top: calc(50% - 40px);
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
background-color: ${(props) =>
|
||||
props.theme.mediaViewer.videoViewer.backgroundColorError};
|
||||
color: ${(props) => props.theme.mediaViewer.videoViewer.colorError};
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
ErrorContainer.defaultProps = { theme: Base };
|
||||
|
||||
class ValumeBtn extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
160
packages/asc-web-common/components/Navigation/Navigation.js
Normal file
160
packages/asc-web-common/components/Navigation/Navigation.js
Normal file
@ -0,0 +1,160 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import Loaders from "@appserver/common/components/Loaders";
|
||||
|
||||
import StyledContainer from "./StyledNavigation";
|
||||
import ArrowButton from "./sub-components/arrow-btn";
|
||||
import Text from "./sub-components/text";
|
||||
import ControlButtons from "./sub-components/control-btn";
|
||||
import DropBox from "./sub-components/drop-box";
|
||||
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
|
||||
import DomHelpers from "@appserver/components/utils/domHelpers";
|
||||
|
||||
const Navigation = ({
|
||||
tReady,
|
||||
showText,
|
||||
isRootFolder,
|
||||
title,
|
||||
canCreate,
|
||||
isDesktop,
|
||||
isTabletView,
|
||||
personal,
|
||||
onClickFolder,
|
||||
navigationItems,
|
||||
getContextOptionsPlus,
|
||||
getContextOptionsFolder,
|
||||
onBackToParentFolder,
|
||||
isRecycleBinFolder,
|
||||
isEmptyFilesList,
|
||||
clearTrash,
|
||||
...rest
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [firstClick, setFirstClick] = React.useState(true);
|
||||
const [dropBoxWidth, setDropBoxWidth] = React.useState(0);
|
||||
|
||||
const dropBoxRef = React.useRef(null);
|
||||
const containerRef = React.useRef(null);
|
||||
|
||||
const onMissClick = (e) => {
|
||||
e.preventDefault;
|
||||
const path = e.path || (e.composedPath && e.composedPath());
|
||||
|
||||
if (!firstClick) {
|
||||
!path.includes(dropBoxRef.current) ? toggleDropBox() : null;
|
||||
} else {
|
||||
setFirstClick((prev) => !prev);
|
||||
}
|
||||
};
|
||||
|
||||
const onClickAvailable = React.useCallback(
|
||||
(id) => {
|
||||
onClickFolder && onClickFolder(id);
|
||||
toggleDropBox();
|
||||
},
|
||||
[onClickFolder, toggleDropBox]
|
||||
);
|
||||
|
||||
const toggleDropBox = () => {
|
||||
if (isRootFolder) return setIsOpen(false);
|
||||
setDropBoxWidth(DomHelpers.getOuterWidth(containerRef.current));
|
||||
setIsOpen((prev) => !prev);
|
||||
setFirstClick(true);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isOpen) {
|
||||
window.addEventListener("click", onMissClick);
|
||||
} else {
|
||||
window.removeEventListener("click", onMissClick);
|
||||
setFirstClick(true);
|
||||
}
|
||||
|
||||
return () => window.removeEventListener("click", onMissClick);
|
||||
}, [isOpen, onMissClick]);
|
||||
|
||||
const onBackToParentFolderAction = React.useCallback(() => {
|
||||
setIsOpen((val) => !val);
|
||||
onBackToParentFolder && onBackToParentFolder();
|
||||
}, [onBackToParentFolder]);
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) => (
|
||||
<>
|
||||
{isOpen && (
|
||||
<DropBox
|
||||
{...rest}
|
||||
ref={dropBoxRef}
|
||||
dropBoxWidth={dropBoxWidth}
|
||||
sectionHeight={context.sectionHeight}
|
||||
showText={showText}
|
||||
isRootFolder={isRootFolder}
|
||||
onBackToParentFolder={onBackToParentFolderAction}
|
||||
title={title}
|
||||
personal={personal}
|
||||
isRootFolder={isRootFolder}
|
||||
canCreate={canCreate}
|
||||
navigationItems={navigationItems}
|
||||
getContextOptionsFolder={getContextOptionsFolder}
|
||||
getContextOptionsPlus={getContextOptionsPlus}
|
||||
toggleDropBox={toggleDropBox}
|
||||
onClickAvailable={onClickAvailable}
|
||||
/>
|
||||
)}
|
||||
<StyledContainer
|
||||
ref={containerRef}
|
||||
width={context.sectionWidth}
|
||||
isRootFolder={isRootFolder}
|
||||
canCreate={canCreate}
|
||||
title={title}
|
||||
isDesktop={isDesktop}
|
||||
isTabletView={isTabletView}
|
||||
isRecycleBinFolder={isRecycleBinFolder}
|
||||
>
|
||||
<ArrowButton
|
||||
isRootFolder={isRootFolder}
|
||||
onBackToParentFolder={onBackToParentFolder}
|
||||
/>
|
||||
<Text
|
||||
title={title}
|
||||
isOpen={false}
|
||||
isRootFolder={isRootFolder}
|
||||
onClick={toggleDropBox}
|
||||
/>
|
||||
<ControlButtons
|
||||
personal={personal}
|
||||
isRootFolder={isRootFolder}
|
||||
canCreate={canCreate}
|
||||
getContextOptionsFolder={getContextOptionsFolder}
|
||||
getContextOptionsPlus={getContextOptionsPlus}
|
||||
isRecycleBinFolder={isRecycleBinFolder}
|
||||
isEmptyFilesList={isEmptyFilesList}
|
||||
clearTrash={clearTrash}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</>
|
||||
)}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
Navigation.propTypes = {
|
||||
tReady: PropTypes.bool,
|
||||
isRootFolder: PropTypes.bool,
|
||||
title: PropTypes.string,
|
||||
canCreate: PropTypes.bool,
|
||||
isDesktop: PropTypes.bool,
|
||||
isTabletView: PropTypes.bool,
|
||||
personal: PropTypes.bool,
|
||||
onClickFolder: PropTypes.func,
|
||||
navigationItems: PropTypes.arrayOf(PropTypes.object),
|
||||
getContextOptionsPlus: PropTypes.func,
|
||||
getContextOptionsFolder: PropTypes.func,
|
||||
onBackToParentFolder: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(Navigation);
|
@ -0,0 +1,47 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import { tablet, desktop, mobile } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
padding: ${(props) => (props.isDropBox ? "14px 0 3px" : "14px 0 0px")};
|
||||
|
||||
width: fit-content;
|
||||
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: ${(props) =>
|
||||
props.isRootFolder ? "1fr auto" : "29px 1fr auto"};
|
||||
|
||||
align-items: center;
|
||||
|
||||
.arrow-button {
|
||||
width: 17px;
|
||||
min-width: 17px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
padding: ${(props) => (props.isDropBox ? "16px 0 5px" : "16px 0 0px")};
|
||||
}
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
width: 100% !important;
|
||||
padding: ${(props) =>
|
||||
props.isDropBox ? "16px 0 5px" : " 16px 0 0px"} !important;
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
width: 100%;
|
||||
padding: ${(props) => (props.isDropBox ? "12px 0 5px" : "12px 0 0")};
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
width: 100% !important;
|
||||
padding: ${(props) =>
|
||||
props.isDropBox ? "12px 0 5px" : "12px 0 0"} !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default StyledContainer;
|
1
packages/asc-web-common/components/Navigation/index.js
Normal file
1
packages/asc-web-common/components/Navigation/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default from "./Navigation";
|
@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
|
||||
const ArrowButton = ({ isRootFolder, onBackToParentFolder }) => {
|
||||
return (
|
||||
<>
|
||||
{!isRootFolder ? (
|
||||
<IconButton
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
size="17"
|
||||
isFill={true}
|
||||
onClick={onBackToParentFolder}
|
||||
className="arrow-button"
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ArrowButton);
|
@ -0,0 +1,111 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import ContextMenuButton from "@appserver/components/context-menu-button";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.add-button {
|
||||
margin-right: 12px;
|
||||
min-width: 17px;
|
||||
|
||||
${(props) =>
|
||||
!props.isDropBox &&
|
||||
css`
|
||||
@media ${tablet} {
|
||||
display: none;
|
||||
}
|
||||
`}
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
${(props) => !props.isDropBox && "display: none"};
|
||||
`}
|
||||
}
|
||||
|
||||
.option-button {
|
||||
margin-right: 8px;
|
||||
min-width: 17px;
|
||||
}
|
||||
|
||||
.trash-button {
|
||||
min-width: 17px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ControlButtons = ({
|
||||
personal,
|
||||
isDropBox,
|
||||
isRootFolder,
|
||||
canCreate,
|
||||
getContextOptionsFolder,
|
||||
getContextOptionsPlus,
|
||||
isRecycleBinFolder,
|
||||
isEmptyFilesList,
|
||||
clearTrash,
|
||||
}) => {
|
||||
return (
|
||||
<StyledContainer isDropBox={isDropBox}>
|
||||
{!isRootFolder && canCreate ? (
|
||||
<>
|
||||
<ContextMenuButton
|
||||
className="add-button"
|
||||
directionX="right"
|
||||
iconName="images/plus.svg"
|
||||
size={17}
|
||||
isFill
|
||||
getData={getContextOptionsPlus}
|
||||
isDisabled={false}
|
||||
/>
|
||||
{!personal && (
|
||||
<ContextMenuButton
|
||||
className="option-button"
|
||||
directionX="right"
|
||||
iconName="images/vertical-dots.react.svg"
|
||||
size={17}
|
||||
isFill
|
||||
getData={getContextOptionsFolder}
|
||||
isDisabled={false}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : canCreate ? (
|
||||
<ContextMenuButton
|
||||
className="add-button"
|
||||
directionX="right"
|
||||
iconName="images/plus.svg"
|
||||
size={17}
|
||||
isFill
|
||||
getData={getContextOptionsPlus}
|
||||
isDisabled={false}
|
||||
/>
|
||||
) : isRecycleBinFolder && !isEmptyFilesList ? (
|
||||
<IconButton
|
||||
iconName="images/clear.active.react.svg"
|
||||
size={17}
|
||||
isFill={true}
|
||||
onClick={clearTrash}
|
||||
className="trash-button"
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
ControlButtons.propTypes = {
|
||||
personal: PropTypes.bool,
|
||||
isRootFolder: PropTypes.bool,
|
||||
canCreate: PropTypes.bool,
|
||||
getContextOptionsFolder: PropTypes.func,
|
||||
getContextOptionsPlus: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(ControlButtons);
|
@ -0,0 +1,171 @@
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { VariableSizeList } from "react-window";
|
||||
import CustomScrollbarsVirtualList from "@appserver/components/scrollbar/custom-scrollbars-virtual-list";
|
||||
|
||||
import ArrowButton from "./arrow-btn";
|
||||
import Text from "./text";
|
||||
import ControlButtons from "./control-btn";
|
||||
import Item from "./item";
|
||||
import StyledContainer from "../StyledNavigation";
|
||||
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import {
|
||||
tablet,
|
||||
mobile,
|
||||
isMobile as isMobileUtils,
|
||||
isTablet as isTabletUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledBox = styled.div`
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: ${isMobile ? "-16px" : "-20px"};
|
||||
|
||||
padding: ${isMobile ? "0 16px" : "0 20px"};
|
||||
|
||||
width: ${(props) => props.dropBoxWidth}px;
|
||||
|
||||
height: ${(props) => (props.height ? `${props.height}px` : "fit-content")};
|
||||
max-height: calc(100vh - 48px);
|
||||
|
||||
z-index: 399;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background: ${(props) => props.theme.navigation.background};
|
||||
|
||||
filter: drop-shadow(0px 12px 40px rgba(4, 15, 27, 0.12));
|
||||
border-radius: 0px 0px 6px 6px;
|
||||
|
||||
@media ${tablet} {
|
||||
left: -16px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledBox.defaultProps = { theme: Base };
|
||||
|
||||
const Row = React.memo(({ data, index, style }) => {
|
||||
const isRoot = index === data[0].length - 1;
|
||||
return (
|
||||
<Item
|
||||
key={data[0][index].id}
|
||||
id={data[0][index].id}
|
||||
title={data[0][index].title}
|
||||
isRoot={isRoot}
|
||||
onClick={data[1]}
|
||||
style={{ ...style }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const DropBox = React.forwardRef(
|
||||
(
|
||||
{
|
||||
sectionHeight,
|
||||
showText,
|
||||
dropBoxWidth,
|
||||
isRootFolder,
|
||||
onBackToParentFolder,
|
||||
title,
|
||||
personal,
|
||||
canCreate,
|
||||
navigationItems,
|
||||
getContextOptionsFolder,
|
||||
getContextOptionsPlus,
|
||||
toggleDropBox,
|
||||
onClickAvailable,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [dropBoxHeight, setDropBoxHeight] = React.useState(0);
|
||||
const countItems = navigationItems.length;
|
||||
|
||||
const getItemSize = (index) => {
|
||||
if (index === countItems - 1) return 51;
|
||||
return isMobile || isMobileUtils() || isTabletUtils() ? 36 : 30;
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const itemsHeight = navigationItems.map((item, index) =>
|
||||
getItemSize(index)
|
||||
);
|
||||
|
||||
const currentHeight = itemsHeight.reduce((a, b) => a + b);
|
||||
|
||||
let navHeight = 41;
|
||||
|
||||
if (isMobile || isTabletUtils()) {
|
||||
navHeight = 49;
|
||||
}
|
||||
|
||||
if (isMobileOnly || isMobileUtils()) {
|
||||
navHeight = 45;
|
||||
}
|
||||
|
||||
setDropBoxHeight(
|
||||
currentHeight + navHeight > sectionHeight
|
||||
? sectionHeight - navHeight
|
||||
: currentHeight
|
||||
);
|
||||
}, [sectionHeight]);
|
||||
|
||||
return (
|
||||
<StyledBox
|
||||
ref={ref}
|
||||
height={sectionHeight < dropBoxHeight ? sectionHeight : null}
|
||||
showText={showText}
|
||||
dropBoxWidth={dropBoxWidth}
|
||||
>
|
||||
<StyledContainer canCreate={canCreate} isDropBox={true}>
|
||||
<ArrowButton
|
||||
isRootFolder={isRootFolder}
|
||||
onBackToParentFolder={onBackToParentFolder}
|
||||
/>
|
||||
<Text title={title} isOpen={true} onClick={toggleDropBox} />
|
||||
<ControlButtons
|
||||
personal={personal}
|
||||
isRootFolder={isRootFolder}
|
||||
isDropBox={true}
|
||||
canCreate={canCreate}
|
||||
getContextOptionsFolder={getContextOptionsFolder}
|
||||
getContextOptionsPlus={getContextOptionsPlus}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
||||
<VariableSizeList
|
||||
height={dropBoxHeight}
|
||||
width={"auto"}
|
||||
itemCount={countItems}
|
||||
itemSize={getItemSize}
|
||||
itemData={[navigationItems, onClickAvailable]}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{Row}
|
||||
</VariableSizeList>
|
||||
</StyledBox>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DropBox.propTypes = {
|
||||
width: PropTypes.number,
|
||||
changeWidth: PropTypes.bool,
|
||||
isRootFolder: PropTypes.bool,
|
||||
onBackToParentFolder: PropTypes.func,
|
||||
title: PropTypes.string,
|
||||
personal: PropTypes.bool,
|
||||
canCreate: PropTypes.bool,
|
||||
navigationItems: PropTypes.arrayOf(PropTypes.object),
|
||||
getContextOptionsFolder: PropTypes.func,
|
||||
getContextOptionsPlus: PropTypes.func,
|
||||
toggleDropBox: PropTypes.func,
|
||||
onClickAvailable: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(DropBox);
|
@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Text from "@appserver/components/text";
|
||||
|
||||
import DefaultIcon from "../svg/default.react.svg";
|
||||
import RootIcon from "../svg/root.react.svg";
|
||||
import DefaultTabletIcon from "../svg/default.tablet.react.svg";
|
||||
import RootTabletIcon from "../svg/root.tablet.react.svg";
|
||||
|
||||
import { isMobile } from "react-device-detect";
|
||||
import {
|
||||
tablet,
|
||||
isTablet,
|
||||
isMobile as IsMobileUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledItem = styled.div`
|
||||
height: auto;
|
||||
width: auto !important;
|
||||
position: relative;
|
||||
display: grid;
|
||||
align-items: ${(props) => (props.isRoot ? "baseline" : "end")};
|
||||
grid-template-columns: 17px auto;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledIconWrapper = styled.div`
|
||||
width: 17px;
|
||||
display: flex;
|
||||
align-items: ${(props) => (props.isRoot ? "center" : "flex-end")};
|
||||
justify-content: center;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.navigation.icon.fill};
|
||||
}
|
||||
|
||||
circle {
|
||||
stroke: ${(props) => props.theme.navigation.icon.fill};
|
||||
}
|
||||
|
||||
path:first-child {
|
||||
fill: none !important;
|
||||
stroke: ${(props) => props.theme.navigation.icon.stroke};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledIconWrapper.defaultProps = { theme: Base };
|
||||
|
||||
const StyledText = styled(Text)`
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
bottom: ${(props) => (props.isRoot ? "2px" : "-1px")};
|
||||
`;
|
||||
|
||||
const Item = ({ id, title, isRoot, onClick, ...rest }) => {
|
||||
const onClickAvailable = () => {
|
||||
onClick && onClick(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledItem id={id} isRoot={isRoot} onClick={onClickAvailable} {...rest}>
|
||||
<StyledIconWrapper isRoot={isRoot}>
|
||||
{isMobile || isTablet() || IsMobileUtils() ? (
|
||||
isRoot ? (
|
||||
<RootTabletIcon />
|
||||
) : (
|
||||
<DefaultTabletIcon />
|
||||
)
|
||||
) : isRoot ? (
|
||||
<RootIcon />
|
||||
) : (
|
||||
<DefaultIcon />
|
||||
)}
|
||||
</StyledIconWrapper>
|
||||
<StyledText
|
||||
isRoot={isRoot}
|
||||
fontWeight={isRoot ? "600" : "400"}
|
||||
isRoot={isRoot}
|
||||
fontSize={"15px"}
|
||||
truncate={true}
|
||||
>
|
||||
{title}
|
||||
</StyledText>
|
||||
</StyledItem>
|
||||
);
|
||||
};
|
||||
|
||||
Item.propTypes = {
|
||||
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
title: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
isRoot: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(Item);
|
@ -0,0 +1,99 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import ExpanderDownIcon from "@appserver/components/public/static/images/expander-down.react.svg";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
|
||||
import Headline from "@appserver/common/components/Headline";
|
||||
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
const StyledTextContainer = styled.div`
|
||||
width: fit-content;
|
||||
|
||||
position: relative;
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: ${(props) =>
|
||||
props.isRootFolder ? "auto" : "auto 12px"};
|
||||
|
||||
align-items: center;
|
||||
|
||||
${(props) => !props.isRootFolder && "cursor: pointer"};
|
||||
`;
|
||||
|
||||
const StyledHeadline = styled(Headline)`
|
||||
width: calc(100% + 1px);
|
||||
font-weight: 700;
|
||||
font-size: ${isMobile ? "21px !important" : "18px"};
|
||||
line-height: ${isMobile ? "28px !important" : "24px"};
|
||||
@media ${tablet} {
|
||||
font-size: 21px;
|
||||
line-height: 28px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledExpanderDownIcon = styled(ExpanderDownIcon)`
|
||||
min-width: 8px !important;
|
||||
width: 8px !important;
|
||||
min-height: 18px !important;
|
||||
padding: 0 2px 0 4px;
|
||||
path {
|
||||
fill: ${(props) => props.theme.navigation.expanderColor};
|
||||
}
|
||||
|
||||
${commonIconsStyles};
|
||||
`;
|
||||
|
||||
StyledExpanderDownIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledExpanderDownIconRotate = styled(ExpanderDownIcon)`
|
||||
min-width: 8px !important;
|
||||
width: 8px !important;
|
||||
min-height: 18px !important;
|
||||
padding: 0 4px 0 1px;
|
||||
transform: rotate(-180deg);
|
||||
|
||||
path {
|
||||
fill: ${(props) => props.theme.navigation.expanderColor};
|
||||
}
|
||||
|
||||
${commonIconsStyles};
|
||||
`;
|
||||
|
||||
StyledExpanderDownIconRotate.defaultProps = { theme: Base };
|
||||
|
||||
const Text = ({ title, isRootFolder, isOpen, onClick, ...rest }) => {
|
||||
return (
|
||||
<StyledTextContainer
|
||||
isRootFolder={isRootFolder}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
>
|
||||
<StyledHeadline type="content" truncate={true}>
|
||||
{title}
|
||||
</StyledHeadline>
|
||||
{!isRootFolder ? (
|
||||
isOpen ? (
|
||||
<StyledExpanderDownIconRotate />
|
||||
) : (
|
||||
<StyledExpanderDownIcon />
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</StyledTextContainer>
|
||||
);
|
||||
};
|
||||
|
||||
Text.propTypes = {
|
||||
title: PropTypes.string,
|
||||
isOpen: PropTypes.bool,
|
||||
isRootFolder: PropTypes.bool,
|
||||
onCLick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default React.memo(Text);
|
@ -0,0 +1,4 @@
|
||||
<svg width="19" height="30" viewBox="0 0 19 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.5 1V16" stroke="#DFE2E3" stroke-miterlimit="16" stroke-dasharray="2 2"/>
|
||||
<circle cx="9.5" cy="23.5" r="2.5" stroke="#316DAA" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 259 B |
@ -0,0 +1,4 @@
|
||||
<svg width="19" height="36" viewBox="0 0 19 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.5 1V22" stroke="#DFE2E3" stroke-miterlimit="16" stroke-dasharray="2 2"/>
|
||||
<circle cx="9.5" cy="29.5" r="2.5" stroke="#316DAA" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 259 B |
@ -0,0 +1,4 @@
|
||||
<svg width="19" height="31" viewBox="0 0 19 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.5 1L9.5 15" stroke="#DFE2E3" stroke-miterlimit="16" stroke-dasharray="2 2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 19L14 22.7386V27.5461C14 28.3491 13.3284 29 12.5 29H6.5C5.67157 29 5 28.3491 5 27.5461V22.7386L9.5 19ZM7 23.6302V27.0615H12V23.6302L9.5 21.5532L7 23.6302Z" fill="#316DAA"/>
|
||||
</svg>
|
After Width: | Height: | Size: 419 B |
@ -0,0 +1,4 @@
|
||||
<svg width="19" height="36" viewBox="0 0 19 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.5 1L9.5 20" stroke="#DFE2E3" stroke-miterlimit="16" stroke-dasharray="2 2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 24L14 27.7386V32.5461C14 33.3491 13.3284 34 12.5 34H6.5C5.67157 34 5 33.3491 5 32.5461V27.7386L9.5 24ZM7 28.6302V32.0615H12V28.6302L9.5 26.5532L7 28.6302Z" fill="#316DAA"/>
|
||||
</svg>
|
After Width: | Height: | Size: 419 B |
@ -0,0 +1,22 @@
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import SearchInput from "@appserver/components/search-input";
|
||||
|
||||
const StyledFilterInput = styled.div`
|
||||
width: 100%;
|
||||
max-width: ${(props) => props.sectionWidth}px;
|
||||
height: 32px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
`;
|
||||
|
||||
const StyledSearchInput = styled(SearchInput)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export { StyledFilterInput, StyledSearchInput };
|
115
packages/asc-web-common/components/NewFilterInput/index.js
Normal file
115
packages/asc-web-common/components/NewFilterInput/index.js
Normal file
@ -0,0 +1,115 @@
|
||||
import React from "react";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
|
||||
import {
|
||||
isMobile as isMobileUtils,
|
||||
isTablet as isTabletUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
|
||||
import ViewSelector from "@appserver/components/view-selector";
|
||||
|
||||
import FilterButton from "./sub-components/FilterButton";
|
||||
import SortButton from "./sub-components/SortButton";
|
||||
|
||||
import { StyledFilterInput, StyledSearchInput } from "./StyledFilterInput";
|
||||
|
||||
const FilterInput = ({
|
||||
t,
|
||||
sectionWidth,
|
||||
getFilterData,
|
||||
getSortData,
|
||||
getViewSettingsData,
|
||||
getSelectedFilterData,
|
||||
onFilter,
|
||||
onSearch,
|
||||
onSort,
|
||||
onChangeViewAs,
|
||||
viewAs,
|
||||
placeholder,
|
||||
contextMenuHeader,
|
||||
headerLabel,
|
||||
viewSelectorVisible,
|
||||
isRecentFolder,
|
||||
isFavoritesFolder,
|
||||
...props
|
||||
}) => {
|
||||
const [viewSettings, setViewSettings] = React.useState([]);
|
||||
const [selectedFilterData, setSelectedFilterData] = React.useState([]);
|
||||
|
||||
const [inputValue, setInputValue] = React.useState("");
|
||||
|
||||
const getSelectedFilterDataAction = React.useCallback(async () => {
|
||||
const data = await getSelectedFilterData();
|
||||
|
||||
setSelectedFilterData(data);
|
||||
setInputValue(!!data.inputValue ? data.inputValue : "");
|
||||
}, [getSelectedFilterData]);
|
||||
|
||||
React.useEffect(() => {
|
||||
getSelectedFilterDataAction();
|
||||
}, [getSelectedFilterData]);
|
||||
|
||||
React.useEffect(() => {
|
||||
getViewSettingsData && setViewSettings(getViewSettingsData());
|
||||
}, [getViewSettingsData]);
|
||||
|
||||
const onClearSearch = () => {
|
||||
onSearch && onSearch();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledFilterInput {...props} sectionWidth={sectionWidth}>
|
||||
<StyledSearchInput
|
||||
placeholder={placeholder}
|
||||
value={inputValue}
|
||||
onChange={onSearch}
|
||||
onClearSearch={onClearSearch}
|
||||
/>
|
||||
|
||||
<FilterButton
|
||||
t={t}
|
||||
selectedFilterData={selectedFilterData}
|
||||
contextMenuHeader={contextMenuHeader}
|
||||
getFilterData={getFilterData}
|
||||
onFilter={onFilter}
|
||||
headerLabel={headerLabel}
|
||||
/>
|
||||
|
||||
{viewSettings &&
|
||||
!isMobile &&
|
||||
viewSelectorVisible &&
|
||||
!isMobileUtils() &&
|
||||
!isTabletUtils() ? (
|
||||
<ViewSelector
|
||||
style={{ marginLeft: "8px" }}
|
||||
onChangeView={onChangeViewAs}
|
||||
viewAs={viewAs === "table" ? "row" : viewAs}
|
||||
viewSettings={viewSettings}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{(isMobile || isTabletUtils() || isMobileUtils()) && (
|
||||
<SortButton
|
||||
t={t}
|
||||
selectedFilterData={selectedFilterData}
|
||||
getSortData={getSortData}
|
||||
onChangeViewAs={onChangeViewAs}
|
||||
viewAs={viewAs === "table" ? "row" : viewAs}
|
||||
viewSettings={viewSettings}
|
||||
onSort={onSort}
|
||||
viewSelectorVisible={viewSelectorVisible}
|
||||
isRecentFolder={isRecentFolder}
|
||||
isFavoritesFolder={isFavoritesFolder}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StyledFilterInput>
|
||||
);
|
||||
};
|
||||
|
||||
FilterInput.defaultProps = {
|
||||
viewSelectorVisible: false,
|
||||
};
|
||||
|
||||
export default React.memo(FilterInput);
|
@ -0,0 +1,256 @@
|
||||
import React from "react";
|
||||
|
||||
import Backdrop from "@appserver/components/backdrop";
|
||||
import Button from "@appserver/components/button";
|
||||
import Heading from "@appserver/components/heading";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
|
||||
import FilterBlockItem from "./FilterBlockItem";
|
||||
|
||||
import PeopleSelector from "people/PeopleSelector";
|
||||
import GroupSelector from "people/GroupSelector";
|
||||
|
||||
import {
|
||||
StyledFilterBlock,
|
||||
StyledFilterBlockHeader,
|
||||
StyledFilterBlockFooter,
|
||||
StyledControlContainer,
|
||||
StyledCrossIcon,
|
||||
} from "./StyledFilterBlock";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
//TODO: fix translate
|
||||
const FilterBlock = ({
|
||||
t,
|
||||
selectedFilterData,
|
||||
contextMenuHeader,
|
||||
getFilterData,
|
||||
hideFilterBlock,
|
||||
onFilter,
|
||||
headerLabel,
|
||||
}) => {
|
||||
const [showSelector, setShowSelector] = React.useState({
|
||||
show: false,
|
||||
isAuthor: false,
|
||||
group: "",
|
||||
});
|
||||
|
||||
const [filterData, setFilterData] = React.useState([]);
|
||||
const [filterValues, setFilterValues] = React.useState([]);
|
||||
|
||||
const changeShowSelector = (isAuthor, group) => {
|
||||
setShowSelector((val) => {
|
||||
return {
|
||||
show: !val.show,
|
||||
isAuthor: isAuthor,
|
||||
group: group,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const changeSelectedItems = (filter) => {
|
||||
const items = filterData.slice();
|
||||
|
||||
items.forEach((item) => {
|
||||
if (filter.find((value) => value.group === item.group)) {
|
||||
const currentFilter = filter.filter(
|
||||
(value) => value.group === item.group
|
||||
)[0];
|
||||
|
||||
item.groupItem.forEach((groupItem) => {
|
||||
groupItem.isSelected = false;
|
||||
if (groupItem.key === currentFilter.key) {
|
||||
groupItem.isSelected = true;
|
||||
}
|
||||
if (groupItem.isSelector) {
|
||||
groupItem.isSelected = true;
|
||||
groupItem.selectedKey = currentFilter.key;
|
||||
groupItem.selectedLabel = currentFilter.label;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
item.groupItem.forEach((groupItem) => {
|
||||
groupItem.isSelected = false;
|
||||
if (groupItem.isSelector) {
|
||||
groupItem.selectedKey = null;
|
||||
groupItem.selectedLabel = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setFilterData(items);
|
||||
};
|
||||
|
||||
const clearFilter = () => {
|
||||
changeSelectedItems([]);
|
||||
setFilterValues([]);
|
||||
};
|
||||
|
||||
const changeFilterValue = (group, key, isSelected, label) => {
|
||||
let value = filterValues.concat();
|
||||
|
||||
if (isSelected) {
|
||||
value = filterValues.filter((item) => item.group !== group);
|
||||
|
||||
setFilterValues(value);
|
||||
changeSelectedItems(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.find((item) => item.group === group)) {
|
||||
value.forEach((item) => {
|
||||
if (item.group === group) {
|
||||
item.key = key;
|
||||
if (label) {
|
||||
item.label = label;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (label) {
|
||||
value.push({ group, key, label });
|
||||
} else {
|
||||
value.push({ group, key });
|
||||
}
|
||||
}
|
||||
|
||||
setFilterValues(value);
|
||||
changeSelectedItems(value);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const data = getFilterData();
|
||||
|
||||
const items = data.filter((item) => item.isHeader === true);
|
||||
|
||||
items.forEach((item) => {
|
||||
const groupItem = data.filter(
|
||||
(val) => val.group === item.group && val.isHeader !== true
|
||||
);
|
||||
|
||||
groupItem.forEach((item) => (item.isSelected = false));
|
||||
|
||||
item.groupItem = groupItem;
|
||||
});
|
||||
|
||||
if (selectedFilterData.filterValues) {
|
||||
selectedFilterData.filterValues.forEach((value) => {
|
||||
items.forEach((item) => {
|
||||
if (item.group === value.group) {
|
||||
item.groupItem.forEach((groupItem) => {
|
||||
if (groupItem.key === value.key || groupItem.isSelector) {
|
||||
groupItem.isSelected = true;
|
||||
if (groupItem.isSelector) {
|
||||
groupItem.selectedLabel = value.label;
|
||||
groupItem.selectedKey = value.key;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setFilterData(items);
|
||||
setFilterValues(selectedFilterData.filterValues);
|
||||
}, [selectedFilterData, getFilterData]);
|
||||
|
||||
const onFilterAction = () => {
|
||||
onFilter && onFilter(filterValues);
|
||||
hideFilterBlock();
|
||||
};
|
||||
|
||||
const onArrowClick = () => {
|
||||
setShowSelector((val) => ({ ...val, show: false }));
|
||||
};
|
||||
|
||||
const selectOption = (items) => {
|
||||
setShowSelector((val) => ({
|
||||
...val,
|
||||
show: false,
|
||||
}));
|
||||
|
||||
changeFilterValue(showSelector.group, items[0].key, false, items[0].label);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showSelector.show ? (
|
||||
<>
|
||||
<StyledFilterBlock>
|
||||
{showSelector.isAuthor ? (
|
||||
<PeopleSelector
|
||||
className="people-selector"
|
||||
isOpen={showSelector.show}
|
||||
withoutAside={true}
|
||||
isMultiSelect={false}
|
||||
onSelect={selectOption}
|
||||
onArrowClick={onArrowClick}
|
||||
headerLabel={headerLabel}
|
||||
/>
|
||||
) : (
|
||||
<GroupSelector
|
||||
className="people-selector"
|
||||
isOpen={showSelector.show}
|
||||
withoutAside={true}
|
||||
isMultiSelect={false}
|
||||
onSelect={selectOption}
|
||||
onArrowClick={onArrowClick}
|
||||
headerLabel={headerLabel}
|
||||
/>
|
||||
)}
|
||||
</StyledFilterBlock>
|
||||
</>
|
||||
) : (
|
||||
<StyledFilterBlock>
|
||||
<StyledFilterBlockHeader>
|
||||
<Heading size="medium">{contextMenuHeader}</Heading>
|
||||
<IconButton
|
||||
iconName="/static/images/clear.react.svg"
|
||||
isFill={true}
|
||||
onClick={clearFilter}
|
||||
size={17}
|
||||
/>
|
||||
</StyledFilterBlockHeader>
|
||||
{filterData.map((item) => {
|
||||
return (
|
||||
<FilterBlockItem
|
||||
key={item.key}
|
||||
label={item.label}
|
||||
keyProp={item.key}
|
||||
group={item.group}
|
||||
groupItem={item.groupItem}
|
||||
isLast={item.isLast}
|
||||
withoutHeader={item.withoutHeader}
|
||||
changeFilterValue={changeFilterValue}
|
||||
showSelector={changeShowSelector}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<StyledFilterBlockFooter>
|
||||
<Button
|
||||
size="normal"
|
||||
primary={true}
|
||||
label={t("AddFilter")}
|
||||
scale={true}
|
||||
onClick={onFilterAction}
|
||||
/>
|
||||
</StyledFilterBlockFooter>
|
||||
</StyledFilterBlock>
|
||||
)}
|
||||
|
||||
<Backdrop
|
||||
visible={true}
|
||||
withBackground={true}
|
||||
onClick={hideFilterBlock}
|
||||
/>
|
||||
|
||||
<StyledControlContainer onClick={hideFilterBlock}>
|
||||
<StyledCrossIcon />
|
||||
</StyledControlContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(withTranslation("Common")(FilterBlock));
|
@ -0,0 +1,153 @@
|
||||
import React from "react";
|
||||
|
||||
import SelectorAddButton from "@appserver/components/selector-add-button";
|
||||
import Heading from "@appserver/components/heading";
|
||||
|
||||
import {
|
||||
StyledFilterBlockItem,
|
||||
StyledFilterBlockItemHeader,
|
||||
StyledFilterBlockItemContent,
|
||||
StyledFilterBlockItemSelector,
|
||||
StyledFilterBlockItemSelectorText,
|
||||
StyledFilterBlockItemTag,
|
||||
StyledFilterBlockItemTagText,
|
||||
StyledFilterBlockItemTagIcon,
|
||||
StyledFilterBlockItemToggle,
|
||||
StyledFilterBlockItemToggleText,
|
||||
StyledFilterBlockItemToggleButton,
|
||||
StyledFilterBlockItemSeparator,
|
||||
} from "./StyledFilterBlock";
|
||||
|
||||
import TickIcon from "../svg/tick.react.svg";
|
||||
import XIcon from "../svg/x.react.svg";
|
||||
|
||||
const FilterBlockItem = ({
|
||||
group,
|
||||
label,
|
||||
groupItem,
|
||||
isLast,
|
||||
withoutHeader,
|
||||
changeFilterValue,
|
||||
showSelector,
|
||||
}) => {
|
||||
const changeFilterValueAction = (key, isSelected) => {
|
||||
changeFilterValue && changeFilterValue(group, key, isSelected);
|
||||
};
|
||||
|
||||
const showSelectorAction = (event, isAuthor, group, ref) => {
|
||||
let target = event.target;
|
||||
|
||||
while (!!target.parentNode) {
|
||||
target = target.parentNode;
|
||||
|
||||
if (target === ref) {
|
||||
changeFilterValue && changeFilterValue(group, [], true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
showSelector && showSelector(isAuthor, group);
|
||||
};
|
||||
|
||||
const getSelectorItem = (item) => {
|
||||
const clearSelectorRef = React.useRef(null);
|
||||
|
||||
const isAuthor = item.key === "user";
|
||||
|
||||
return !item.isSelected ? (
|
||||
<StyledFilterBlockItemSelector key={item.key}>
|
||||
<SelectorAddButton
|
||||
onClick={(event) =>
|
||||
showSelectorAction(event, isAuthor, item.group, [])
|
||||
}
|
||||
/>
|
||||
<StyledFilterBlockItemSelectorText noSelect={true}>
|
||||
{item.label}
|
||||
</StyledFilterBlockItemSelectorText>
|
||||
</StyledFilterBlockItemSelector>
|
||||
) : (
|
||||
<StyledFilterBlockItemTag
|
||||
key={item.key}
|
||||
isSelected={item.isSelected}
|
||||
onClick={(event) =>
|
||||
showSelectorAction(
|
||||
event,
|
||||
isAuthor,
|
||||
item.group,
|
||||
clearSelectorRef.current
|
||||
)
|
||||
}
|
||||
>
|
||||
<StyledFilterBlockItemTagText
|
||||
noSelect={true}
|
||||
isSelected={item.isSelected}
|
||||
>
|
||||
{item.selectedLabel.toLowerCase()}
|
||||
</StyledFilterBlockItemTagText>
|
||||
{item.isSelected && (
|
||||
<StyledFilterBlockItemTagIcon ref={clearSelectorRef}>
|
||||
<XIcon style={{ marginTop: "2px" }} />
|
||||
</StyledFilterBlockItemTagIcon>
|
||||
)}
|
||||
</StyledFilterBlockItemTag>
|
||||
);
|
||||
};
|
||||
|
||||
const getToggleItem = (item) => {
|
||||
return (
|
||||
<StyledFilterBlockItemToggle key={item.key}>
|
||||
<StyledFilterBlockItemToggleText noSelect={true}>
|
||||
{item.label}
|
||||
</StyledFilterBlockItemToggleText>
|
||||
<StyledFilterBlockItemToggleButton
|
||||
isChecked={item.isSelected}
|
||||
onChange={() => changeFilterValueAction(item.key, item.isSelected)}
|
||||
/>
|
||||
</StyledFilterBlockItemToggle>
|
||||
);
|
||||
};
|
||||
|
||||
const getTagItem = (item) => {
|
||||
return (
|
||||
<StyledFilterBlockItemTag
|
||||
key={item.key}
|
||||
isSelected={item.isSelected}
|
||||
name={`${item.label.toLowerCase()}-${item.key}`}
|
||||
onClick={() => changeFilterValueAction(item.key, item.isSelected)}
|
||||
>
|
||||
<StyledFilterBlockItemTagText
|
||||
noSelect={true}
|
||||
isSelected={item.isSelected}
|
||||
>
|
||||
{item.label.toLowerCase()}
|
||||
</StyledFilterBlockItemTagText>
|
||||
{item.isSelected && (
|
||||
<StyledFilterBlockItemTagIcon>
|
||||
<TickIcon />
|
||||
</StyledFilterBlockItemTagIcon>
|
||||
)}
|
||||
</StyledFilterBlockItemTag>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledFilterBlockItem withoutHeader={withoutHeader}>
|
||||
{!withoutHeader && (
|
||||
<StyledFilterBlockItemHeader>
|
||||
<Heading size="xsmall">{label}</Heading>
|
||||
</StyledFilterBlockItemHeader>
|
||||
)}
|
||||
|
||||
<StyledFilterBlockItemContent withoutHeader={withoutHeader}>
|
||||
{groupItem.map((item) => {
|
||||
if (item.isSelector === true) return getSelectorItem(item);
|
||||
if (item.isToggle === true) return getToggleItem(item);
|
||||
return getTagItem(item);
|
||||
})}
|
||||
</StyledFilterBlockItemContent>
|
||||
{!isLast && <StyledFilterBlockItemSeparator />}
|
||||
</StyledFilterBlockItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FilterBlockItem);
|
@ -0,0 +1,66 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
import FilterBlock from "./FilterBlock";
|
||||
|
||||
import StyledButton from "./StyledButton";
|
||||
|
||||
const Indicator = styled.div`
|
||||
border-radius: 50%;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
||||
background: ${(props) => props.theme.newFilterInput.filter.indicatorColor};
|
||||
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
left: 25px;
|
||||
|
||||
z-index: 3;
|
||||
`;
|
||||
|
||||
Indicator.defaultProps = { theme: Base };
|
||||
|
||||
const FilterButton = ({
|
||||
t,
|
||||
selectedFilterData,
|
||||
contextMenuHeader,
|
||||
getFilterData,
|
||||
onFilter,
|
||||
headerLabel,
|
||||
}) => {
|
||||
const [showFilterBlock, setShowFilterBlock] = React.useState(false);
|
||||
|
||||
const changeShowFilterBlock = React.useCallback(() => {
|
||||
setShowFilterBlock((value) => !value);
|
||||
}, [setShowFilterBlock]);
|
||||
|
||||
// console.log(selectedFilterData.filterValues);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledButton onClick={changeShowFilterBlock}>
|
||||
<IconButton iconName="/static/images/filter.react.svg" size={16} />
|
||||
{selectedFilterData.filterValues &&
|
||||
selectedFilterData.filterValues.length > 0 && <Indicator />}
|
||||
</StyledButton>
|
||||
|
||||
{showFilterBlock && (
|
||||
<FilterBlock
|
||||
t={t}
|
||||
contextMenuHeader={contextMenuHeader}
|
||||
selectedFilterData={selectedFilterData}
|
||||
hideFilterBlock={changeShowFilterBlock}
|
||||
getFilterData={getFilterData}
|
||||
onFilter={onFilter}
|
||||
headerLabel={headerLabel}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(FilterButton);
|
@ -0,0 +1,318 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
||||
import ComboBox from "@appserver/components/combobox";
|
||||
import DropDownItem from "@appserver/components/drop-down-item";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
import ViewSelector from "@appserver/components/view-selector";
|
||||
import Text from "@appserver/components/text";
|
||||
|
||||
import { mobile } from "@appserver/components/utils/device";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
|
||||
import SortDesc from "../../../../../public/images/sort.desc.react.svg";
|
||||
|
||||
const selectedViewIcon = css`
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.newFilterInput.sort.selectedViewIcon};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const notSelectedViewIcon = css`
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.newFilterInput.sort.viewIcon};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const mobileView = css`
|
||||
position: fixed;
|
||||
top: auto;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100vw;
|
||||
|
||||
z-index: 999;
|
||||
`;
|
||||
|
||||
const StyledSortButton = styled.div`
|
||||
.combo-button {
|
||||
background: ${(props) =>
|
||||
props.theme.newFilterInput.sort.background} !important;
|
||||
|
||||
.icon-button_svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-combo-box {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
margin-left: 8px;
|
||||
|
||||
.dropdown-container {
|
||||
top: 102%;
|
||||
bottom: auto;
|
||||
min-width: 200px;
|
||||
margin-top: 3px;
|
||||
|
||||
@media ${mobile} {
|
||||
${mobileView}
|
||||
}
|
||||
|
||||
${isMobileOnly && mobileView}
|
||||
|
||||
.view-selector-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
cursor: auto;
|
||||
|
||||
.view-selector {
|
||||
width: 44px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
cursor: auto;
|
||||
|
||||
.view-selector-icon {
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.view-selector-icon:nth-child(1) {
|
||||
${(props) =>
|
||||
props.viewAs === "row" ? selectedViewIcon : notSelectedViewIcon};
|
||||
}
|
||||
|
||||
.view-selector-icon:nth-child(2) {
|
||||
${(props) =>
|
||||
props.viewAs !== "row" ? selectedViewIcon : notSelectedViewIcon};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
min-width: 200px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.option-item__icon {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
${(props) =>
|
||||
props.isDesc &&
|
||||
css`
|
||||
transform: rotate(180deg);
|
||||
`}
|
||||
|
||||
path {
|
||||
fill: ${(props) => props.theme.newFilterInput.sort.sortFill};
|
||||
}
|
||||
}
|
||||
|
||||
:hover {
|
||||
.option-item__icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-option-item {
|
||||
background: ${(props) =>
|
||||
props.theme.newFilterInput.sort.hoverBackground};
|
||||
cursor: auto;
|
||||
|
||||
.selected-option-item__icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.optionalBlock {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.backdrop-active {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledSortButton.defaultProps = { theme: Base };
|
||||
|
||||
const SortButton = ({
|
||||
t,
|
||||
selectedFilterData,
|
||||
getSortData,
|
||||
onChangeViewAs,
|
||||
viewAs,
|
||||
viewSettings,
|
||||
onSort,
|
||||
viewSelectorVisible,
|
||||
isRecentFolder,
|
||||
isFavoritesFolder,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
|
||||
const [
|
||||
currentSelectedFilterData,
|
||||
setCurrentSelectedFilterData,
|
||||
] = React.useState({});
|
||||
|
||||
React.useEffect(() => {
|
||||
setCurrentSelectedFilterData({
|
||||
sortDirection: selectedFilterData.sortDirection,
|
||||
sortId: selectedFilterData.sortId,
|
||||
});
|
||||
}, [selectedFilterData]);
|
||||
|
||||
const toggleCombobox = React.useCallback(() => {
|
||||
setIsOpen((val) => !val);
|
||||
}, []);
|
||||
|
||||
const onOptionClick = React.useCallback(
|
||||
(e) => {
|
||||
const sortDirection =
|
||||
currentSelectedFilterData.sortDirection === "desc" &&
|
||||
e.target.closest(".option-item__icon")
|
||||
? "asc"
|
||||
: "desc";
|
||||
|
||||
const key = e.target.closest(".option-item").dataset.value;
|
||||
|
||||
setCurrentSelectedFilterData({
|
||||
sortId: key,
|
||||
sortDirection: sortDirection,
|
||||
});
|
||||
|
||||
toggleCombobox();
|
||||
onSort && onSort(key, sortDirection);
|
||||
},
|
||||
[onSort, toggleCombobox, currentSelectedFilterData]
|
||||
);
|
||||
|
||||
const getSortOption = () => {};
|
||||
|
||||
const getAdvancedOptions = React.useCallback(() => {
|
||||
const data = getSortData();
|
||||
|
||||
data.forEach((item) => {
|
||||
item.className = "option-item";
|
||||
item.isSelected = false;
|
||||
if (currentSelectedFilterData.sortId === item.key) {
|
||||
item.className = item.className + " selected-option-item";
|
||||
item.isSelected = true;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{viewSelectorVisible && (
|
||||
<>
|
||||
<DropDownItem noHover className="view-selector-item">
|
||||
<Text fontWeight={600}>{t("View")}</Text>
|
||||
<ViewSelector
|
||||
className="view-selector"
|
||||
onChangeView={onChangeViewAs}
|
||||
viewAs={viewAs}
|
||||
viewSettings={viewSettings}
|
||||
/>
|
||||
</DropDownItem>
|
||||
{!isFavoritesFolder && !isRecentFolder && (
|
||||
<DropDownItem isSeparator={true}></DropDownItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isFavoritesFolder && !isRecentFolder && (
|
||||
<>
|
||||
{data.map((item, index) => (
|
||||
<DropDownItem
|
||||
onClick={onOptionClick}
|
||||
className={item.className}
|
||||
key={item.key}
|
||||
data-value={item.key}
|
||||
>
|
||||
<Text fontWeight={600}>{item.label}</Text>
|
||||
<SortDesc
|
||||
className={`option-item__icon ${
|
||||
item.isSelected ? "selected-option-item__icon" : ""
|
||||
}`}
|
||||
/>
|
||||
</DropDownItem>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [
|
||||
currentSelectedFilterData,
|
||||
onOptionClick,
|
||||
onChangeViewAs,
|
||||
viewAs,
|
||||
viewSettings,
|
||||
getSortData,
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
]);
|
||||
|
||||
return (
|
||||
<StyledSortButton
|
||||
viewAs={viewAs}
|
||||
isDesc={currentSelectedFilterData.sortDirection === "desc"}
|
||||
onClick={toggleCombobox}
|
||||
>
|
||||
<ComboBox
|
||||
opened={isOpen}
|
||||
toggleAction={toggleCombobox}
|
||||
className={"sort-combo-box"}
|
||||
options={[]}
|
||||
selectedOption={{}}
|
||||
directionX={"right"}
|
||||
directionY={"both"}
|
||||
scaled={true}
|
||||
size={"content"}
|
||||
advancedOptions={getAdvancedOptions()}
|
||||
disableIconClick={false}
|
||||
disableItemClick={true}
|
||||
isDefaultMode={false}
|
||||
manualY={"102%"}
|
||||
>
|
||||
<IconButton iconName="/static/images/sort.react.svg" size={16} />
|
||||
</ComboBox>
|
||||
</StyledSortButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(withTranslation("Common")(SortButton));
|
@ -0,0 +1,58 @@
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
const StyledButton = styled.div`
|
||||
width: 32px;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
|
||||
position: relative;
|
||||
|
||||
border: ${(props) => props.theme.newFilterInput.button.border};
|
||||
border-radius: 3px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
margin-left: 8px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border: ${(props) => props.theme.newFilterInput.button.hoverBorder};
|
||||
}
|
||||
|
||||
div {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isOpen &&
|
||||
css`
|
||||
background: ${(props) =>
|
||||
props.theme.newFilterInput.button.openBackground};
|
||||
pointer-events: none;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.newFilterInput.button.openFill};
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
margin-top: 5px;
|
||||
min-width: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledButton.defaultProps = { theme: Base };
|
||||
|
||||
export default StyledButton;
|
@ -0,0 +1,322 @@
|
||||
import Text from "@appserver/components/text";
|
||||
import styled, { css } from "styled-components";
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
|
||||
import ToggleButton from "@appserver/components/toggle-button";
|
||||
import { mobile } from "@appserver/components/utils/device";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import CrossIcon from "@appserver/components/public/static/images/cross.react.svg";
|
||||
|
||||
const mobileView = css`
|
||||
top: 64px;
|
||||
|
||||
width: 100vw !important;
|
||||
height: calc(100vh - 64px) !important;
|
||||
`;
|
||||
|
||||
const StyledFilterBlock = styled.div`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
width: 480px;
|
||||
height: 100vh;
|
||||
|
||||
z-index: 400;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background: ${(props) => props.theme.newFilterInput.filter.background};
|
||||
|
||||
@media ${mobile} {
|
||||
${mobileView}
|
||||
}
|
||||
|
||||
${isMobileOnly && mobileView}
|
||||
|
||||
.people-selector {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.selector-wrapper,
|
||||
.column-options {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledFilterBlock.defaultProps = { theme: Base };
|
||||
|
||||
const StyledFilterBlockHeader = styled.div`
|
||||
height: 53px;
|
||||
min-height: 53px;
|
||||
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
border-bottom: ${(props) =>
|
||||
props.isSelector ? "none" : props.theme.newFilterInput.filter.border};
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: ${(props) => (props.isSelector ? "start" : "space-between")};
|
||||
|
||||
.arrow-button {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledFilterBlockHeader.defaultProps = { theme: Base };
|
||||
|
||||
const StyledFilterBlockItem = styled.div`
|
||||
padding: ${(props) =>
|
||||
!props.withoutHeader ? "12px 16px 0px 16px" : "6px 16px 0px 16px"};
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemHeader = styled.div`
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemContent = styled.div`
|
||||
margin-top: ${(props) => !props.withoutHeader && "12px"};
|
||||
|
||||
height: fit-content;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemSelector = styled.div`
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
margin: 0 0 11px;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemSelectorText = styled(Text)`
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
color: ${(props) => props.theme.newFilterInput.filter.color};
|
||||
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
StyledFilterBlockItemSelectorText.defaultProps = { theme: Base };
|
||||
|
||||
const selectedItemTag = css`
|
||||
background: ${(props) =>
|
||||
props.theme.newFilterInput.filter.selectedItem.background};
|
||||
border-color: ${(props) =>
|
||||
props.theme.newFilterInput.filter.selectedItem.border};
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemTag = styled.div`
|
||||
height: 30px;
|
||||
max-height: 30px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
border: ${(props) => props.theme.newFilterInput.filter.border};
|
||||
border-radius: 16px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
padding: 4px 15px;
|
||||
|
||||
margin: 0 6px 12px 0;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
${(props) => props.isSelected && selectedItemTag}
|
||||
`;
|
||||
|
||||
StyledFilterBlockItemTag.defaultProps = { theme: Base };
|
||||
|
||||
const selectedItemTagText = css`
|
||||
color: ${(props) => props.theme.newFilterInput.filter.selectedItem.color};
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemTagText = styled(Text)`
|
||||
height: 20px;
|
||||
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
|
||||
${(props) => props.isSelected && selectedItemTagText}
|
||||
`;
|
||||
|
||||
StyledFilterBlockItemTagText.defaultProps = { theme: Base };
|
||||
|
||||
const StyledFilterBlockItemTagIcon = styled.div`
|
||||
margin-left: 8px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.newFilterInput.filter.selectedItem.color};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
StyledFilterBlockItemTagIcon.defaultProps = { theme: Base };
|
||||
|
||||
const StyledFilterBlockItemToggle = styled.div`
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemToggleText = styled(Text)`
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 36px;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemToggleButton = styled(ToggleButton)`
|
||||
position: static;
|
||||
`;
|
||||
|
||||
const StyledFilterBlockItemSeparator = styled.div`
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
|
||||
background: ${(props) => props.theme.newFilterInput.filter.separatorColor};
|
||||
|
||||
margin: 2px 0 0 0;
|
||||
`;
|
||||
|
||||
StyledFilterBlockItemToggleButton.defaultProps = { theme: Base };
|
||||
|
||||
const StyledFilterBlockFooter = styled.div`
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
z-index: 401;
|
||||
|
||||
width: 480px;
|
||||
height: 72px;
|
||||
min-height: 72px;
|
||||
|
||||
border-top: ${(props) => props.theme.newFilterInput.filter.border};
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@media ${mobile} {
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
width: 100vw;
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledFilterBlockFooter.defaultProps = { theme: Base };
|
||||
|
||||
const StyledControlContainer = styled.div`
|
||||
background: ${(props) => props.theme.catalog.control.background};
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
right: 10px;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 999;
|
||||
display: none;
|
||||
|
||||
@media ${mobile} {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
display: flex;
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledControlContainer.defaultProps = { theme: Base };
|
||||
|
||||
const StyledCrossIcon = styled(CrossIcon)`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
path {
|
||||
fill: ${(props) => props.theme.catalog.control.fill};
|
||||
}
|
||||
|
||||
display: none;
|
||||
|
||||
@media ${mobile} {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
display: flex;
|
||||
`}
|
||||
`;
|
||||
|
||||
StyledCrossIcon.defaultProps = { theme: Base };
|
||||
|
||||
export {
|
||||
StyledFilterBlock,
|
||||
StyledFilterBlockHeader,
|
||||
StyledFilterBlockItem,
|
||||
StyledFilterBlockItemHeader,
|
||||
StyledFilterBlockItemContent,
|
||||
StyledFilterBlockItemSelector,
|
||||
StyledFilterBlockItemSelectorText,
|
||||
StyledFilterBlockItemTag,
|
||||
StyledFilterBlockItemTagText,
|
||||
StyledFilterBlockItemTagIcon,
|
||||
StyledFilterBlockItemToggle,
|
||||
StyledFilterBlockItemToggleText,
|
||||
StyledFilterBlockItemToggleButton,
|
||||
StyledFilterBlockItemSeparator,
|
||||
StyledFilterBlockFooter,
|
||||
StyledControlContainer,
|
||||
StyledCrossIcon,
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.70719 10.7069L11.7072 3.70694L10.293 2.29272L4.00008 8.58561L1.70718 6.29272L0.292969 7.70694L3.29298 10.7069C3.6835 11.0975 4.31666 11.0975 4.70719 10.7069Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 329 B |
@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_19272_27968)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.41442 6.00033L10.707 9.29295L9.29284 10.7072L6.00033 7.41465L2.70919 10.7063L1.29486 9.29222L4.58611 6.00044L1.29284 2.70716L2.70705 1.29295L6.00021 4.58611L9.29278 1.29301L10.7071 2.70711L7.41442 6.00033Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_19272_27968">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 527 B |
@ -1,43 +0,0 @@
|
||||
# PageLayout
|
||||
|
||||
Default page layout
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import PageLayout from "@appserver/common/components/PageLayout";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<PageLayout withBodyScroll={true}>
|
||||
<PageLayout.ArticleHeader>{articleHeaderContent}</PageLayout.ArticleHeader>
|
||||
|
||||
<PageLayout.ArticleMainButton>
|
||||
{articleMainButtonContent}
|
||||
</PageLayout.ArticleMainButton>
|
||||
|
||||
<PageLayout.ArticleBody>{articleBodyContent}</PageLayout.ArticleBody>
|
||||
|
||||
<PageLayout.SectionHeader>{sectionHeaderContent}</PageLayout.SectionHeader>
|
||||
|
||||
<PageLayout.SectionFilter>{sectionFilterContent}</PageLayout.SectionFilter>
|
||||
|
||||
<PageLayout.SectionBody>{sectionBodyContent}</PageLayout.SectionBody>
|
||||
|
||||
<PageLayout.SectionPaging>{sectionPagingContent}</PageLayout.SectionPaging>
|
||||
</PageLayout>
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------------------- | :----: | :------: | :----: | :-----: | ----------------------------------------- |
|
||||
| `articleHeaderContent` | `bool` | - | - | - | Article header content |
|
||||
| `articleMainButtonContent` | `bool` | - | - | - | Article main button content |
|
||||
| `articleBodyContent` | `bool` | - | - | - | Article body content |
|
||||
| `sectionHeaderContent` | `bool` | - | - | - | Section header content |
|
||||
| `sectionFilterContent` | `bool` | - | - | - | Section filter content |
|
||||
| `sectionBodyContent` | `bool` | - | - | - | Section body content |
|
||||
| `sectionPagingContent` | `bool` | - | - | - | Section paging content |
|
||||
| `withBodyScroll` | `bool` | - | - | `true` | If you need display scroll inside content |
|
||||
| `withBodyAutoFocus` | `bool` | - | - | `false` | If you need set focus on content element |
|
@ -1,88 +0,0 @@
|
||||
import React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import PageLayout from ".";
|
||||
|
||||
const baseProps = {
|
||||
withBodyScroll: true,
|
||||
withBodyAutoFocus: false,
|
||||
};
|
||||
|
||||
describe("<PageLayout />", () => {
|
||||
it("renders without error", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />);
|
||||
|
||||
expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
it("componentDidUpdate() test re-render", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />).instance();
|
||||
|
||||
wrapper.componentDidUpdate({ withBodyScroll: false });
|
||||
|
||||
expect(wrapper.props).toBe(wrapper.props);
|
||||
});
|
||||
|
||||
it("componentDidUpdate() test no re-render", () => {
|
||||
const wrapper = mount(
|
||||
<PageLayout
|
||||
{...baseProps}
|
||||
articleHeaderContent={<>1</>}
|
||||
articleMainButtonContent={<>2</>}
|
||||
articleBodyContent={<>3</>}
|
||||
sectionHeaderContent={<>4</>}
|
||||
sectionFilterContent={<>5</>}
|
||||
sectionBodyContent={<>6</>}
|
||||
sectionPagingContent={<>7</>}
|
||||
withBodyScroll={false}
|
||||
/>
|
||||
).instance();
|
||||
|
||||
wrapper.componentDidUpdate(wrapper.props);
|
||||
|
||||
expect(wrapper.props.withBodyScroll).toBe(false);
|
||||
|
||||
wrapper.componentDidUpdate(wrapper.props);
|
||||
|
||||
expect(wrapper.props).toBe(wrapper.props);
|
||||
});
|
||||
|
||||
it("call backdropClick()", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />).instance();
|
||||
|
||||
wrapper.backdropClick();
|
||||
|
||||
expect(wrapper.state.isBackdropVisible).toBe(false);
|
||||
expect(wrapper.state.isArticleVisible).toBe(false);
|
||||
expect(wrapper.state.isArticlePinned).toBe(false);
|
||||
});
|
||||
|
||||
it("call pinArticle()", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />).instance();
|
||||
|
||||
wrapper.pinArticle();
|
||||
|
||||
expect(wrapper.state.isBackdropVisible).toBe(false);
|
||||
expect(wrapper.state.isArticleVisible).toBe(true);
|
||||
expect(wrapper.state.isArticlePinned).toBe(true);
|
||||
});
|
||||
|
||||
it("call unpinArticle()", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />).instance();
|
||||
|
||||
wrapper.unpinArticle();
|
||||
|
||||
expect(wrapper.state.isBackdropVisible).toBe(true);
|
||||
expect(wrapper.state.isArticleVisible).toBe(true);
|
||||
expect(wrapper.state.isArticlePinned).toBe(false);
|
||||
});
|
||||
|
||||
it("call showArticle()", () => {
|
||||
const wrapper = mount(<PageLayout {...baseProps} />).instance();
|
||||
|
||||
wrapper.showArticle();
|
||||
|
||||
expect(wrapper.state.isBackdropVisible).toBe(true);
|
||||
expect(wrapper.state.isArticleVisible).toBe(true);
|
||||
expect(wrapper.state.isArticlePinned).toBe(false);
|
||||
});
|
||||
});
|
@ -1,94 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import equal from "fast-deep-equal/react";
|
||||
import Scrollbar from "@appserver/components/scrollbar";
|
||||
import { tablet, smallTablet } from "@appserver/components/utils/device";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
const StyledArticleBody = styled.div`
|
||||
${(props) => props.displayBorder && `outline: 1px dotted;`}
|
||||
flex-grow: 1;
|
||||
|
||||
${(props) => (props.isDesktop ? "height:auto" : "height:100%")};
|
||||
.custom-scrollbar {
|
||||
width: calc(100% + 24px) !important;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
height: ${(props) =>
|
||||
props.isDesktop ? "calc(100% - 104px)" : "calc(100% - 44px)"};
|
||||
display: table;
|
||||
width: calc(100% + 16px);
|
||||
|
||||
.custom-scrollbar {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.people-tree-menu {
|
||||
margin-right: 0;
|
||||
${(props) => isMobile && props.pinned && `margin-bottom: 56px`}
|
||||
}
|
||||
|
||||
.custom-scrollbar {
|
||||
.nav-thumb-vertical {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
:hover {
|
||||
.custom-scrollbar {
|
||||
.nav-thumb-vertical {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledArticleWrapper = styled.div`
|
||||
margin: 16px 0;
|
||||
@media ${tablet} {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
`;
|
||||
|
||||
class ArticleBody extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !equal(this.props, nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
//console.log("PageLayout ArticleBody render");
|
||||
const { children, pinned, isDesktop } = this.props;
|
||||
|
||||
return (
|
||||
<StyledArticleBody pinned={pinned} isDesktop={isDesktop}>
|
||||
<Scrollbar
|
||||
id="articleScrollBar"
|
||||
className="custom-scrollbar"
|
||||
stype="mediumBlack"
|
||||
>
|
||||
<StyledArticleWrapper>{children}</StyledArticleWrapper>
|
||||
</Scrollbar>
|
||||
</StyledArticleBody>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ArticleBody.displayName = "ArticleBody";
|
||||
|
||||
ArticleBody.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
export default ArticleBody;
|
@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import equal from "fast-deep-equal/react";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledArticleHeader = styled.div`
|
||||
height: 39px;
|
||||
|
||||
@media ${tablet} {
|
||||
height: 39px;
|
||||
|
||||
.headline-heading {
|
||||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
class ArticleHeader extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !equal(this.props, nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
//console.log("PageLayout ArticleHeader render");
|
||||
return <StyledArticleHeader {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
ArticleHeader.displayName = "ArticleHeader";
|
||||
|
||||
export default ArticleHeader;
|
@ -1,33 +0,0 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import equal from "fast-deep-equal/react";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledArticleMainButton = styled.div`
|
||||
margin: 12px 0 0;
|
||||
max-width: 216px;
|
||||
.main-button_drop-down {
|
||||
line-height: 36px;
|
||||
}
|
||||
@media ${tablet} {
|
||||
margin: 16px 0 0;
|
||||
.main-button_drop-down {
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class ArticleMainButton extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !equal(this.props, nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
//console.log("PageLayout ArticleMainButton render");
|
||||
return <StyledArticleMainButton {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
ArticleMainButton.displayName = "ArticleMainButton";
|
||||
|
||||
export default ArticleMainButton;
|
@ -1,105 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import Text from "@appserver/components/text";
|
||||
import { tablet, smallTablet } from "@appserver/components/utils/device";
|
||||
import CatalogPinIcon from "../../../../../public/images/catalog.pin.react.svg";
|
||||
import CatalogUnpinIcon from "../../../../../public/images/catalog.unpin.react.svg";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
|
||||
const StyledCatalogPinIcon = styled(CatalogPinIcon)`
|
||||
${commonIconsStyles}
|
||||
`;
|
||||
|
||||
const StyledCatalogUnpinIcon = styled(CatalogUnpinIcon)`
|
||||
${commonIconsStyles}
|
||||
`;
|
||||
|
||||
const StyledArticlePinPanel = styled.div`
|
||||
border-top: 1px solid #eceef1;
|
||||
height: 47px;
|
||||
min-height: 47px;
|
||||
display: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
@media ${tablet} {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 208px;
|
||||
z-index: 10;
|
||||
background-color: #f8f9f9;
|
||||
}
|
||||
|
||||
@media ${smallTablet} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
|
||||
.icon-wrapper {
|
||||
width: 19px;
|
||||
height: 16px;
|
||||
}
|
||||
svg {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-left: 6px;
|
||||
margin-top: -2px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ArticlePinPanel = React.memo((props) => {
|
||||
//console.log("PageLayout ArticlePinPanel render");
|
||||
|
||||
const { pinned, onPin, onUnpin, t } = props;
|
||||
const textStyles = {
|
||||
as: "span",
|
||||
color: "#555F65",
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledArticlePinPanel>
|
||||
{pinned ? (
|
||||
<div onClick={onUnpin}>
|
||||
<div className="icon-wrapper">
|
||||
<StyledCatalogUnpinIcon size="scale" />
|
||||
</div>
|
||||
<Text {...textStyles}>{t("Common:Unpin")}</Text>
|
||||
</div>
|
||||
) : (
|
||||
<div onClick={onPin}>
|
||||
<div className="icon-wrapper">
|
||||
<StyledCatalogPinIcon size="scale" />
|
||||
</div>
|
||||
<Text {...textStyles}>{t("Common:Pin")}</Text>
|
||||
</div>
|
||||
)}
|
||||
</StyledArticlePinPanel>
|
||||
);
|
||||
});
|
||||
|
||||
ArticlePinPanel.displayName = "ArticlePinPanel";
|
||||
|
||||
ArticlePinPanel.propTypes = {
|
||||
pinned: PropTypes.bool,
|
||||
pinText: PropTypes.string,
|
||||
onPin: PropTypes.func,
|
||||
unpinText: PropTypes.string,
|
||||
onUnpin: PropTypes.func,
|
||||
};
|
||||
|
||||
const ArticlePinPanelWrapper = withTranslation("Common")(ArticlePinPanel);
|
||||
|
||||
export default ArticlePinPanelWrapper;
|
@ -1,119 +0,0 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
import PropTypes from "prop-types";
|
||||
import { Resizable } from "re-resizable";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
|
||||
const StyledArticle = styled.article`
|
||||
@media ${tablet} {
|
||||
${(props) =>
|
||||
props.visible &&
|
||||
!props.pinned &&
|
||||
css`
|
||||
position: fixed;
|
||||
z-index: 400;
|
||||
`}
|
||||
}
|
||||
|
||||
.resizable-block {
|
||||
padding: 0 20px;
|
||||
background: #f8f9f9;
|
||||
min-width: 256px;
|
||||
height: 100% !important;
|
||||
max-width: ${(props) =>
|
||||
props.firstLoad ? "256px" : "calc(100vw - 368px)"};
|
||||
box-sizing: border-box;
|
||||
overflow: hidden auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.resizable-border {
|
||||
div {
|
||||
cursor: ew-resize !important;
|
||||
}
|
||||
}
|
||||
${isMobile &&
|
||||
css`
|
||||
margin-top: 48px;
|
||||
height: calc(100% - 48px) !important;
|
||||
width: 240px !important;
|
||||
@media ${tablet} {
|
||||
margin-top: ${(props) => (props.pinned ? "48px;" : "0;")};
|
||||
}
|
||||
`}
|
||||
@media ${tablet} {
|
||||
padding: 0 16px;
|
||||
${(props) =>
|
||||
props.visible
|
||||
? props.pinned
|
||||
? `
|
||||
min-width: 240px;
|
||||
max-width: 240px;
|
||||
|
||||
|
||||
.increaseHeight {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
min-width: 240px;
|
||||
background: #f8f9f9;
|
||||
z-index: -1;
|
||||
}
|
||||
`
|
||||
: `
|
||||
position: fixed !important;
|
||||
width: 260px !important;
|
||||
min-width: 260px;
|
||||
max-width: 260px;
|
||||
position: fixed;
|
||||
height: 100% !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 400;
|
||||
.resizable-border {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.newItem {
|
||||
right: -24px;
|
||||
}
|
||||
`
|
||||
: `
|
||||
display: none;
|
||||
`}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class Article extends React.Component {
|
||||
render() {
|
||||
//console.log("PageLayout Article render", this.props);
|
||||
const { children, ...rest } = this.props;
|
||||
const enable = {
|
||||
top: false,
|
||||
right: !isMobile,
|
||||
bottom: false,
|
||||
left: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledArticle {...rest}>
|
||||
<Resizable
|
||||
enable={enable}
|
||||
className="resizable-block"
|
||||
handleWrapperClass="resizable-border not-selectable"
|
||||
>
|
||||
{children}
|
||||
<div className="increaseHeight"></div>
|
||||
</Resizable>
|
||||
</StyledArticle>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Article.propTypes = {
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
export default Article;
|
@ -1,59 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { tablet } from "@appserver/components/utils/device";
|
||||
import CatalogButtonIcon from "../../../../../public/images/catalog.button.react.svg";
|
||||
|
||||
const StyledSectionToggler = styled.div`
|
||||
height: 64px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 16px;
|
||||
display: none;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
@media ${tablet} {
|
||||
display: ${(props) => (props.visible ? "block" : "none")};
|
||||
}
|
||||
|
||||
div {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
padding: 14px 12px 14px 16px;
|
||||
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.13);
|
||||
border-radius: 48px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
line-height: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const iconStyle = {
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
minWidth: "20px",
|
||||
minHeight: "20px",
|
||||
};
|
||||
|
||||
const SectionToggler = React.memo((props) => {
|
||||
//console.log("PageLayout SectionToggler render");
|
||||
const { visible, onClick } = props;
|
||||
|
||||
return (
|
||||
<StyledSectionToggler className="not-selectable" visible={visible}>
|
||||
<div onClick={onClick}>
|
||||
<CatalogButtonIcon style={iconStyle} />
|
||||
</div>
|
||||
</StyledSectionToggler>
|
||||
);
|
||||
});
|
||||
|
||||
SectionToggler.displayName = "SectionToggler";
|
||||
|
||||
SectionToggler.propTypes = {
|
||||
visible: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default SectionToggler;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user