Compare commits
94 Commits
Author | SHA1 | Date | |
---|---|---|---|
a61f5e9b97 | |||
d845d87a6e | |||
b501dfe824 | |||
3fad9770a3 | |||
b1d153aa9f | |||
8b0af505a1 | |||
f0eaff7d42 | |||
352bbe2b75 | |||
0fe4894192 | |||
a7be4e00b4 | |||
2abc321eca | |||
6b2fb8dee4 | |||
66537dcec8 | |||
1bf8830887 | |||
c4d28c8a23 | |||
29c460fd4b | |||
3196b10aed | |||
f41b484dc4 | |||
855d7ae75c | |||
ffa8cdc826 | |||
8f39603f9f | |||
bcea6de791 | |||
28f4869628 | |||
cf535286c5 | |||
633513d3e9 | |||
705bfb2d64 | |||
3a3bf2addb | |||
5bb573100b | |||
a57ee78492 | |||
eab4f0427c | |||
fd2c5d1286 | |||
b35b44b2b8 | |||
ce66dcb2b5 | |||
56a91f853c | |||
81e0e58650 | |||
1470170928 | |||
85c7132b30 | |||
7e050954c3 | |||
16880cd0e2 | |||
696d87d190 | |||
87bae89ea3 | |||
1abc2cc6e1 | |||
1abab9db94 | |||
410daf649e | |||
9666841c3c | |||
9b33a20cc4 | |||
649749f9c1 | |||
5d3b8609bb | |||
93640959db | |||
f667250b2c | |||
4a84c25ac7 | |||
8ab5b4f35c | |||
de2d139288 | |||
d326d3c308 | |||
d3fc9c135f | |||
eb6e0b8d43 | |||
b01f7d5baf | |||
1ddb58ebbb | |||
b260912e01 | |||
7debd9ff2b | |||
49fd9b419a | |||
3161fe4fcb | |||
add3f85812 | |||
853bd92340 | |||
a56b2d8002 | |||
af7ca851d5 | |||
b34dad8836 | |||
ef2ea5e266 | |||
91b5b53137 | |||
d4c916923d | |||
e94a7d58e1 | |||
15d5bf605a | |||
33c59e5e65 | |||
c86d3c97a1 | |||
be87a12f21 | |||
e99e349c0b | |||
baf5acc01a | |||
00a8350f1d | |||
732f777c75 | |||
c588e13bd8 | |||
dc623b18ae | |||
d97087b83f | |||
c8c3939d59 | |||
daf240e363 | |||
070aabfc88 | |||
57b6b8c1f1 | |||
4c09e767bc | |||
07836f3d30 | |||
41d6948f3c | |||
ba9124ce0f | |||
949c7f30c3 | |||
a59bbc8790 | |||
9b3ef7a3ba | |||
9e39e607cf |
@ -1,7 +1,7 @@
|
|||||||
APP_NAME=Laravel
|
APP_NAME=Laravel
|
||||||
APP_ENV=production
|
APP_ENV=production
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=false
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
|
|
||||||
LOG_CHANNEL=daily
|
LOG_CHANNEL=daily
|
||||||
|
@ -2,8 +2,8 @@ name: Create Docker Image
|
|||||||
run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
run-name: ${{ gitea.actor }} Building Docker Image 🐳
|
||||||
on: [push]
|
on: [push]
|
||||||
env:
|
env:
|
||||||
VERSION: latest
|
|
||||||
DOCKER_HOST: tcp://127.0.0.1:2375
|
DOCKER_HOST: tcp://127.0.0.1:2375
|
||||||
|
ASSETS: c2780a3
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
public/js/manifest.js
|
public/js/manifest.js
|
||||||
public/js/vendor.js
|
public/js/vendor.js
|
||||||
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
||||||
key: build-pla-page-assets-29f7ce2
|
key: build-pla-page-assets-${{ env.ASSETS }}
|
||||||
#restore-keys: |
|
#restore-keys: |
|
||||||
# build-pla-page-assets-
|
# build-pla-page-assets-
|
||||||
|
|
||||||
@ -85,7 +85,6 @@ jobs:
|
|||||||
privileged: true
|
privileged: true
|
||||||
env:
|
env:
|
||||||
ARCH: ${{ matrix.arch }}
|
ARCH: ${{ matrix.arch }}
|
||||||
VERSIONARCH: ${{ env.VERSION }}-${{ env.ARCH }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
@ -130,7 +129,7 @@ jobs:
|
|||||||
public/js/manifest.js
|
public/js/manifest.js
|
||||||
public/js/vendor.js
|
public/js/vendor.js
|
||||||
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
#key: build-pla-page-assets-${{ hashFiles('**/package-lock.json') }}
|
||||||
key: build-pla-page-assets-29f7ce2
|
key: build-pla-page-assets-${{ env.ASSETS }}
|
||||||
#restore-keys: |
|
#restore-keys: |
|
||||||
# build-pla-page-assets-
|
# build-pla-page-assets-
|
||||||
|
|
||||||
@ -145,12 +144,12 @@ jobs:
|
|||||||
- name: Record version and Delete Unnecessary files
|
- name: Record version and Delete Unnecessary files
|
||||||
id: prebuild
|
id: prebuild
|
||||||
run: |
|
run: |
|
||||||
echo "version=$(cat public/VERSION)" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "revision=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo ${GITHUB_SHA::8} > VERSION
|
echo ${GITHUB_SHA::8} > VERSION
|
||||||
|
# [ "${GITHUB_REF_TYPE}" -eq "tag" ] && echo v${GITHUB_REF_NAME}-rel > public/VERSION
|
||||||
rm -rf .git* tests/ storage/app/test/
|
rm -rf .git* tests/ storage/app/test/
|
||||||
ls -al public/css/
|
cat VERSION public/VERSION
|
||||||
ls -al public/js/
|
# ls -al public/css/
|
||||||
|
# ls -al public/js/
|
||||||
|
|
||||||
- name: Build and Push Docker Image
|
- name: Build and Push Docker Image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
@ -158,10 +157,10 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
file: docker/Dockerfile
|
file: docker/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: "${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSIONARCH }}"
|
tags: "${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }}-${{ env.ARCH }}"
|
||||||
build-args: |
|
build-args: |
|
||||||
BUILD_REVISION=${{ steps.prebuild.outputs.revision }}
|
BUILD_REVISION=${{ env.GITHUB_SHA }}
|
||||||
BUILD_VERSION=${{ steps.prebuild.outputs.version }}
|
BUILD_VERSION=v${{ env.GITHUB_REF_NAME }}
|
||||||
|
|
||||||
manifest:
|
manifest:
|
||||||
name: Final Docker Image Manifest
|
name: Final Docker Image Manifest
|
||||||
@ -196,7 +195,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Build Docker Manifest
|
- name: Build Docker Manifest
|
||||||
run: |
|
run: |
|
||||||
docker manifest create ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }} \
|
docker manifest create ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }} \
|
||||||
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-x86_64 \
|
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }}-x86_64 \
|
||||||
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}-arm64
|
${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }}-arm64
|
||||||
docker manifest push --purge ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.VERSION }}
|
docker manifest push --purge ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }}
|
||||||
|
echo "Built container: ${{ steps.registry.outputs.registry }}/${{ env.GITHUB_REPOSITORY }}:${{ env.GITHUB_REF_NAME }}"
|
||||||
|
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -10,6 +10,9 @@ assignees: ''
|
|||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is. (One issue per report please.)
|
A clear and concise description of what the bug is. (One issue per report please.)
|
||||||
|
|
||||||
|
**Version of PLA**
|
||||||
|
What version of PLA are you using. Are you using the docker container, an distribution package or running from GIT source?
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
blank_issues_enabled: false
|
@ -32,7 +32,7 @@ Take a look at the [Docker Container](https://github.com/leenooks/phpLDAPadmin/w
|
|||||||
The update to v2 is progressing well - here is a list of work to do and done:
|
The update to v2 is progressing well - here is a list of work to do and done:
|
||||||
|
|
||||||
- [X] Creating new LDAP entries
|
- [X] Creating new LDAP entries
|
||||||
- [ ] Delete existing LDAP entries
|
- [X] Delete existing LDAP entries
|
||||||
- [X] Updating existing LDAP Entries
|
- [X] Updating existing LDAP Entries
|
||||||
- [X] Password attributes
|
- [X] Password attributes
|
||||||
- [X] Support different password hash options
|
- [X] Support different password hash options
|
||||||
@ -46,18 +46,22 @@ The update to v2 is progressing well - here is a list of work to do and done:
|
|||||||
- [X] Delete extra values for Attributes that support multiple values
|
- [X] Delete extra values for Attributes that support multiple values
|
||||||
- [ ] Delete Attributes
|
- [ ] Delete Attributes
|
||||||
- [ ] Templates to enable entries to conform to a custom standard
|
- [ ] Templates to enable entries to conform to a custom standard
|
||||||
|
- [ ] Autopopulate attribute values
|
||||||
- [X] Login to LDAP server
|
- [X] Login to LDAP server
|
||||||
- [X] Configure login by a specific attribute
|
- [X] Configure login by a specific attribute
|
||||||
- [X] Logout LDAP server
|
- [X] Logout LDAP server
|
||||||
- [X] Export entries as an LDAP
|
- [X] Export entries as an LDAP
|
||||||
- [X] Import LDIF
|
- [X] Import LDIF
|
||||||
- [X] Schema Browser
|
- [X] Schema Browser
|
||||||
|
- [ ] Searching
|
||||||
|
- [ ] Enforcing attribute uniqueness
|
||||||
- [ ] Is there something missing?
|
- [ ] Is there something missing?
|
||||||
|
|
||||||
Support is known for these LDAP servers:
|
Support is known for these LDAP servers:
|
||||||
- [X] OpenLDAP
|
- [X] OpenLDAP
|
||||||
- [X] OpenDJ
|
- [X] OpenDJ
|
||||||
- [ ] Microsoft Active Directory
|
- [ ] Microsoft Active Directory
|
||||||
|
- [ ] 389 Directory Server
|
||||||
|
|
||||||
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
If there is an LDAP server that you have that you would like to have supported, please open an issue to request it.
|
||||||
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
You might need to provide access, provide a copy or instructions to get an environment for testing. If you have enabled
|
||||||
|
@ -7,41 +7,38 @@ use Illuminate\Support\Arr;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\LDAP\Schema\AttributeType;
|
use App\Classes\LDAP\Schema\AttributeType;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an attribute of an LDAP Object
|
* Represents an attribute of an LDAP Object
|
||||||
*/
|
*/
|
||||||
class Attribute implements \Countable, \ArrayAccess, \Iterator
|
class Attribute implements \Countable, \ArrayAccess
|
||||||
{
|
{
|
||||||
// Attribute Name
|
// Attribute Name
|
||||||
protected string $name;
|
protected string $name;
|
||||||
private int $counter = 0;
|
|
||||||
|
|
||||||
protected ?AttributeType $schema = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
# Source of this attribute definition
|
|
||||||
protected $source;
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Current and Old Values
|
|
||||||
protected Collection $values;
|
|
||||||
|
|
||||||
// Is this attribute an internal attribute
|
// Is this attribute an internal attribute
|
||||||
protected bool $is_internal = FALSE;
|
protected(set) bool $is_internal = FALSE;
|
||||||
|
protected(set) bool $no_attr_tags = FALSE;
|
||||||
// Is this attribute the RDN?
|
|
||||||
protected bool $is_rdn = FALSE;
|
|
||||||
|
|
||||||
// MIN/MAX number of values
|
// MIN/MAX number of values
|
||||||
protected int $min_values_count = 0;
|
protected(set) int $min_values_count = 0;
|
||||||
protected int $max_values_count = 0;
|
protected(set) int $max_values_count = 0;
|
||||||
|
|
||||||
// RFC3866 Language Tags
|
// The schema's representation of this attribute
|
||||||
protected Collection $lang_tags;
|
protected(set) ?AttributeType $schema;
|
||||||
|
|
||||||
|
// The DN this object is in
|
||||||
|
protected(set) string $dn;
|
||||||
// The old values for this attribute - helps with isDirty() to determine if there is an update pending
|
// The old values for this attribute - helps with isDirty() to determine if there is an update pending
|
||||||
protected Collection $oldValues;
|
private Collection $_values_old;
|
||||||
|
// Current Values
|
||||||
|
private Collection $_values;
|
||||||
|
// The objectclasses of the entry that has this attribute
|
||||||
|
protected(set) Collection $oc;
|
||||||
|
|
||||||
|
private const SYNTAX_CERTIFICATE = '1.3.6.1.4.1.1466.115.121.1.8';
|
||||||
|
private const SYNTAX_CERTIFICATE_LIST = '1.3.6.1.4.1.1466.115.121.1.9';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
# Has the attribute been modified
|
# Has the attribute been modified
|
||||||
@ -94,12 +91,22 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
protected $postvalue = array();
|
protected $postvalue = array();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function __construct(string $name,array $values)
|
/**
|
||||||
|
* Create an Attribute
|
||||||
|
*
|
||||||
|
* @param string $dn DN this attribute is used in
|
||||||
|
* @param string $name Name of the attribute
|
||||||
|
* @param array $values Current Values
|
||||||
|
* @param array $oc The objectclasses that the DN of this attribute has
|
||||||
|
*/
|
||||||
|
public function __construct(string $dn,string $name,array $values,array $oc=[])
|
||||||
{
|
{
|
||||||
|
$this->dn = $dn;
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->values = collect($values);
|
$this->_values = collect($values);
|
||||||
$this->lang_tags = collect();
|
$this->_values_old = collect($values);
|
||||||
$this->oldValues = collect($values);
|
|
||||||
|
$this->oc = collect($oc);
|
||||||
|
|
||||||
$this->schema = (new Server)
|
$this->schema = (new Server)
|
||||||
->schema('attributetypes',$name);
|
->schema('attributetypes',$name);
|
||||||
@ -119,6 +126,11 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __call(string $name,array $arguments)
|
||||||
|
{
|
||||||
|
abort(555,'Method not handled: '.$name);
|
||||||
|
}
|
||||||
|
|
||||||
public function __get(string $key): mixed
|
public function __get(string $key): mixed
|
||||||
{
|
{
|
||||||
return match ($key) {
|
return match ($key) {
|
||||||
@ -132,22 +144,22 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
'hints' => $this->hints(),
|
'hints' => $this->hints(),
|
||||||
// Can this attribute be edited
|
// Can this attribute be edited
|
||||||
'is_editable' => $this->schema ? $this->schema->{$key} : NULL,
|
'is_editable' => $this->schema ? $this->schema->{$key} : NULL,
|
||||||
// Is this an internal attribute
|
// Objectclasses that required this attribute for an LDAP entry
|
||||||
'is_internal' => isset($this->{$key}) && $this->{$key},
|
'required' => $this->required(),
|
||||||
// Is this attribute the RDN
|
// Is this attribute an RDN attribute
|
||||||
'is_rdn' => $this->is_rdn,
|
'is_rdn' => $this->isRDN(),
|
||||||
// We prefer the name as per the schema if it exists
|
// We prefer the name as per the schema if it exists
|
||||||
'name' => $this->schema ? $this->schema->{$key} : $this->{$key},
|
'name' => $this->schema ? $this->schema->{$key} : $this->{$key},
|
||||||
// Attribute name in lower case
|
// Attribute name in lower case
|
||||||
'name_lc' => strtolower($this->name),
|
'name_lc' => strtolower($this->name),
|
||||||
// Old Values
|
|
||||||
'old_values' => $this->oldValues,
|
|
||||||
// Attribute values
|
|
||||||
'values' => $this->values,
|
|
||||||
// Required by Object Classes
|
// Required by Object Classes
|
||||||
'required_by' => $this->schema?->required_by_object_classes ?: collect(),
|
'required_by' => $this->schema?->required_by_object_classes ?: collect(),
|
||||||
// Used in Object Classes
|
// Used in Object Classes
|
||||||
'used_in' => $this->schema?->used_in_object_classes ?: collect(),
|
'used_in' => $this->schema?->used_in_object_classes ?: collect(),
|
||||||
|
// The current attribute values
|
||||||
|
'values' => $this->no_attr_tags ? $this->tagValues() : $this->_values,
|
||||||
|
// The original attribute values
|
||||||
|
'values_old' => $this->no_attr_tags ? $this->tagValuesOld() : $this->_values_old,
|
||||||
|
|
||||||
default => throw new \Exception('Unknown key:' . $key),
|
default => throw new \Exception('Unknown key:' . $key),
|
||||||
};
|
};
|
||||||
@ -156,11 +168,16 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
public function __set(string $key,mixed $values): void
|
public function __set(string $key,mixed $values): void
|
||||||
{
|
{
|
||||||
switch ($key) {
|
switch ($key) {
|
||||||
case 'value':
|
case 'values':
|
||||||
$this->values = collect($values);
|
$this->_values = $values;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'values_old':
|
||||||
|
$this->_values_old = $values;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
throw new \Exception('Unknown key:'.$key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,49 +186,21 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
return $this->name;
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addValue(string $value): void
|
/* INTERFACE */
|
||||||
{
|
|
||||||
$this->values->push($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function current(): mixed
|
|
||||||
{
|
|
||||||
return $this->values->get($this->counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function next(): void
|
|
||||||
{
|
|
||||||
$this->counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function key(): mixed
|
|
||||||
{
|
|
||||||
return $this->counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function valid(): bool
|
|
||||||
{
|
|
||||||
return $this->values->has($this->counter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewind(): void
|
|
||||||
{
|
|
||||||
$this->counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function count(): int
|
public function count(): int
|
||||||
{
|
{
|
||||||
return $this->values->count();
|
return $this->_values->dot()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function offsetExists(mixed $offset): bool
|
public function offsetExists(mixed $offset): bool
|
||||||
{
|
{
|
||||||
return ! is_null($this->values->has($offset));
|
return $this->_values->dot()->has($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function offsetGet(mixed $offset): mixed
|
public function offsetGet(mixed $offset): mixed
|
||||||
{
|
{
|
||||||
return $this->values->get($offset);
|
return $this->_values->dot()->get($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function offsetSet(mixed $offset, mixed $value): void
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
@ -224,15 +213,36 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
// We cannot clear values using array syntax
|
// We cannot clear values using array syntax
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
public function addValue(string $tag,array $values): void
|
||||||
|
{
|
||||||
|
$this->_values->put(
|
||||||
|
$tag,
|
||||||
|
array_unique(array_merge($this->_values
|
||||||
|
->get($tag,[]),$values)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addValueOld(string $tag,array $values): void
|
||||||
|
{
|
||||||
|
$this->_values_old->put(
|
||||||
|
$tag,
|
||||||
|
array_unique(array_merge($this->_values_old
|
||||||
|
->get($tag,[]),$values)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the hints about this attribute, ie: RDN, Required, etc
|
* Return the hints about this attribute, ie: RDN, Required, etc
|
||||||
*
|
*
|
||||||
* @return array
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function hints(): array
|
public function hints(): Collection
|
||||||
{
|
{
|
||||||
$result = collect();
|
$result = collect();
|
||||||
|
|
||||||
|
if ($this->is_internal)
|
||||||
|
return $result;
|
||||||
|
|
||||||
// Is this Attribute an RDN
|
// Is this Attribute an RDN
|
||||||
if ($this->is_rdn)
|
if ($this->is_rdn)
|
||||||
$result->put(__('rdn'),__('This attribute is required for the RDN'));
|
$result->put(__('rdn'),__('This attribute is required for the RDN'));
|
||||||
@ -240,16 +250,14 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
// If this attribute name is an alias for the schema attribute name
|
// If this attribute name is an alias for the schema attribute name
|
||||||
// @todo
|
// @todo
|
||||||
|
|
||||||
// objectClasses requiring this attribute
|
if ($this->required()->count())
|
||||||
// eg: $result->put('required','Required by objectClasses: a,b');
|
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required()->join(', ')));
|
||||||
if ($this->required_by->count())
|
|
||||||
$result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required_by->join(',')));
|
|
||||||
|
|
||||||
// This attribute has language tags
|
// If this attribute is a dynamic attribute
|
||||||
if ($this->lang_tags->count())
|
if ($this->isDynamic())
|
||||||
$result->put(__('language tags'),sprintf('%s: %d',__('This Attribute has Language Tags'),$this->lang_tags->count()));
|
$result->put(__('dynamic'),__('These are dynamic values present as a result of another attribute'));
|
||||||
|
|
||||||
return $result->toArray();
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,13 +267,38 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
*/
|
*/
|
||||||
public function isDirty(): bool
|
public function isDirty(): bool
|
||||||
{
|
{
|
||||||
return ($this->oldValues->count() !== $this->values->count())
|
return (($a=$this->values_old->dot()->filter())->keys()->count() !== ($b=$this->values->dot()->filter())->keys()->count())
|
||||||
|| ($this->values->diff($this->oldValues)->count() !== 0);
|
|| ($a->count() !== $b->count())
|
||||||
|
|| ($a->diff($b)->count() !== 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function oldValues(array $array): void
|
/**
|
||||||
|
* Are these values as a result of a dynamic attribute
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDynamic(): bool
|
||||||
{
|
{
|
||||||
$this->oldValues = collect($array);
|
return $this->schema->used_in_object_classes
|
||||||
|
->keys()
|
||||||
|
->intersect($this->schema->heirachy($this->oc))
|
||||||
|
->count() === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Work out if this attribute is an RDN attribute
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isRDN(): bool
|
||||||
|
{
|
||||||
|
// If we dont have an DN, then we cant know
|
||||||
|
if (! $this->dn)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
$rdns = collect(explode('+',substr($this->dn,0,strpos($this->dn,','))));
|
||||||
|
|
||||||
|
return $rdns->filter(fn($item) => str_starts_with($item,$this->name.'='))->count() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,37 +311,61 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator
|
|||||||
*/
|
*/
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
{
|
{
|
||||||
return view('components.attribute')
|
$view = match ($this->schema->syntax_oid) {
|
||||||
|
self::SYNTAX_CERTIFICATE => view('components.syntax.certificate'),
|
||||||
|
self::SYNTAX_CERTIFICATE_LIST => view('components.syntax.certificatelist'),
|
||||||
|
|
||||||
|
default => view()->exists($x = 'components.attribute.' . $this->name_lc)
|
||||||
|
? view($x)
|
||||||
|
: view('components.attribute'),
|
||||||
|
};
|
||||||
|
|
||||||
|
return $view
|
||||||
->with('o',$this)
|
->with('o',$this)
|
||||||
->with('edit',$edit)
|
->with('edit',$edit)
|
||||||
->with('old',$old)
|
->with('old',$old)
|
||||||
->with('new',$new);
|
->with('new',$new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render_item_old(int $key): ?string
|
public function render_item_old(string $dotkey): ?string
|
||||||
{
|
{
|
||||||
return Arr::get($this->old_values,$key);
|
return match ($this->schema->syntax_oid) {
|
||||||
|
self::SYNTAX_CERTIFICATE => join("\n",str_split(base64_encode(Arr::get($this->values_old->dot(),$dotkey)),80)),
|
||||||
|
self::SYNTAX_CERTIFICATE_LIST => join("\n",str_split(base64_encode(Arr::get($this->values_old->dot(),$dotkey)),80)),
|
||||||
|
|
||||||
|
default => Arr::get($this->values_old->dot(),$dotkey),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render_item_new(int $key): ?string
|
public function render_item_new(string $dotkey): ?string
|
||||||
{
|
{
|
||||||
return Arr::get($this->values,$key);
|
return Arr::get($this->values->dot(),$dotkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this attribute has RFC3866 Language Tags, this will enable those values to be captured
|
* Work out if this attribute is required by an objectClass the entry has
|
||||||
*
|
*
|
||||||
* @param string $tag
|
* @return Collection
|
||||||
* @param array $value
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function setLangTag(string $tag,array $value): void
|
public function required(): Collection
|
||||||
{
|
{
|
||||||
$this->lang_tags->put($tag,$value);
|
// If we dont have any objectclasses then we cant know if it is required
|
||||||
|
return $this->oc->count()
|
||||||
|
? $this->oc->intersect($this->required_by->keys())->sort()
|
||||||
|
: collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRDN(): void
|
public function tagValues(string $tag=Entry::TAG_NOTAG): Collection
|
||||||
{
|
{
|
||||||
$this->is_rdn = TRUE;
|
return collect($this->_values
|
||||||
|
->filter(fn($item,$key)=>($key===$tag))
|
||||||
|
->get($tag,[]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tagValuesOld(string $tag=Entry::TAG_NOTAG): Collection
|
||||||
|
{
|
||||||
|
return collect($this->_values_old
|
||||||
|
->filter(fn($item,$key)=>($key===$tag))
|
||||||
|
->get($tag,[]));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,6 @@ use App\Classes\LDAP\Attribute;
|
|||||||
/**
|
/**
|
||||||
* Represents an attribute whose values are binary
|
* Represents an attribute whose values are binary
|
||||||
*/
|
*/
|
||||||
class Binary extends Attribute
|
abstract class Binary extends Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -5,6 +5,7 @@ namespace App\Classes\LDAP\Attribute\Binary;
|
|||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute\Binary;
|
use App\Classes\LDAP\Attribute\Binary;
|
||||||
|
use App\Ldap\Entry;
|
||||||
use App\Traits\MD5Updates;
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,13 +15,14 @@ final class JpegPhoto extends Binary
|
|||||||
{
|
{
|
||||||
use MD5Updates;
|
use MD5Updates;
|
||||||
|
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,string $langtag=Entry::TAG_NOTAG): View
|
||||||
{
|
{
|
||||||
return view('components.attribute.binary.jpegphoto')
|
return view('components.attribute.binary.jpegphoto')
|
||||||
->with('o',$this)
|
->with('o',$this)
|
||||||
->with('edit',$edit)
|
->with('edit',$edit)
|
||||||
->with('old',$old)
|
->with('old',$old)
|
||||||
->with('new',$new)
|
->with('new',$new)
|
||||||
|
->with('langtag',$langtag)
|
||||||
->with('f',new \finfo);
|
->with('f',new \finfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
47
app/Classes/LDAP/Attribute/Certificate.php
Normal file
47
app/Classes/LDAP/Attribute/Certificate.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values is a binary user certificate
|
||||||
|
*/
|
||||||
|
final class Certificate extends Attribute
|
||||||
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
|
private array $_object = [];
|
||||||
|
|
||||||
|
public function certificate(int $key=0): string
|
||||||
|
{
|
||||||
|
return sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----",
|
||||||
|
join("\n",str_split(base64_encode(Arr::get($this->values_old,'binary.'.$key)),80))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cert_info(string $index,int $key=0): mixed
|
||||||
|
{
|
||||||
|
if (! array_key_exists($key,$this->_object))
|
||||||
|
$this->_object[$key] = openssl_x509_parse(openssl_x509_read($this->certificate($key)));
|
||||||
|
|
||||||
|
|
||||||
|
return Arr::get($this->_object[$key],$index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function expires($key=0): Carbon
|
||||||
|
{
|
||||||
|
return Carbon::createFromTimestampUTC($this->cert_info('validTo_time_t',$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subject($key=0): string
|
||||||
|
{
|
||||||
|
$subject = collect($this->cert_info('subject',$key))->reverse();
|
||||||
|
|
||||||
|
return $subject->map(fn($item,$key)=>sprintf("%s=%s",$key,$item))->join(',');
|
||||||
|
}
|
||||||
|
}
|
17
app/Classes/LDAP/Attribute/CertificateList.php
Normal file
17
app/Classes/LDAP/Attribute/CertificateList.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values is a binary user certificate
|
||||||
|
*/
|
||||||
|
final class CertificateList extends Attribute
|
||||||
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
}
|
@ -20,39 +20,59 @@ class Factory
|
|||||||
* Map of attributes to appropriate class
|
* Map of attributes to appropriate class
|
||||||
*/
|
*/
|
||||||
public const map = [
|
public const map = [
|
||||||
|
'authorityrevocationlist' => CertificateList::class,
|
||||||
|
'cacertificate' => Certificate::class,
|
||||||
|
'certificaterevocationlist' => CertificateList::class,
|
||||||
'createtimestamp' => Internal\Timestamp::class,
|
'createtimestamp' => Internal\Timestamp::class,
|
||||||
'creatorsname' => Internal\DN::class,
|
'creatorsname' => Internal\DN::class,
|
||||||
|
'configcontext' => Schema\Generic::class,
|
||||||
'contextcsn' => Internal\CSN::class,
|
'contextcsn' => Internal\CSN::class,
|
||||||
'entrycsn' => Internal\CSN::class,
|
'entrycsn' => Internal\CSN::class,
|
||||||
'entrydn' => Internal\DN::class,
|
'entrydn' => Internal\DN::class,
|
||||||
'entryuuid' => Internal\UUID::class,
|
'entryuuid' => Internal\UUID::class,
|
||||||
|
'etag' => Internal\Etag::class,
|
||||||
|
'krblastfailedauth' => Attribute\NoAttrTags\Generic::class,
|
||||||
|
'krblastpwdchange' => Attribute\NoAttrTags\Generic::class,
|
||||||
|
'krblastsuccessfulauth' => Attribute\NoAttrTags\Generic::class,
|
||||||
|
'krbpasswordexpiration' => Attribute\NoAttrTags\Generic::class,
|
||||||
|
'krbloginfailedcount' => Attribute\NoAttrTags\Generic::class,
|
||||||
|
'krbprincipalkey' => KrbPrincipalKey::class,
|
||||||
|
'krbticketflags' => KrbTicketFlags::class,
|
||||||
'gidnumber' => GidNumber::class,
|
'gidnumber' => GidNumber::class,
|
||||||
'hassubordinates' => Internal\HasSubordinates::class,
|
'hassubordinates' => Internal\HasSubordinates::class,
|
||||||
'jpegphoto' => Binary\JpegPhoto::class,
|
'jpegphoto' => Binary\JpegPhoto::class,
|
||||||
'modifytimestamp' => Internal\Timestamp::class,
|
'modifytimestamp' => Internal\Timestamp::class,
|
||||||
'modifiersname' => Internal\DN::class,
|
'modifiersname' => Internal\DN::class,
|
||||||
|
'monitorcontext' => Schema\Generic::class,
|
||||||
|
'namingcontexts' => Schema\Generic::class,
|
||||||
|
'numsubordinates' => Internal\NumSubordinates::class,
|
||||||
'objectclass' => ObjectClass::class,
|
'objectclass' => ObjectClass::class,
|
||||||
|
'pwdpolicysubentry' => Internal\PwdPolicySubentry::class,
|
||||||
'structuralobjectclass' => Internal\StructuralObjectClass::class,
|
'structuralobjectclass' => Internal\StructuralObjectClass::class,
|
||||||
'subschemasubentry' => Internal\SubschemaSubentry::class,
|
'subschemasubentry' => Internal\SubschemaSubentry::class,
|
||||||
'supportedcontrol' => Schema\OID::class,
|
'supportedcontrol' => Schema\OID::class,
|
||||||
'supportedextension' => Schema\OID::class,
|
'supportedextension' => Schema\OID::class,
|
||||||
'supportedfeatures' => Schema\OID::class,
|
'supportedfeatures' => Schema\OID::class,
|
||||||
|
'supportedldapversion' => Schema\Generic::class,
|
||||||
'supportedsaslmechanisms' => Schema\Mechanisms::class,
|
'supportedsaslmechanisms' => Schema\Mechanisms::class,
|
||||||
|
'usercertificate' => Certificate::class,
|
||||||
'userpassword' => Password::class,
|
'userpassword' => Password::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the new Object for an attribute
|
* Create the new Object for an attribute
|
||||||
*
|
*
|
||||||
|
* @param string $dn
|
||||||
* @param string $attribute
|
* @param string $attribute
|
||||||
* @param array $values
|
* @param array $values
|
||||||
|
* @param array $oc
|
||||||
* @return Attribute
|
* @return Attribute
|
||||||
*/
|
*/
|
||||||
public static function create(string $attribute,array $values): Attribute
|
public static function create(string $dn,string $attribute,array $values,array $oc=[]): Attribute
|
||||||
{
|
{
|
||||||
$class = Arr::get(self::map,strtolower($attribute),Attribute::class);
|
$class = Arr::get(self::map,strtolower($attribute),Attribute::class);
|
||||||
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class));
|
||||||
|
|
||||||
return new $class($attribute,$values);
|
return new $class($dn,$attribute,$values,$oc);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,4 +9,5 @@ use App\Classes\LDAP\Attribute;
|
|||||||
*/
|
*/
|
||||||
final class GidNumber extends Attribute
|
final class GidNumber extends Attribute
|
||||||
{
|
{
|
||||||
|
protected(set) bool $no_attr_tags = FALSE;
|
||||||
}
|
}
|
@ -11,7 +11,8 @@ use App\Classes\LDAP\Attribute;
|
|||||||
*/
|
*/
|
||||||
abstract class Internal extends Attribute
|
abstract class Internal extends Attribute
|
||||||
{
|
{
|
||||||
protected bool $is_internal = TRUE;
|
protected(set) bool $is_internal = TRUE;
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
{
|
{
|
||||||
|
12
app/Classes/LDAP/Attribute/Internal/Etag.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/Etag.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an Etag Attribute
|
||||||
|
*/
|
||||||
|
final class Etag extends Internal
|
||||||
|
{
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/NumSubordinates.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/NumSubordinates.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an NumSubordinates Attribute
|
||||||
|
*/
|
||||||
|
final class NumSubordinates extends Internal
|
||||||
|
{
|
||||||
|
}
|
12
app/Classes/LDAP/Attribute/Internal/PwdPolicySubentry.php
Normal file
12
app/Classes/LDAP/Attribute/Internal/PwdPolicySubentry.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Internal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an PwdPolicySubentry Attribute
|
||||||
|
*/
|
||||||
|
final class PwdPolicySubentry extends Internal
|
||||||
|
{
|
||||||
|
}
|
42
app/Classes/LDAP/Attribute/KrbPrincipalKey.php
Normal file
42
app/Classes/LDAP/Attribute/KrbPrincipalKey.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Traits\MD5Updates;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose values are passwords
|
||||||
|
*/
|
||||||
|
final class KrbPrincipalKey extends Attribute
|
||||||
|
{
|
||||||
|
use MD5Updates;
|
||||||
|
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute.krbprincipalkey')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_item_old(string $dotkey): ?string
|
||||||
|
{
|
||||||
|
return parent::render_item_old($dotkey)
|
||||||
|
? str_repeat('*',16)
|
||||||
|
: NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render_item_new(string $dotkey): ?string
|
||||||
|
{
|
||||||
|
return parent::render_item_new($dotkey)
|
||||||
|
? str_repeat('*',16)
|
||||||
|
: NULL;
|
||||||
|
}
|
||||||
|
}
|
61
app/Classes/LDAP/Attribute/KrbTicketFlags.php
Normal file
61
app/Classes/LDAP/Attribute/KrbTicketFlags.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an attribute whose value is a Kerberos Ticket Flag
|
||||||
|
* See RFC4120
|
||||||
|
*/
|
||||||
|
final class KrbTicketFlags extends Attribute
|
||||||
|
{
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
|
private const DISALLOW_POSTDATED = 0x00000001;
|
||||||
|
private const DISALLOW_FORWARDABLE = 0x00000002;
|
||||||
|
private const DISALLOW_TGT_BASED = 0x00000004;
|
||||||
|
private const DISALLOW_RENEWABLE = 0x00000008;
|
||||||
|
private const DISALLOW_PROXIABLE = 0x00000010;
|
||||||
|
private const DISALLOW_DUP_SKEY = 0x00000020;
|
||||||
|
private const DISALLOW_ALL_TIX = 0x00000040;
|
||||||
|
private const REQUIRES_PRE_AUTH = 0x00000080;
|
||||||
|
private const REQUIRES_HW_AUTH = 0x00000100;
|
||||||
|
private const REQUIRES_PWCHANGE = 0x00000200;
|
||||||
|
private const DISALLOW_SVR = 0x00001000;
|
||||||
|
private const PWCHANGE_SERVICE = 0x00002000;
|
||||||
|
|
||||||
|
private static function helpers(): Collection
|
||||||
|
{
|
||||||
|
$helpers = collect([
|
||||||
|
log(self::DISALLOW_POSTDATED,2) => __('KRB_DISALLOW_POSTDATED'),
|
||||||
|
log(self::DISALLOW_FORWARDABLE,2) => __('KRB_DISALLOW_FORWARDABLE'),
|
||||||
|
log(self::DISALLOW_TGT_BASED,2) => __('KRB_DISALLOW_TGT_BASED'),
|
||||||
|
log(self::DISALLOW_RENEWABLE,2) => __('KRB_DISALLOW_RENEWABLE'),
|
||||||
|
log(self::DISALLOW_PROXIABLE,2) => __('KRB_DISALLOW_PROXIABLE'),
|
||||||
|
log(self::DISALLOW_DUP_SKEY,2) => __('KRB_DISALLOW_DUP_SKEY'),
|
||||||
|
log(self::DISALLOW_ALL_TIX,2) => __('KRB_DISALLOW_ALL_TIX'),
|
||||||
|
log(self::REQUIRES_PRE_AUTH,2) => __('KRB_REQUIRES_PRE_AUTH'),
|
||||||
|
log(self::REQUIRES_HW_AUTH,2) => __('KRB_REQUIRES_HW_AUTH'),
|
||||||
|
log(self::REQUIRES_PWCHANGE,2) => __('KRB_REQUIRES_PWCHANGE'),
|
||||||
|
log(self::DISALLOW_SVR,2) => __('KRB_DISALLOW_SVR'),
|
||||||
|
log(self::PWCHANGE_SERVICE,2) => __('KRB_PWCHANGE_SERVICE'),
|
||||||
|
])
|
||||||
|
->replace(config('pla.krb.bits',[]));
|
||||||
|
|
||||||
|
return $helpers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
return view('components.attribute.krbticketflags')
|
||||||
|
->with('o',$this)
|
||||||
|
->with('edit',$edit)
|
||||||
|
->with('old',$old)
|
||||||
|
->with('new',$new)
|
||||||
|
->with('helper',static::helpers());
|
||||||
|
}
|
||||||
|
}
|
13
app/Classes/LDAP/Attribute/NoAttrTags/Generic.php
Normal file
13
app/Classes/LDAP/Attribute/NoAttrTags/Generic.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\NoAttrTags;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an Attribute that doesnt have Lang Tags
|
||||||
|
*/
|
||||||
|
class Generic extends Attribute
|
||||||
|
{
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
}
|
@ -6,32 +6,57 @@ use Illuminate\Contracts\View\View;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute;
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an ObjectClass Attribute
|
* Represents an ObjectClass Attribute
|
||||||
*/
|
*/
|
||||||
final class ObjectClass extends Attribute
|
final class ObjectClass extends Attribute
|
||||||
{
|
{
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
// The schema ObjectClasses for this objectclass of a DN
|
// The schema ObjectClasses for this objectclass of a DN
|
||||||
protected Collection $oc_schema;
|
protected Collection $oc_schema;
|
||||||
|
|
||||||
public function __construct(string $name,array $values)
|
/**
|
||||||
|
* Create an ObjectClass Attribute
|
||||||
|
*
|
||||||
|
* @param string $dn DN this attribute is used in
|
||||||
|
* @param string $name Name of the attribute
|
||||||
|
* @param array $values Current Values
|
||||||
|
* @param array $oc The objectclasses that the DN of this attribute has (ignored for objectclasses)
|
||||||
|
*/
|
||||||
|
public function __construct(string $dn,string $name,array $values,array $oc=[])
|
||||||
{
|
{
|
||||||
parent::__construct($name,$values);
|
parent::__construct($dn,$name,$values,['top']);
|
||||||
|
|
||||||
$this->oc_schema = config('server')
|
$this->set_oc_schema($this->tagValuesOld());
|
||||||
->schema('objectclasses')
|
|
||||||
->filter(fn($item)=>$this->values->contains($item->name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get(string $key): mixed
|
public function __get(string $key): mixed
|
||||||
{
|
{
|
||||||
return match ($key) {
|
return match ($key) {
|
||||||
'structural' => $this->oc_schema->filter(fn($item) => $item->isStructural()),
|
'structural' => $this->oc_schema->filter(fn($item)=>$item->isStructural()),
|
||||||
default => parent::__get($key),
|
default => parent::__get($key),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __set(string $key,mixed $values): void
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'values':
|
||||||
|
parent::__set($key,$values);
|
||||||
|
|
||||||
|
// We need to populate oc_schema, if we are a new OC and thus dont have any old values
|
||||||
|
if (! $this->values_old->count() && $this->values->count())
|
||||||
|
$this->set_oc_schema($this->tagValues());
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: parent::__set($key,$values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is a specific value the structural objectclass
|
* Is a specific value the structural objectclass
|
||||||
*
|
*
|
||||||
@ -50,7 +75,15 @@ final class ObjectClass extends Attribute
|
|||||||
return view('components.attribute.objectclass')
|
return view('components.attribute.objectclass')
|
||||||
->with('o',$this)
|
->with('o',$this)
|
||||||
->with('edit',$edit)
|
->with('edit',$edit)
|
||||||
|
->with('langtag',Entry::TAG_NOTAG)
|
||||||
->with('old',$old)
|
->with('old',$old)
|
||||||
->with('new',$new);
|
->with('new',$new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function set_oc_schema(Collection $tv): void
|
||||||
|
{
|
||||||
|
$this->oc_schema = config('server')
|
||||||
|
->schema('objectclasses')
|
||||||
|
->filter(fn($item)=>$tv->contains($item->name));
|
||||||
|
}
|
||||||
}
|
}
|
@ -15,6 +15,9 @@ use App\Traits\MD5Updates;
|
|||||||
final class Password extends Attribute
|
final class Password extends Attribute
|
||||||
{
|
{
|
||||||
use MD5Updates;
|
use MD5Updates;
|
||||||
|
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
private const password_helpers = 'Classes/LDAP/Attribute/Password';
|
private const password_helpers = 'Classes/LDAP/Attribute/Password';
|
||||||
public const commands = 'App\\Classes\\LDAP\\Attribute\\Password\\';
|
public const commands = 'App\\Classes\\LDAP\\Attribute\\Password\\';
|
||||||
|
|
||||||
@ -85,19 +88,23 @@ final class Password extends Attribute
|
|||||||
->with('helpers',static::helpers()->map(fn($item,$key)=>['id'=>$key,'value'=>$key])->sort());
|
->with('helpers',static::helpers()->map(fn($item,$key)=>['id'=>$key,'value'=>$key])->sort());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render_item_old(int $key): ?string
|
public function render_item_old(string $dotkey): ?string
|
||||||
{
|
{
|
||||||
$pw = Arr::get($this->oldValues,$key);
|
$pw = parent::render_item_old($dotkey);
|
||||||
|
|
||||||
return $pw
|
return $pw
|
||||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
? (((($x=$this->hash($pw)) && ($x::id() === '*clear*')) ? sprintf('{%s}',$x::shortid()) : '')
|
||||||
|
.str_repeat('*',16))
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render_item_new(int $key): ?string
|
public function render_item_new(string $dotkey): ?string
|
||||||
{
|
{
|
||||||
$pw = Arr::get($this->values,$key);
|
$pw = parent::render_item_new($dotkey);
|
||||||
|
|
||||||
return $pw
|
return $pw
|
||||||
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '').str_repeat('*',16))
|
? (((($x=$this->hash($pw)) && ($x::id() !== '*clear*')) ? sprintf('{%s}',$x::shortid()) : '')
|
||||||
|
.str_repeat('*',16))
|
||||||
: NULL;
|
: NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ final class SMD5 extends Base
|
|||||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function encode(string $password,string $salt=NULL): string
|
public function encode(string $password,?string $salt=NULL): string
|
||||||
{
|
{
|
||||||
if (is_null($salt))
|
if (is_null($salt))
|
||||||
$salt = hex2bin(random_salt(self::salt));
|
$salt = hex2bin(random_salt(self::salt));
|
||||||
|
@ -12,7 +12,7 @@ final class SSHA extends Base
|
|||||||
return $source === $this->encode($compare,$this->salted_salt($source));
|
return $source === $this->encode($compare,$this->salted_salt($source));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function encode(string $password,string $salt=NULL): string
|
public function encode(string $password,?string $salt=NULL): string
|
||||||
{
|
{
|
||||||
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha1',self::salt,$salt));
|
return sprintf('{%s}%s',self::key,$this->salted_hash($password,'sha1',self::salt,$salt));
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ final class RDN extends Attribute
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hints(): array
|
public function hints(): Collection
|
||||||
{
|
{
|
||||||
return [
|
return collect([
|
||||||
'required' => __('RDN is required')
|
'required' => __('RDN is required')
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
@ -14,6 +14,7 @@ use App\Classes\LDAP\Attribute;
|
|||||||
abstract class Schema extends Attribute
|
abstract class Schema extends Attribute
|
||||||
{
|
{
|
||||||
protected bool $internal = TRUE;
|
protected bool $internal = TRUE;
|
||||||
|
protected(set) bool $no_attr_tags = TRUE;
|
||||||
|
|
||||||
protected static function _get(string $filename,string $string,string $key): ?string
|
protected static function _get(string $filename,string $string,string $key): ?string
|
||||||
{
|
{
|
||||||
@ -30,7 +31,7 @@ abstract class Schema extends Attribute
|
|||||||
while (! feof($f)) {
|
while (! feof($f)) {
|
||||||
$line = trim(fgets($f));
|
$line = trim(fgets($f));
|
||||||
|
|
||||||
if (! $line OR preg_match('/^#/',$line))
|
if ((! $line) || preg_match('/^#/',$line))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$fields = explode(':',$line);
|
$fields = explode(':',$line);
|
||||||
@ -41,12 +42,15 @@ abstract class Schema extends Attribute
|
|||||||
'desc'=>Arr::get($fields,3,__('No description available, can you help with one?')),
|
'desc'=>Arr::get($fields,3,__('No description available, can you help with one?')),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose($f);
|
fclose($f);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Arr::get(($array ? $array->get($string) : []),$key);
|
return Arr::get(($array ? $array->get($string) : []),
|
||||||
|
$key,
|
||||||
|
__('No description available, can you help with one?'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
20
app/Classes/LDAP/Attribute/Schema/Generic.php
Normal file
20
app/Classes/LDAP/Attribute/Schema/Generic.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Generic Schema Attribute
|
||||||
|
*/
|
||||||
|
class Generic extends Schema
|
||||||
|
{
|
||||||
|
public function render(bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE): View
|
||||||
|
{
|
||||||
|
// @note Schema attributes cannot be edited
|
||||||
|
return view('components.attribute.schema.generic')
|
||||||
|
->with('o',$this);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace App\Classes\LDAP\Export;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
use App\Classes\LDAP\Export;
|
use App\Classes\LDAP\Export;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export from LDAP using an LDIF format
|
* Export from LDAP using an LDIF format
|
||||||
@ -41,13 +42,25 @@ class LDIF extends Export
|
|||||||
|
|
||||||
// Display Attributes
|
// Display Attributes
|
||||||
foreach ($o->getObjects() as $ao) {
|
foreach ($o->getObjects() as $ao) {
|
||||||
foreach ($ao->values as $value) {
|
if ($ao->no_attr_tags)
|
||||||
$result .= $this->multiLineDisplay(
|
foreach ($ao->values as $value) {
|
||||||
Str::isAscii($value)
|
$result .= $this->multiLineDisplay(
|
||||||
? sprintf('%s: %s',$ao->name,$value)
|
Str::isAscii($value)
|
||||||
: sprintf('%s:: %s',$ao->name,base64_encode($value))
|
? sprintf('%s: %s',$ao->name,$value)
|
||||||
,$this->br);
|
: sprintf('%s:: %s',$ao->name,base64_encode($value))
|
||||||
}
|
,$this->br);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
foreach ($ao->values as $tag => $tagvalues) {
|
||||||
|
foreach ($tagvalues as $value) {
|
||||||
|
$result .= $this->multiLineDisplay(
|
||||||
|
Str::isAscii($value)
|
||||||
|
? sprintf('%s: %s',$ao->name.(($tag !== Entry::TAG_NOTAG) ? ';'.$tag : ''),$value)
|
||||||
|
: sprintf('%s:: %s',$ao->name.(($tag !== Entry::TAG_NOTAG) ? ';'.$tag : ''),base64_encode($value))
|
||||||
|
,$this->br);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace App\Classes\LDAP;
|
namespace App\Classes\LDAP;
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use LdapRecord\LdapRecordException;
|
||||||
|
|
||||||
use App\Exceptions\Import\GeneralException;
|
use App\Exceptions\Import\GeneralException;
|
||||||
use App\Exceptions\Import\ObjectExistsException;
|
|
||||||
use App\Ldap\Entry;
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +48,6 @@ abstract class Import
|
|||||||
* @param int $action
|
* @param int $action
|
||||||
* @return Collection
|
* @return Collection
|
||||||
* @throws GeneralException
|
* @throws GeneralException
|
||||||
* @throws ObjectExistsException
|
|
||||||
*/
|
*/
|
||||||
final protected function commit(Entry $o,int $action): Collection
|
final protected function commit(Entry $o,int $action): Collection
|
||||||
{
|
{
|
||||||
@ -57,15 +56,24 @@ abstract class Import
|
|||||||
try {
|
try {
|
||||||
$o->save();
|
$o->save();
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (LdapRecordException $e) {
|
||||||
return collect([
|
if ($e->getDetailedError())
|
||||||
'dn'=>$o->getDN(),
|
return collect([
|
||||||
'result'=>sprintf('%d: %s (%s)',
|
'dn'=>$o->getDN(),
|
||||||
($x=$e->getDetailedError())->getErrorCode(),
|
'result'=>sprintf('%d: %s (%s)',
|
||||||
$x->getErrorMessage(),
|
($x=$e->getDetailedError())->getErrorCode(),
|
||||||
$x->getDiagnosticMessage(),
|
$x->getErrorMessage(),
|
||||||
)
|
$x->getDiagnosticMessage(),
|
||||||
]);
|
)
|
||||||
|
]);
|
||||||
|
else
|
||||||
|
return collect([
|
||||||
|
'dn'=>$o->getDN(),
|
||||||
|
'result'=>sprintf('%d: %s',
|
||||||
|
$e->getCode(),
|
||||||
|
$e->getMessage(),
|
||||||
|
)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
|
||||||
|
@ -46,7 +46,7 @@ class LDIF extends Import
|
|||||||
if (! $line) {
|
if (! $line) {
|
||||||
if (! is_null($o)) {
|
if (! is_null($o)) {
|
||||||
// Add the last attribute;
|
// Add the last attribute;
|
||||||
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
@ -59,8 +59,6 @@ class LDIF extends Import
|
|||||||
$base64encoded = FALSE;
|
$base64encoded = FALSE;
|
||||||
$attribute = NULL;
|
$attribute = NULL;
|
||||||
$value = '';
|
$value = '';
|
||||||
|
|
||||||
// Else its a blank line
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -69,7 +67,7 @@ class LDIF extends Import
|
|||||||
$m = [];
|
$m = [];
|
||||||
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
|
||||||
|
|
||||||
switch ($x=Arr::get($m,1)) {
|
switch (Arr::get($m,1)) {
|
||||||
case 'changetype':
|
case 'changetype':
|
||||||
if ($m[2] !== ':')
|
if ($m[2] !== ':')
|
||||||
throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
|
||||||
@ -125,7 +123,7 @@ class LDIF extends Import
|
|||||||
Log::debug(sprintf('%s: Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
Log::debug(sprintf('%s: Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
|
||||||
|
|
||||||
if ($value)
|
if ($value)
|
||||||
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
else
|
else
|
||||||
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
|
||||||
}
|
}
|
||||||
@ -133,7 +131,6 @@ class LDIF extends Import
|
|||||||
|
|
||||||
// Start of a new attribute
|
// Start of a new attribute
|
||||||
$base64encoded = ($m[2] === '::');
|
$base64encoded = ($m[2] === '::');
|
||||||
// @todo Need to parse attributes with ';' options
|
|
||||||
$attribute = $m[1];
|
$attribute = $m[1];
|
||||||
$value = $m[3];
|
$value = $m[3];
|
||||||
|
|
||||||
@ -147,7 +144,7 @@ class LDIF extends Import
|
|||||||
// We may still have a pending action
|
// We may still have a pending action
|
||||||
if ($action) {
|
if ($action) {
|
||||||
// Add the last attribute;
|
// Add the last attribute;
|
||||||
$o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
|
$o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value);
|
||||||
|
|
||||||
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
|
||||||
|
|
||||||
@ -159,8 +156,8 @@ class LDIF extends Import
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function readEntry() {
|
public function xreadEntry() {
|
||||||
static $haveVersion = false;
|
static $haveVersion = FALSE;
|
||||||
|
|
||||||
if ($lines = $this->nextLines()) {
|
if ($lines = $this->nextLines()) {
|
||||||
|
|
||||||
@ -179,7 +176,7 @@ class LDIF extends Import
|
|||||||
} else
|
} else
|
||||||
$changetype = 'add';
|
$changetype = 'add';
|
||||||
|
|
||||||
$this->template = new Template($this->server_id,null,null,$changetype);
|
$this->template = new Template($this->server_id,NULL,NULL,$changetype);
|
||||||
|
|
||||||
switch ($changetype) {
|
switch ($changetype) {
|
||||||
case 'add':
|
case 'add':
|
||||||
@ -201,7 +198,7 @@ class LDIF extends Import
|
|||||||
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
|
||||||
|
|
||||||
$this->template->setDN($dn);
|
$this->template->setDN($dn);
|
||||||
$this->template->accept(false,true);
|
$this->template->accept(FALSE,TRUE);
|
||||||
|
|
||||||
return $this->getModifyDetails($lines);
|
return $this->getModifyDetails($lines);
|
||||||
|
|
||||||
@ -221,13 +218,13 @@ class LDIF extends Import
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
if (! $server->dnExists($dn))
|
if (! $server->dnExists($dn))
|
||||||
return $this->error(_('Unkown change type'),$lines);
|
return $this->error(_('Unknown change type'),$lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else
|
} else
|
||||||
return $this->error(_('A valid dn line is required'),$lines);
|
return $this->error(_('A valid dn line is required'),$lines);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
return false;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,9 @@ use Illuminate\Support\Arr;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\LDAP\Attribute;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an LDAP AttributeType
|
* Represents an LDAP AttributeType
|
||||||
*
|
*
|
||||||
@ -320,11 +323,12 @@ final class AttributeType extends Base {
|
|||||||
* that is the list of objectClasses which must have this attribute.
|
* that is the list of objectClasses which must have this attribute.
|
||||||
*
|
*
|
||||||
* @param string $name The name of the objectClass to add.
|
* @param string $name The name of the objectClass to add.
|
||||||
|
* @param bool $structural
|
||||||
*/
|
*/
|
||||||
public function addRequiredByObjectClass(string $name): void
|
public function addRequiredByObjectClass(string $name,bool $structural): void
|
||||||
{
|
{
|
||||||
if (! $this->required_by_object_classes->contains($name))
|
if (! $this->required_by_object_classes->has($name))
|
||||||
$this->required_by_object_classes->push($name);
|
$this->required_by_object_classes->put($name,$structural);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -332,6 +336,7 @@ final class AttributeType extends Base {
|
|||||||
* that is the list of objectClasses which provide this attribute.
|
* that is the list of objectClasses which provide this attribute.
|
||||||
*
|
*
|
||||||
* @param string $name The name of the objectClass to add.
|
* @param string $name The name of the objectClass to add.
|
||||||
|
* @param bool $structural
|
||||||
*/
|
*/
|
||||||
public function addUsedInObjectClass(string $name,bool $structural): void
|
public function addUsedInObjectClass(string $name,bool $structural): void
|
||||||
{
|
{
|
||||||
@ -339,6 +344,11 @@ final class AttributeType extends Base {
|
|||||||
$this->used_in_object_classes->put($name,$structural);
|
$this->used_in_object_classes->put($name,$structural);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function factory(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute\Factory::create(dn:'',attribute:$this->name,values:[]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the names of attributes that are an alias for this attribute (if any).
|
* Gets the names of attributes that are an alias for this attribute (if any).
|
||||||
*
|
*
|
||||||
@ -476,6 +486,28 @@ final class AttributeType extends Base {
|
|||||||
return $this->used_in_object_classes;
|
return $this->used_in_object_classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a list of objectclasses return all parent objectclasses as well
|
||||||
|
*
|
||||||
|
* @param Collection $ocs
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function heirachy(Collection $ocs): Collection
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach ($ocs as $oc) {
|
||||||
|
$schema = config('server')
|
||||||
|
->schema('objectclasses',$oc)
|
||||||
|
->getParents(TRUE)
|
||||||
|
->pluck('name');
|
||||||
|
|
||||||
|
$result = $result->merge($schema)->push($oc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool
|
* @return bool
|
||||||
* @deprecated use $this->forced_as_may
|
* @deprecated use $this->forced_as_may
|
||||||
@ -544,21 +576,22 @@ final class AttributeType extends Base {
|
|||||||
*/
|
*/
|
||||||
public function validation(array $array): ?array
|
public function validation(array $array): ?array
|
||||||
{
|
{
|
||||||
// For each item in array, we need to get the OC heirachy
|
// For each item in array, we need to get the OC hierarchy
|
||||||
$heirachy = collect($array)
|
$heirachy = $this->heirachy(collect($array)
|
||||||
->filter()
|
|
||||||
->map(fn($item)=>config('server')
|
|
||||||
->schema('objectclasses',$item)
|
|
||||||
->getSupClasses()
|
|
||||||
->push($item))
|
|
||||||
->flatten()
|
->flatten()
|
||||||
->unique();
|
->filter());
|
||||||
|
|
||||||
|
// Get any config validation
|
||||||
$validation = collect(Arr::get(config('ldap.validation'),$this->name_lc,[]));
|
$validation = collect(Arr::get(config('ldap.validation'),$this->name_lc,[]));
|
||||||
if (($heirachy->intersect($this->required_by_object_classes)->count() > 0)
|
|
||||||
|
$nolangtag = sprintf('%s.%s.0',$this->name_lc,Entry::TAG_NOTAG);
|
||||||
|
|
||||||
|
// Add in schema required by conditions
|
||||||
|
if (($heirachy->intersect($this->required_by_object_classes->keys())->count() > 0)
|
||||||
&& (! collect($validation->get($this->name_lc))->contains('required'))) {
|
&& (! collect($validation->get($this->name_lc))->contains('required'))) {
|
||||||
$validation->put($this->name_lc,array_merge(['required','min:1'],$validation->get($this->name_lc,[])))
|
$validation
|
||||||
->put($this->name_lc.'.*',array_merge(['required','min:1'],$validation->get($this->name_lc.'.*',[])));
|
->prepend(array_merge(['required','min:1'],$validation->get($nolangtag,[])),$nolangtag)
|
||||||
|
->prepend(array_merge(['required','array','min:1',($this->factory()->no_attr_tags ? 'max:1' : NULL)],$validation->get($this->name_lc,[])),$this->name_lc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $validation->toArray();
|
return $validation->toArray();
|
||||||
|
@ -209,7 +209,8 @@ final class Server
|
|||||||
/**
|
/**
|
||||||
* Obtain the rootDSE for the server, that gives us server information
|
* Obtain the rootDSE for the server, that gives us server information
|
||||||
*
|
*
|
||||||
* @param null $connection
|
* @param string|null $connection
|
||||||
|
* @param Carbon|null $cachetime
|
||||||
* @return Entry|null
|
* @return Entry|null
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
* @testedin TranslateOidTest::testRootDSE();
|
* @testedin TranslateOidTest::testRootDSE();
|
||||||
@ -230,7 +231,7 @@ final class Server
|
|||||||
/**
|
/**
|
||||||
* Get the Schema DN
|
* Get the Schema DN
|
||||||
*
|
*
|
||||||
* @param $connection
|
* @param string|null $connection
|
||||||
* @return string
|
* @return string
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
*/
|
*/
|
||||||
@ -245,16 +246,21 @@ final class Server
|
|||||||
* Query the server for a DN and return its children and if those children have children.
|
* Query the server for a DN and return its children and if those children have children.
|
||||||
*
|
*
|
||||||
* @param string $dn
|
* @param string $dn
|
||||||
|
* @param array $attrs
|
||||||
* @return LDAPCollection|NULL
|
* @return LDAPCollection|NULL
|
||||||
*/
|
*/
|
||||||
public function children(string $dn): ?LDAPCollection
|
public function children(string $dn,array $attrs=['dn']): ?LDAPCollection
|
||||||
{
|
{
|
||||||
return ($x=(new Entry)
|
return ($x=(new Entry)
|
||||||
->on($this->connection)
|
->on($this->connection)
|
||||||
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
||||||
->select(['*','hassubordinates'])
|
->select(array_merge($attrs,[
|
||||||
|
'hassubordinates', // Needed for the tree to know if an entry has children
|
||||||
|
'c' // Needed for the tree to show icons for countries
|
||||||
|
]))
|
||||||
->setDn($dn)
|
->setDn($dn)
|
||||||
->list()
|
->list()
|
||||||
|
->orderBy('dn')
|
||||||
->get()) ? $x : NULL;
|
->get()) ? $x : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,7 +434,7 @@ final class Server
|
|||||||
// Add Required By.
|
// Add Required By.
|
||||||
foreach ($must_attrs as $attr_name)
|
foreach ($must_attrs as $attr_name)
|
||||||
if ($this->attributetypes->has(strtolower($attr_name)))
|
if ($this->attributetypes->has(strtolower($attr_name)))
|
||||||
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name);
|
$this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name,$object_class->isStructural());
|
||||||
|
|
||||||
// Force May
|
// Force May
|
||||||
foreach ($object_class->getForceMayAttrs() as $attr_name)
|
foreach ($object_class->getForceMayAttrs() as $attr_name)
|
||||||
@ -528,7 +534,6 @@ final class Server
|
|||||||
*
|
*
|
||||||
* @param string $oid
|
* @param string $oid
|
||||||
* @return LDAPSyntax|null
|
* @return LDAPSyntax|null
|
||||||
* @throws InvalidUsage
|
|
||||||
*/
|
*/
|
||||||
public function schemaSyntaxName(string $oid): ?LDAPSyntax
|
public function schemaSyntaxName(string $oid): ?LDAPSyntax
|
||||||
{
|
{
|
||||||
|
@ -39,14 +39,13 @@ class APIController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function children(Request $request): Collection
|
public function children(Request $request): Collection
|
||||||
{
|
{
|
||||||
$levels = $request->query('depth',1);
|
|
||||||
$dn = Crypt::decryptString($request->query('key'));
|
$dn = Crypt::decryptString($request->query('key'));
|
||||||
|
|
||||||
// Sometimes our key has a command, so we'll ignore it
|
// Sometimes our key has a command, so we'll ignore it
|
||||||
if (str_starts_with($dn,'*') && ($x=strpos($dn,'|')))
|
if (str_starts_with($dn,'*') && ($x=strpos($dn,'|')))
|
||||||
$dn = substr($dn,$x+1);
|
$dn = substr($dn,$x+1);
|
||||||
|
|
||||||
Log::debug(sprintf('%s: Query [%s] - Levels [%d]',__METHOD__,$dn,$levels));
|
Log::debug(sprintf('%s: Query [%s]',__METHOD__,$dn));
|
||||||
|
|
||||||
return (config('server'))
|
return (config('server'))
|
||||||
->children($dn)
|
->children($dn)
|
||||||
@ -98,11 +97,10 @@ class APIController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Return the required and additional attributes for an object class
|
* Return the required and additional attributes for an object class
|
||||||
*
|
*
|
||||||
* @param Request $request
|
|
||||||
* @param string $objectclass
|
* @param string $objectclass
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function schema_objectclass_attrs(Request $request,string $objectclass): array
|
public function schema_objectclass_attrs(string $objectclass): array
|
||||||
{
|
{
|
||||||
$oc = config('server')->schema('objectclasses',$objectclass);
|
$oc = config('server')->schema('objectclasses',$objectclass);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Contracts\View\View;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -15,8 +14,8 @@ use LdapRecord\LdapRecordException;
|
|||||||
use LdapRecord\Query\ObjectNotFoundException;
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
use Nette\NotImplementedException;
|
use Nette\NotImplementedException;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute\Factory;
|
use App\Classes\LDAP\Attribute\{Factory,Password};
|
||||||
use App\Classes\LDAP\{Attribute,Server};
|
use App\Classes\LDAP\Server;
|
||||||
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
use App\Classes\LDAP\Import\LDIF as LDIFImport;
|
||||||
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
use App\Classes\LDAP\Export\LDIF as LDIFExport;
|
||||||
use App\Exceptions\Import\{GeneralException,VersionException};
|
use App\Exceptions\Import\{GeneralException,VersionException};
|
||||||
@ -42,24 +41,14 @@ class HomeController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Debug Page
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
|
||||||
*/
|
|
||||||
public function debug()
|
|
||||||
{
|
|
||||||
return view('debug');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new object in the LDAP server
|
* Create a new object in the LDAP server
|
||||||
*
|
*
|
||||||
* @param EntryAddRequest $request
|
* @param EntryAddRequest $request
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
* @return View
|
||||||
* @throws InvalidUsage
|
* @throws InvalidUsage
|
||||||
*/
|
*/
|
||||||
public function entry_add(EntryAddRequest $request)
|
public function entry_add(EntryAddRequest $request): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
if (! old('step',$request->validated('step')))
|
if (! old('step',$request->validated('step')))
|
||||||
abort(404);
|
abort(404);
|
||||||
@ -68,11 +57,12 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
$o = new Entry;
|
$o = new Entry;
|
||||||
|
|
||||||
if (count(array_filter($x=old('objectclass',$request->objectclass)))) {
|
if (count($x=array_filter(old('objectclass',$request->objectclass)))) {
|
||||||
$o->objectclass = $x;
|
$o->objectclass = $x;
|
||||||
|
|
||||||
|
// Also add in our required attributes
|
||||||
foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao)
|
foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao)
|
||||||
$o->addAttribute($ao,'');
|
$o->{$ao->name} = [Entry::TAG_NOTAG=>''];
|
||||||
|
|
||||||
$o->setRDNBase($key['dn']);
|
$o->setRDNBase($key['dn']);
|
||||||
}
|
}
|
||||||
@ -92,24 +82,26 @@ class HomeController extends Controller
|
|||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return \Closure|\Illuminate\Contracts\View\View|string
|
* @return \Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function entry_attr_add(Request $request,string $id): string
|
public function entry_attr_add(Request $request,string $id): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
$xx = new \stdClass();
|
$xx = new \stdClass;
|
||||||
$xx->index = 0;
|
$xx->index = 0;
|
||||||
|
|
||||||
$x = $request->noheader
|
$dn = $request->dn ? Crypt::decrypt($request->dn) : '';
|
||||||
? (string)view(sprintf('components.attribute.widget.%s',$id))
|
|
||||||
->with('o',new Attribute($id,[]))
|
|
||||||
->with('value',$request->value)
|
|
||||||
->with('loop',$xx)
|
|
||||||
: (new AttributeType(new Attribute($id,[]),TRUE,collect($request->oc ?: [])))->render();
|
|
||||||
|
|
||||||
return $x;
|
return $request->noheader
|
||||||
|
? view(sprintf('components.attribute.widget.%s',$id))
|
||||||
|
->with('o',Factory::create(dn: $dn,attribute: $id,values: [],oc: $request->objectclasses))
|
||||||
|
->with('value',$request->value)
|
||||||
|
->with('langtag',Entry::TAG_NOTAG)
|
||||||
|
->with('loop',$xx)
|
||||||
|
: new AttributeType(Factory::create($dn,$id,[],$request->objectclasses),new: TRUE,edit: TRUE)
|
||||||
|
->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function entry_create(EntryAddRequest $request)
|
public function entry_create(EntryAddRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
{
|
{
|
||||||
$key = $this->request_key($request,collect(old()));
|
$key = $this->request_key($request,collect(old()));
|
||||||
|
|
||||||
@ -137,7 +129,44 @@ class HomeController extends Controller
|
|||||||
abort(599,$e->getDetailedError()->getErrorMessage());
|
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo To test and valide this Exception is caught
|
// @todo when we create an entry, and it already exists, enable a redirect to it
|
||||||
|
} catch (LdapRecordException $e) {
|
||||||
|
return Redirect::back()
|
||||||
|
->withInput()
|
||||||
|
->withErrors(sprintf('%s: %s - %s: %s',
|
||||||
|
__('LDAP Server Error Code'),
|
||||||
|
$e->getDetailedError()->getErrorCode(),
|
||||||
|
__($e->getDetailedError()->getErrorMessage()),
|
||||||
|
$e->getDetailedError()->getDiagnosticMessage(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect::to('/')
|
||||||
|
->withFragment($o->getDNSecure());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entry_delete(Request $request): \Illuminate\Http\RedirectResponse
|
||||||
|
{
|
||||||
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
|
$o = config('server')->fetch($dn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$o->delete();
|
||||||
|
|
||||||
|
} catch (InsufficientAccessException $e) {
|
||||||
|
$request->flash();
|
||||||
|
|
||||||
|
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
||||||
|
case 50:
|
||||||
|
return Redirect::to('/')
|
||||||
|
->withInput()
|
||||||
|
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort(599,$e->getDetailedError()->getErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
} catch (LdapRecordException $e) {
|
} catch (LdapRecordException $e) {
|
||||||
$request->flash();
|
$request->flash();
|
||||||
|
|
||||||
@ -153,17 +182,15 @@ class HomeController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Redirect::to('/')
|
return Redirect::to('/')
|
||||||
->withFragment($o->getDNSecure());
|
->with('success',[sprintf('%s: %s',__('Deleted'),$dn)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function entry_export(Request $request,string $id)
|
public function entry_export(Request $request,string $id): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
$dn = Crypt::decryptString($id);
|
$dn = Crypt::decryptString($id);
|
||||||
|
|
||||||
$result = (new Entry)
|
$result = (new Entry)
|
||||||
->query()
|
->query()
|
||||||
//->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
|
|
||||||
//->select(['*'])
|
|
||||||
->setDn($dn)
|
->setDn($dn)
|
||||||
->recursive()
|
->recursive()
|
||||||
->get();
|
->get();
|
||||||
@ -175,12 +202,13 @@ class HomeController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Render an available list of objectclasses for an Entry
|
* Render an available list of objectclasses for an Entry
|
||||||
*
|
*
|
||||||
* @param string $id
|
* @param Request $request
|
||||||
* @return mixed
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function entry_objectclass_add(Request $request)
|
public function entry_objectclass_add(Request $request): Collection
|
||||||
{
|
{
|
||||||
$oc = Factory::create('objectclass',$request->oc);
|
$dn = $request->key ? Crypt::decryptString($request->dn) : '';
|
||||||
|
$oc = Factory::create($dn,'objectclass',$request->oc);
|
||||||
|
|
||||||
$ocs = $oc
|
$ocs = $oc
|
||||||
->structural
|
->structural
|
||||||
@ -202,7 +230,7 @@ class HomeController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function entry_password_check(Request $request)
|
public function entry_password_check(Request $request): Collection
|
||||||
{
|
{
|
||||||
$dn = Crypt::decryptString($request->dn);
|
$dn = Crypt::decryptString($request->dn);
|
||||||
$o = config('server')->fetch($dn);
|
$o = config('server')->fetch($dn);
|
||||||
@ -210,7 +238,7 @@ class HomeController extends Controller
|
|||||||
$password = $o->getObject('userpassword');
|
$password = $o->getObject('userpassword');
|
||||||
|
|
||||||
$result = collect();
|
$result = collect();
|
||||||
foreach ($password as $key => $value) {
|
foreach ($password->values->dot() as $key => $value) {
|
||||||
$hash = $password->hash($value);
|
$hash = $password->hash($value);
|
||||||
$compare = Arr::get($request->password,$key);
|
$compare = Arr::get($request->password,$key);
|
||||||
//Log::debug(sprintf('comparing [%s] with [%s] type [%s]',$value,$compare,$hash::id()),['object'=>$hash]);
|
//Log::debug(sprintf('comparing [%s] with [%s] type [%s]',$value,$compare,$hash::id()),['object'=>$hash]);
|
||||||
@ -225,10 +253,10 @@ class HomeController extends Controller
|
|||||||
* Show a confirmation to update a DN
|
* Show a confirmation to update a DN
|
||||||
*
|
*
|
||||||
* @param EntryRequest $request
|
* @param EntryRequest $request
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
*/
|
*/
|
||||||
public function entry_pending_update(EntryRequest $request)
|
public function entry_pending_update(EntryRequest $request): \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||||
{
|
{
|
||||||
$dn = Crypt::decryptString($request->dn);
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
@ -237,22 +265,27 @@ class HomeController extends Controller
|
|||||||
foreach ($request->except(['_token','dn','userpassword_hash','userpassword']) as $key => $value)
|
foreach ($request->except(['_token','dn','userpassword_hash','userpassword']) as $key => $value)
|
||||||
$o->{$key} = array_filter($value,fn($item)=>! is_null($item));
|
$o->{$key} = array_filter($value,fn($item)=>! is_null($item));
|
||||||
|
|
||||||
|
// @todo Need to handle incoming attributes that were modified by MD5Updates Trait (eg: jpegphoto)
|
||||||
|
|
||||||
// We need to process and encrypt the password
|
// We need to process and encrypt the password
|
||||||
if ($request->userpassword) {
|
if ($request->userpassword) {
|
||||||
$passwords = [];
|
$passwords = [];
|
||||||
foreach ($request->userpassword as $key => $value) {
|
$po = $o->getObject('userpassword');
|
||||||
|
foreach (Arr::dot($request->userpassword) as $dotkey => $value) {
|
||||||
// If the password is still the MD5 of the old password, then it hasnt changed
|
// If the password is still the MD5 of the old password, then it hasnt changed
|
||||||
if (($old=Arr::get($o->userpassword,$key)) && ($value === md5($old))) {
|
if (($old=Arr::get($po,$dotkey)) && ($value === md5($old))) {
|
||||||
array_push($passwords,$old);
|
$passwords[$dotkey] = $value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($value) {
|
if ($value) {
|
||||||
$type = Arr::get($request->userpassword_hash,$key);
|
$type = Arr::get($request->userpassword_hash,$dotkey);
|
||||||
array_push($passwords,Attribute\Password::hash_id($type)->encode($value));
|
$passwords[$dotkey] = Password::hash_id($type)
|
||||||
|
->encode($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$o->userpassword = $passwords;
|
|
||||||
|
$o->userpassword = Arr::undot($passwords);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $o->getDirty())
|
if (! $o->getDirty())
|
||||||
@ -272,8 +305,10 @@ class HomeController extends Controller
|
|||||||
* @param EntryRequest $request
|
* @param EntryRequest $request
|
||||||
* @return \Illuminate\Http\RedirectResponse
|
* @return \Illuminate\Http\RedirectResponse
|
||||||
* @throws ObjectNotFoundException
|
* @throws ObjectNotFoundException
|
||||||
|
* @todo When removing an attribute value, from a multi-value attribute, we have a ghost record showing after the update
|
||||||
|
* @todo Need to check when removing a single attribute value, do we have a ghost as well? Might be because we are redirecting with input?
|
||||||
*/
|
*/
|
||||||
public function entry_update(EntryRequest $request)
|
public function entry_update(EntryRequest $request): \Illuminate\Http\RedirectResponse
|
||||||
{
|
{
|
||||||
$dn = Crypt::decryptString($request->dn);
|
$dn = Crypt::decryptString($request->dn);
|
||||||
|
|
||||||
@ -304,22 +339,19 @@ class HomeController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (LdapRecordException $e) {
|
} catch (LdapRecordException $e) {
|
||||||
$request->flash();
|
return Redirect::to('/')
|
||||||
|
->withInput()
|
||||||
switch ($x=$e->getDetailedError()->getErrorCode()) {
|
->withErrors(sprintf('%s: %s - %s: %s',
|
||||||
case 8:
|
__('LDAP Server Error Code'),
|
||||||
return Redirect::to('/')
|
$e->getDetailedError()->getErrorCode(),
|
||||||
->withInput()
|
__($e->getDetailedError()->getErrorMessage()),
|
||||||
->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage())));
|
$e->getDetailedError()->getDiagnosticMessage(),
|
||||||
|
));
|
||||||
default:
|
|
||||||
abort(599,$e->getDetailedError()->getErrorMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect::to('/')
|
return Redirect::to('/')
|
||||||
->withInput()
|
->withInput()
|
||||||
->with('updated',collect($dirty)->map(fn($key,$item)=>$o->getObject($item)));
|
->with('updated',collect($dirty)->map(fn($item,$key)=>$o->getObject(collect(explode(';',$key))->first())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -328,9 +360,9 @@ class HomeController extends Controller
|
|||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param Collection|null $old
|
* @param Collection|null $old
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|View
|
* @return \Illuminate\View\View
|
||||||
*/
|
*/
|
||||||
public function frame(Request $request,?Collection $old=NULL): View
|
public function frame(Request $request,?Collection $old=NULL): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
// If our index was not render from a root url, then redirect to it
|
// If our index was not render from a root url, then redirect to it
|
||||||
if (($request->root().'/' !== url()->previous()) && $request->method() === 'POST')
|
if (($request->root().'/' !== url()->previous()) && $request->method() === 'POST')
|
||||||
@ -343,6 +375,14 @@ class HomeController extends Controller
|
|||||||
: view('frames.'.$key['cmd']))
|
: view('frames.'.$key['cmd']))
|
||||||
->with('bases',$this->bases());
|
->with('bases',$this->bases());
|
||||||
|
|
||||||
|
// If we are rendering a DN, rebuild our object
|
||||||
|
if ($key['dn']) {
|
||||||
|
$o = config('server')->fetch($key['dn']);
|
||||||
|
|
||||||
|
foreach (collect(old())->except(['key','dn','step','_token','userpassword_hash']) as $attr => $value)
|
||||||
|
$o->{$attr} = $value;
|
||||||
|
}
|
||||||
|
|
||||||
return match ($key['cmd']) {
|
return match ($key['cmd']) {
|
||||||
'create' => $view
|
'create' => $view
|
||||||
->with('container',old('container',$key['dn']))
|
->with('container',old('container',$key['dn']))
|
||||||
@ -350,7 +390,14 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
'dn' => $view
|
'dn' => $view
|
||||||
->with('dn',$key['dn'])
|
->with('dn',$key['dn'])
|
||||||
->with('page_actions',collect(['edit'=>TRUE,'copy'=>TRUE])),
|
->with('o',$o)
|
||||||
|
->with('page_actions',collect([
|
||||||
|
'copy'=>FALSE,
|
||||||
|
'create'=>FALSE,
|
||||||
|
'delete'=>TRUE,
|
||||||
|
'edit'=>TRUE,
|
||||||
|
'export'=>TRUE,
|
||||||
|
])),
|
||||||
|
|
||||||
'import' => $view,
|
'import' => $view,
|
||||||
|
|
||||||
@ -361,7 +408,7 @@ class HomeController extends Controller
|
|||||||
/**
|
/**
|
||||||
* This is the main page render function
|
* This is the main page render function
|
||||||
*/
|
*/
|
||||||
public function home(Request $request)
|
public function home(Request $request): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
// Did we come here as a result of a redirect
|
// Did we come here as a result of a redirect
|
||||||
return count(old())
|
return count(old())
|
||||||
@ -375,11 +422,11 @@ class HomeController extends Controller
|
|||||||
*
|
*
|
||||||
* @param ImportRequest $request
|
* @param ImportRequest $request
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
|
* @return \Illuminate\View\View
|
||||||
* @throws GeneralException
|
* @throws GeneralException
|
||||||
* @throws VersionException
|
* @throws VersionException
|
||||||
*/
|
*/
|
||||||
public function import(ImportRequest $request,string $type)
|
public function import(ImportRequest $request,string $type): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'ldif':
|
case 'ldif':
|
||||||
@ -407,22 +454,6 @@ class HomeController extends Controller
|
|||||||
->with('ldif',htmlspecialchars($x));
|
->with('ldif',htmlspecialchars($x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function import_frame()
|
|
||||||
{
|
|
||||||
return view('frames.import');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LDAP Server INFO
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
|
||||||
*/
|
|
||||||
public function info()
|
|
||||||
{
|
|
||||||
return view('frames.info')
|
|
||||||
->with('s',config('server'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For any incoming request, work out the command and DN involved
|
* For any incoming request, work out the command and DN involved
|
||||||
*
|
*
|
||||||
@ -461,10 +492,10 @@ class HomeController extends Controller
|
|||||||
*
|
*
|
||||||
* @note Our route will validate that types are valid.
|
* @note Our route will validate that types are valid.
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
* @return \Illuminate\View\View
|
||||||
* @throws InvalidUsage
|
* @throws InvalidUsage
|
||||||
*/
|
*/
|
||||||
public function schema_frame(Request $request)
|
public function schema_frame(Request $request): \Illuminate\View\View
|
||||||
{
|
{
|
||||||
// If an invalid key, we'll 404
|
// If an invalid key, we'll 404
|
||||||
if ($request->type && $request->key && (! config('server')->schema($request->type)->has($request->key)))
|
if ($request->type && $request->key && (! config('server')->schema($request->type)->has($request->key)))
|
||||||
@ -490,9 +521,9 @@ class HomeController extends Controller
|
|||||||
* Return the image for the logged in user or anonymous
|
* Return the image for the logged in user or anonymous
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @return mixed
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function user_image(Request $request)
|
public function user_image(Request $request): \Illuminate\Http\Response
|
||||||
{
|
{
|
||||||
$image = NULL;
|
$image = NULL;
|
||||||
$content = NULL;
|
$content = NULL;
|
||||||
@ -510,4 +541,4 @@ class HomeController extends Controller
|
|||||||
return response($image)
|
return response($image)
|
||||||
->header('Content-Type',$content);
|
->header('Content-Type',$content);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,6 +25,7 @@ class ApplicationSession
|
|||||||
{
|
{
|
||||||
Config::set('server',new Server);
|
Config::set('server',new Server);
|
||||||
|
|
||||||
|
view()->share('server', Config::get('server'));
|
||||||
view()->share('user', auth()->user() ?: new User);
|
view()->share('user', auth()->user() ?: new User);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
@ -25,6 +25,9 @@ class SwapinAuthUser
|
|||||||
{
|
{
|
||||||
$key = config('ldap.default');
|
$key = config('ldap.default');
|
||||||
|
|
||||||
|
if (! array_key_exists($key,config('ldap.connections')))
|
||||||
|
abort(599,sprintf('LDAP default server [%s] configuration doesnt exist?',$key));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Rebuild our connection with the authenticated user.
|
// Rebuild our connection with the authenticated user.
|
||||||
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
|
||||||
|
@ -34,10 +34,11 @@ class EntryAddRequest extends FormRequest
|
|||||||
if (request()->method() === 'GET')
|
if (request()->method() === 'GET')
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
|
$r = request() ?: collect();
|
||||||
return config('server')
|
return config('server')
|
||||||
->schema('attributetypes')
|
->schema('attributetypes')
|
||||||
->intersectByKeys($this->request)
|
->intersectByKeys($r->all())
|
||||||
->map(fn($item)=>$item->validation(request()->get('objectclass')))
|
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||||
->filter()
|
->filter()
|
||||||
->flatMap(fn($item)=>$item)
|
->flatMap(fn($item)=>$item)
|
||||||
->merge([
|
->merge([
|
||||||
@ -60,6 +61,12 @@ class EntryAddRequest extends FormRequest
|
|||||||
'rdn_value' => 'required_if:step,2|string|min:1',
|
'rdn_value' => 'required_if:step,2|string|min:1',
|
||||||
'step' => 'int|min:1|max:2',
|
'step' => 'int|min:1|max:2',
|
||||||
'objectclass'=>[
|
'objectclass'=>[
|
||||||
|
'required',
|
||||||
|
'array',
|
||||||
|
'min:1',
|
||||||
|
'max:1',
|
||||||
|
],
|
||||||
|
'objectclass._null_'=>[
|
||||||
'required',
|
'required',
|
||||||
'array',
|
'array',
|
||||||
'min:1',
|
'min:1',
|
||||||
|
@ -13,10 +13,12 @@ class EntryRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
|
$r = request() ?: collect();
|
||||||
|
|
||||||
return config('server')
|
return config('server')
|
||||||
->schema('attributetypes')
|
->schema('attributetypes')
|
||||||
->intersectByKeys($this->request)
|
->intersectByKeys($r->all())
|
||||||
->map(fn($item)=>$item->validation(request()?->get('objectclass') ?: []))
|
->map(fn($item)=>$item->validation($r->get('objectclass',[])))
|
||||||
->filter()
|
->filter()
|
||||||
->flatMap(fn($item)=>$item)
|
->flatMap(fn($item)=>$item)
|
||||||
->toArray();
|
->toArray();
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Ldap;
|
namespace App\Ldap;
|
||||||
|
|
||||||
|
use LdapRecord\Configuration\DomainConfiguration;
|
||||||
use LdapRecord\Connection as ConnectionBase;
|
use LdapRecord\Connection as ConnectionBase;
|
||||||
use LdapRecord\LdapInterface;
|
use LdapRecord\LdapInterface;
|
||||||
|
|
||||||
class Connection extends ConnectionBase
|
class Connection extends ConnectionBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct($config = [], LdapInterface $ldap = null)
|
public function __construct(DomainConfiguration|array $config=[],?LdapInterface $ldap=NULL)
|
||||||
{
|
{
|
||||||
parent::__construct($config,$ldap);
|
parent::__construct($config,$ldap);
|
||||||
|
|
||||||
|
@ -14,9 +14,20 @@ use App\Classes\LDAP\Export\LDIF;
|
|||||||
use App\Exceptions\Import\AttributeException;
|
use App\Exceptions\Import\AttributeException;
|
||||||
use App\Exceptions\InvalidUsage;
|
use App\Exceptions\InvalidUsage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Entry in an LDAP server
|
||||||
|
*
|
||||||
|
* @notes https://ldap.com/ldap-dns-and-rdns
|
||||||
|
*/
|
||||||
class Entry extends Model
|
class Entry extends Model
|
||||||
{
|
{
|
||||||
|
private const TAG_CHARS = 'a-zA-Z0-9-';
|
||||||
|
private const TAG_CHARS_LANG = 'lang-['.self::TAG_CHARS.']';
|
||||||
|
public const TAG_NOTAG = '_null_';
|
||||||
|
|
||||||
|
// Our Attribute objects
|
||||||
private Collection $objects;
|
private Collection $objects;
|
||||||
|
/* @deprecated */
|
||||||
private bool $noObjectAttributes = FALSE;
|
private bool $noObjectAttributes = FALSE;
|
||||||
// For new entries, this is the container that this entry will be stored in
|
// For new entries, this is the container that this entry will be stored in
|
||||||
private string $rdnbase;
|
private string $rdnbase;
|
||||||
@ -43,13 +54,18 @@ class Entry extends Model
|
|||||||
/**
|
/**
|
||||||
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
|
* This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
|
||||||
*
|
*
|
||||||
|
* This returns an array that should be consistent with $this->attributes
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @note $this->attributes may not be updated with changes
|
|
||||||
*/
|
*/
|
||||||
public function getAttributes(): array
|
public function getAttributes(): array
|
||||||
{
|
{
|
||||||
return $this->objects
|
return $this->objects
|
||||||
->map(fn($item)=>$item->values)
|
->flatMap(fn($item)=>
|
||||||
|
($item->no_attr_tags)
|
||||||
|
? [strtolower($item->name)=>$item->values]
|
||||||
|
: $item->values
|
||||||
|
->flatMap(fn($v,$k)=>[strtolower($item->name.($k !== self::TAG_NOTAG ? ';'.$k : ''))=>$v]))
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,12 +78,10 @@ class Entry extends Model
|
|||||||
{
|
{
|
||||||
$key = $this->normalizeAttributeKey($key);
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
|
||||||
// @todo Silently ignore keys of language tags - we should work with them
|
list($attribute,$tag) = $this->keytag($key);
|
||||||
if (str_contains($key,';'))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
return ((! array_key_exists($key,$this->original)) && (! $this->objects->has($key)))
|
return ((! array_key_exists($key,$this->original)) && (! $this->objects->has($attribute)))
|
||||||
|| (! $this->getObject($key)->isDirty());
|
|| (! $this->getObject($attribute)->isDirty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function query(bool $noattrs=false): Builder
|
public static function query(bool $noattrs=false): Builder
|
||||||
@ -82,24 +96,26 @@ class Entry extends Model
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* As attribute values are updated, or new ones created, we need to mirror that
|
* As attribute values are updated, or new ones created, we need to mirror that
|
||||||
* into our $objects
|
* into our $objects. This is called when we $o->key = $value
|
||||||
|
*
|
||||||
|
* This function should update $this->attributes and correctly reflect changes in $this->objects
|
||||||
*
|
*
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAttribute(string $key, mixed $value): static
|
public function setAttribute(string $key,mixed $value): static
|
||||||
{
|
{
|
||||||
parent::setAttribute($key,$value);
|
foreach ($value as $k => $v)
|
||||||
|
parent::setAttribute($key.($k !== self::TAG_NOTAG ? ';'.$k : ''),$v);
|
||||||
|
|
||||||
$key = $this->normalizeAttributeKey($key);
|
$key = $this->normalizeAttributeKey($key);
|
||||||
|
list($attribute,$tags) = $this->keytag($key);
|
||||||
|
|
||||||
if ((! $this->objects->get($key)) && $value) {
|
$o = $this->objects->get($attribute) ?: Factory::create($this->dn ?: '',$attribute,[],Arr::get($this->attributes,'objectclass',[]));
|
||||||
$this->objects->put($key,Factory::create($key,$value));
|
$o->values = collect($value);
|
||||||
|
|
||||||
} elseif ($this->objects->get($key)) {
|
$this->objects->put($key,$o);
|
||||||
$this->objects->get($key)->value = $this->attributes[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -133,68 +149,105 @@ class Entry extends Model
|
|||||||
* Return a key to use for sorting
|
* Return a key to use for sorting
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
* @todo This should be the DN in reverse order
|
|
||||||
*/
|
*/
|
||||||
public function getSortKeyAttribute(): string
|
public function getSortKeyAttribute(): string
|
||||||
{
|
{
|
||||||
return $this->getDn();
|
return collect(explode(',',$this->getDn()))->reverse()->join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
public function addAttribute(string $key,mixed $value): void
|
/**
|
||||||
|
* Add an attribute to this entry, if the attribute already exists, then we'll add the value to the existing item.
|
||||||
|
*
|
||||||
|
* This is primarily used by LDIF imports, where attributes have multiple entries over multiple lines
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @return void
|
||||||
|
* @throws AttributeException
|
||||||
|
* @note Attributes added this way dont have objectclass information, and the Model::attributes are not populated
|
||||||
|
*/
|
||||||
|
public function addAttributeItem(string $key,mixed $value): void
|
||||||
{
|
{
|
||||||
// While $value is mixed, it can only be a string
|
// While $value is mixed, it can only be a string
|
||||||
if (! is_string($value))
|
if (! is_string($value))
|
||||||
throw new \Exception('value should be a string');
|
throw new \Exception('value should be a string');
|
||||||
|
|
||||||
$key = $this->normalizeAttributeKey($key);
|
$key = $this->normalizeAttributeKey(strtolower($key));
|
||||||
|
|
||||||
if (! config('server')->schema('attributetypes')->has($key))
|
// If the attribute name has tags
|
||||||
throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$key));
|
list($attribute,$tag) = $this->keytag($key);
|
||||||
|
|
||||||
if ($x=$this->objects->get($key)) {
|
if (! config('server')->schema('attributetypes')->has($attribute))
|
||||||
$x->addValue($value);
|
throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$attribute));
|
||||||
|
|
||||||
} else {
|
$o = $this->objects->get($attribute) ?: Attribute\Factory::create($this->dn ?: '',$attribute,[]);
|
||||||
$this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
|
$o->addValue($tag,[$value]);
|
||||||
|
|
||||||
|
$this->objects->put($attribute,$o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export this record
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $scope
|
||||||
|
* @return string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function export(string $method,string $scope): string
|
||||||
|
{
|
||||||
|
// @todo To implement
|
||||||
|
switch ($scope) {
|
||||||
|
case 'base':
|
||||||
|
case 'one':
|
||||||
|
case 'sub':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export scope unknown:'.$scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($method) {
|
||||||
|
case 'ldif':
|
||||||
|
return new LDIF(collect($this));
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Export method not implemented:'.$method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert all our attribute values into an array of Objects
|
* Convert all our attribute values into an array of Objects
|
||||||
*
|
*
|
||||||
* @param array $attributes
|
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getAttributesAsObjects(): Collection
|
private function getAttributesAsObjects(): Collection
|
||||||
{
|
{
|
||||||
$result = collect();
|
$result = collect();
|
||||||
|
$entry_oc = Arr::get($this->attributes,'objectclass',[]);
|
||||||
|
|
||||||
foreach ($this->attributes as $attribute => $value) {
|
foreach ($this->attributes as $attrtag => $values) {
|
||||||
// If the attribute name has language tags
|
list($attribute,$tags) = $this->keytag($attrtag);
|
||||||
$matches = [];
|
|
||||||
if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
|
|
||||||
$attribute = $matches[1];
|
|
||||||
|
|
||||||
// If the attribute doesnt exist we'll create it
|
$orig = Arr::get($this->original,$attrtag,[]);
|
||||||
$o = Arr::get($result,$attribute,Factory::create($attribute,[]));
|
|
||||||
$o->setLangTag($matches[3],$value);
|
|
||||||
|
|
||||||
} else {
|
// If the attribute doesnt exist we'll create it
|
||||||
$o = Factory::create($attribute,$value);
|
$o = Arr::get(
|
||||||
}
|
$result,
|
||||||
|
$attribute,
|
||||||
|
Factory::create(
|
||||||
|
$this->dn,
|
||||||
|
$attribute,
|
||||||
|
[$tags=>$orig],
|
||||||
|
$entry_oc,
|
||||||
|
));
|
||||||
|
|
||||||
if (! $result->has($attribute)) {
|
$o->addValue($tags,$values);
|
||||||
// Set the rdn flag
|
$o->addValueOld($tags,Arr::get($this->original,$attrtag));
|
||||||
if (preg_match('/^'.$attribute.'=/i',$this->dn))
|
|
||||||
$o->setRDN();
|
|
||||||
|
|
||||||
// Store our original value to know if this attribute has changed
|
$result->put($attribute,$o);
|
||||||
$o->oldValues(Arr::get($this->original,$attribute,[]));
|
|
||||||
|
|
||||||
$result->put($attribute,$o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sort = collect(config('pla.attr_display_order',[]))->map(fn($item)=>strtolower($item));
|
$sort = collect(config('pla.attr_display_order',[]))->map(fn($item)=>strtolower($item));
|
||||||
@ -235,7 +288,7 @@ class Entry extends Model
|
|||||||
{
|
{
|
||||||
$result = collect();
|
$result = collect();
|
||||||
|
|
||||||
foreach ($this->objectclass as $oc)
|
foreach ($this->getObject('objectclass')->values as $oc)
|
||||||
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
|
$result = $result->merge(config('server')->schema('objectclasses',$oc)->attributes);
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -262,6 +315,38 @@ class Entry extends Model
|
|||||||
->filter(fn($item)=>$item->is_internal);
|
->filter(fn($item)=>$item->is_internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identify the language tags (RFC 3866) used by this entry
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getLangTags(): Collection
|
||||||
|
{
|
||||||
|
return $this->getObjects()
|
||||||
|
->filter(fn($item)=>! $item->no_attr_tags)
|
||||||
|
->map(fn($item)=>$item
|
||||||
|
->values
|
||||||
|
->keys()
|
||||||
|
->filter(fn($item)=>preg_match(sprintf('/%s+;?/',self::TAG_CHARS_LANG),$item))
|
||||||
|
->map(fn($item)=>preg_replace('/lang-/','',$item))
|
||||||
|
)
|
||||||
|
->filter(fn($item)=>$item->count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of all the items with lang tags, which ones have more than 1 lang tag
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getLangMultiTags(): Collection
|
||||||
|
{
|
||||||
|
return $this->getLangTags()
|
||||||
|
->map(fn($item)=>$item->values()
|
||||||
|
->map(fn($item)=>explode(';',$item))
|
||||||
|
->filter(fn($item)=>count($item) > 1))
|
||||||
|
->filter(fn($item)=>$item->count());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an attribute as an object
|
* Get an attribute as an object
|
||||||
*
|
*
|
||||||
@ -287,10 +372,36 @@ class Entry extends Model
|
|||||||
return $this->objects;
|
return $this->objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find other attribute tags used by this entry
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getOtherTags(): Collection
|
||||||
|
{
|
||||||
|
return $this->getObjects()
|
||||||
|
->filter(fn($item)=>! $item->no_attr_tags)
|
||||||
|
->map(fn($item)=>$item
|
||||||
|
->values
|
||||||
|
->keys()
|
||||||
|
->filter(fn($item)=>
|
||||||
|
$item && collect(explode(';',$item))->filter(
|
||||||
|
fn($item)=>
|
||||||
|
(! preg_match(sprintf('/^%s$/',self::TAG_NOTAG),$item))
|
||||||
|
&& (! preg_match(sprintf('/^%s+$/',self::TAG_CHARS_LANG),$item))
|
||||||
|
)
|
||||||
|
->count())
|
||||||
|
)
|
||||||
|
->filter(fn($item)=>$item->count());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a list of attributes without any values
|
* Return a list of attributes without any values
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
|
* @todo Dont show attributes that are not provided by an objectclass, make a new function to show those
|
||||||
|
* This is for dynamic list items eg: labeledURI, which are not editable.
|
||||||
|
* We can highlight those values that are as a result of a dynamic module
|
||||||
*/
|
*/
|
||||||
public function getMissingAttributes(): Collection
|
public function getMissingAttributes(): Collection
|
||||||
{
|
{
|
||||||
@ -300,8 +411,8 @@ class Entry extends Model
|
|||||||
|
|
||||||
private function getRDNObject(): Attribute\RDN
|
private function getRDNObject(): Attribute\RDN
|
||||||
{
|
{
|
||||||
$o = new Attribute\RDN('dn',['']);
|
$o = new Attribute\RDN('','dn',['']);
|
||||||
// @todo for an existing object, return the base.
|
// @todo for an existing object, rdnbase would be null, so dynamically get it from the DN.
|
||||||
$o->setBase($this->rdnbase);
|
$o->setBase($this->rdnbase);
|
||||||
$o->setAttributes($this->getAvailableAttributes()->filter(fn($item)=>$item->required));
|
$o->setAttributes($this->getAvailableAttributes()->filter(fn($item)=>$item->required));
|
||||||
|
|
||||||
@ -311,12 +422,22 @@ class Entry extends Model
|
|||||||
/**
|
/**
|
||||||
* Return this list of user attributes
|
* Return this list of user attributes
|
||||||
*
|
*
|
||||||
|
* @param string $tag If null return all tags
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getVisibleAttributes(): Collection
|
public function getVisibleAttributes(string $tag=''): Collection
|
||||||
{
|
{
|
||||||
return $this->objects
|
static $cache = [];
|
||||||
->filter(fn($item)=>! $item->is_internal);
|
|
||||||
|
if (! Arr::get($cache,$tag ?: '_all_')) {
|
||||||
|
$ot = $this->getOtherTags();
|
||||||
|
|
||||||
|
$cache[$tag ?: '_all_'] = $this->objects
|
||||||
|
->filter(fn($item)=>(! $item->is_internal) && ((! $item->no_attr_tags) || (! $tag) || ($tag === Entry::TAG_NOTAG)))
|
||||||
|
->filter(fn($item)=>(! $tag) || $ot->has($item->name_lc) || count($item->tagValues($tag)) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cache[$tag ?: '_all_'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasAttribute(int|string $key): bool
|
public function hasAttribute(int|string $key): bool
|
||||||
@ -325,36 +446,6 @@ class Entry extends Model
|
|||||||
->has($key);
|
->has($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Export this record
|
|
||||||
*
|
|
||||||
* @param string $method
|
|
||||||
* @param string $scope
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function export(string $method,string $scope): string
|
|
||||||
{
|
|
||||||
// @todo To implement
|
|
||||||
switch ($scope) {
|
|
||||||
case 'base':
|
|
||||||
case 'one':
|
|
||||||
case 'sub':
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception('Export scope unknown:'.$scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($method) {
|
|
||||||
case 'ldif':
|
|
||||||
return new LDIF(collect($this));
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception('Export method not implemented:'.$method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an icon for a DN based on objectClass
|
* Return an icon for a DN based on objectClass
|
||||||
*
|
*
|
||||||
@ -362,69 +453,97 @@ class Entry extends Model
|
|||||||
*/
|
*/
|
||||||
public function icon(): string
|
public function icon(): string
|
||||||
{
|
{
|
||||||
$objectclasses = array_map('strtolower',$this->objectclass);
|
$objectclasses = $this->getObject('objectclass')
|
||||||
|
->tagValues()
|
||||||
|
->map(fn($item)=>strtolower($item));
|
||||||
|
|
||||||
// Return icon based upon objectClass value
|
// Return icon based upon objectClass value
|
||||||
if (in_array('person',$objectclasses) ||
|
if ($objectclasses->intersect([
|
||||||
in_array('organizationalperson',$objectclasses) ||
|
'account',
|
||||||
in_array('inetorgperson',$objectclasses) ||
|
'inetorgperson',
|
||||||
in_array('account',$objectclasses) ||
|
'organizationalperson',
|
||||||
in_array('posixaccount',$objectclasses))
|
'person',
|
||||||
|
'posixaccount',
|
||||||
|
])->count())
|
||||||
return 'fas fa-user';
|
return 'fas fa-user';
|
||||||
|
|
||||||
elseif (in_array('organization',$objectclasses))
|
elseif ($objectclasses->contains('organization'))
|
||||||
return 'fas fa-university';
|
return 'fas fa-university';
|
||||||
|
|
||||||
elseif (in_array('organizationalunit',$objectclasses))
|
elseif ($objectclasses->contains('organizationalunit'))
|
||||||
return 'fas fa-object-group';
|
return 'fas fa-object-group';
|
||||||
|
|
||||||
elseif (in_array('posixgroup',$objectclasses) ||
|
elseif ($objectclasses->intersect([
|
||||||
in_array('groupofnames',$objectclasses) ||
|
'posixgroup',
|
||||||
in_array('groupofuniquenames',$objectclasses) ||
|
'groupofnames',
|
||||||
in_array('group',$objectclasses))
|
'groupofuniquenames',
|
||||||
|
'group',
|
||||||
|
])->count())
|
||||||
return 'fas fa-users';
|
return 'fas fa-users';
|
||||||
|
|
||||||
elseif (in_array('dcobject',$objectclasses) ||
|
elseif ($objectclasses->intersect([
|
||||||
in_array('domainrelatedobject',$objectclasses) ||
|
'dcobject',
|
||||||
in_array('domain',$objectclasses) ||
|
'domainrelatedobject',
|
||||||
in_array('builtindomain',$objectclasses))
|
'domain',
|
||||||
|
'builtindomain',
|
||||||
|
])->count())
|
||||||
return 'fas fa-network-wired';
|
return 'fas fa-network-wired';
|
||||||
|
|
||||||
elseif (in_array('alias',$objectclasses))
|
elseif ($objectclasses->contains('alias'))
|
||||||
return 'fas fa-theater-masks';
|
return 'fas fa-theater-masks';
|
||||||
|
|
||||||
elseif (in_array('country',$objectclasses))
|
elseif ($objectclasses->contains('country'))
|
||||||
return sprintf('flag %s',strtolower(Arr::get($this->c,0)));
|
return sprintf('flag %s',strtolower(Arr::get($this->c ?: [],0)));
|
||||||
|
|
||||||
elseif (in_array('device',$objectclasses))
|
elseif ($objectclasses->contains('device'))
|
||||||
return 'fas fa-mobile-alt';
|
return 'fas fa-mobile-alt';
|
||||||
|
|
||||||
elseif (in_array('document',$objectclasses))
|
elseif ($objectclasses->contains('document'))
|
||||||
return 'fas fa-file-alt';
|
return 'fas fa-file-alt';
|
||||||
|
|
||||||
elseif (in_array('iphost',$objectclasses))
|
elseif ($objectclasses->contains('iphost'))
|
||||||
return 'fas fa-wifi';
|
return 'fas fa-wifi';
|
||||||
|
|
||||||
elseif (in_array('room',$objectclasses))
|
elseif ($objectclasses->contains('room'))
|
||||||
return 'fas fa-door-open';
|
return 'fas fa-door-open';
|
||||||
|
|
||||||
elseif (in_array('server',$objectclasses))
|
elseif ($objectclasses->contains('server'))
|
||||||
return 'fas fa-server';
|
return 'fas fa-server';
|
||||||
|
|
||||||
elseif (in_array('openldaprootdse',$objectclasses))
|
elseif ($objectclasses->contains('openldaprootdse'))
|
||||||
return 'fas fa-info';
|
return 'fas fa-info';
|
||||||
|
|
||||||
// Default
|
// Default
|
||||||
return 'fa-fw fas fa-cog';
|
return 'fa-fw fas fa-cog';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an LDAP attribute, this will return the attribute name and the tag
|
||||||
|
* eg: description;lang-cn will return [description,lang-cn]
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function keytag(string $key): array
|
||||||
|
{
|
||||||
|
$matches = [];
|
||||||
|
if (preg_match(sprintf('/^([%s]+);+([%s;]+)/',self::TAG_CHARS,self::TAG_CHARS),$key,$matches)) {
|
||||||
|
$attribute = $matches[1];
|
||||||
|
$tags = $matches[2];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$attribute = $key;
|
||||||
|
$tags = self::TAG_NOTAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$attribute,$tags];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
* Dont convert our $this->attributes to $this->objects when creating a new Entry::class
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public function noObjectAttributes(): static
|
public function noObjectAttributes(): static
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ use LdapRecord\Models\Model as LdapRecord;
|
|||||||
*/
|
*/
|
||||||
class LoginObjectclassRule implements Rule
|
class LoginObjectclassRule implements Rule
|
||||||
{
|
{
|
||||||
public function passes(LdapRecord $user, Eloquent $model = null): bool
|
public function passes(LdapRecord $user,?Eloquent $model=NULL): bool
|
||||||
{
|
{
|
||||||
if ($x=config('pla.login.objectclass')) {
|
if ($x=config('pla.login.objectclass')) {
|
||||||
return count(array_intersect($user->objectclass,$x));
|
return count(array_intersect($user->objectclass,$x));
|
||||||
|
@ -20,7 +20,7 @@ class HasStructuralObjectClass implements ValidationRule
|
|||||||
*/
|
*/
|
||||||
public function validate(string $attribute,mixed $value,Closure $fail): void
|
public function validate(string $attribute,mixed $value,Closure $fail): void
|
||||||
{
|
{
|
||||||
foreach ($value as $item)
|
foreach (collect($value)->dot() as $item)
|
||||||
if ($item && config('server')->schema('objectclasses',$item)->isStructural())
|
if ($item && config('server')->schema('objectclasses',$item)->isStructural())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ trait MD5Updates
|
|||||||
{
|
{
|
||||||
public function isDirty(): bool
|
public function isDirty(): bool
|
||||||
{
|
{
|
||||||
foreach ($this->values->diff($this->oldValues) as $key => $value)
|
foreach ($this->values_old->dot()->keys()->merge($this->values->dot()->keys())->unique() as $dotkey)
|
||||||
if (md5(Arr::get($this->oldValues,$key)) !== $value)
|
if (md5(Arr::get($this->values_old->dot(),$dotkey)) !== Arr::get($this->values->dot(),$dotkey))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
namespace App\View\Components;
|
namespace App\View\Components;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
class Attribute extends Component
|
class Attribute extends Component
|
||||||
{
|
{
|
||||||
@ -12,30 +15,32 @@ class Attribute extends Component
|
|||||||
public bool $edit;
|
public bool $edit;
|
||||||
public bool $new;
|
public bool $new;
|
||||||
public bool $old;
|
public bool $old;
|
||||||
public ?string $na;
|
public string $langtag;
|
||||||
|
public ?string $na; // Text to render if the LDAPAttribute is null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new component instance.
|
* Create a new component instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,?string $na=NULL)
|
public function __construct(?LDAPAttribute $o,bool $edit=FALSE,bool $old=FALSE,bool $new=FALSE,string $langtag=Entry::TAG_NOTAG,?string $na=NULL)
|
||||||
{
|
{
|
||||||
$this->o = $o;
|
$this->o = $o;
|
||||||
$this->edit = $edit;
|
$this->edit = $edit;
|
||||||
$this->old = $old;
|
$this->old = $old;
|
||||||
$this->new = $new;
|
$this->new = $new;
|
||||||
|
$this->langtag = $langtag;
|
||||||
$this->na = $na;
|
$this->na = $na;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the view / contents that represent the component.
|
* Get the view / contents that represent the component.
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
* @return View|string
|
||||||
*/
|
*/
|
||||||
public function render()
|
public function render(): View|string
|
||||||
{
|
{
|
||||||
return $this->o
|
return $this->o
|
||||||
? $this->o
|
? $this->o
|
||||||
->render($this->edit,$this->old,$this->new)
|
->render(edit: $this->edit,old: $this->old,new: $this->new)
|
||||||
: $this->na;
|
: $this->na;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,37 +2,39 @@
|
|||||||
|
|
||||||
namespace App\View\Components;
|
namespace App\View\Components;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
|
|
||||||
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
use App\Classes\LDAP\Attribute as LDAPAttribute;
|
||||||
|
use App\Ldap\Entry;
|
||||||
|
|
||||||
class AttributeType extends Component
|
class AttributeType extends Component
|
||||||
{
|
{
|
||||||
public Collection $oc;
|
private LDAPAttribute $o;
|
||||||
public LDAPAttribute $o;
|
private bool $new;
|
||||||
public bool $new;
|
private bool $edit;
|
||||||
|
private string $langtag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new component instance.
|
* Create a new component instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(LDAPAttribute $o,bool $new=FALSE,?Collection $oc=NULL)
|
public function __construct(LDAPAttribute $o,bool $new=FALSE,bool $edit=FALSE,string $langtag=Entry::TAG_NOTAG)
|
||||||
{
|
{
|
||||||
$this->o = $o;
|
$this->o = $o;
|
||||||
$this->oc = $oc;
|
|
||||||
$this->new = $new;
|
$this->new = $new;
|
||||||
|
$this->edit = $edit;
|
||||||
|
$this->langtag = $langtag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the view / contents that represent the component.
|
* Get the view / contents that represent the component.
|
||||||
*/
|
*/
|
||||||
public function render(): View|Closure|string
|
public function render(): View
|
||||||
{
|
{
|
||||||
return view('components.attribute-type')
|
return view('components.attribute-type')
|
||||||
->with('o',$this->o)
|
->with('o',$this->o)
|
||||||
->with('oc',$this->oc)
|
->with('new',$this->new)
|
||||||
->with('new',$this->new);
|
->with('edit',$this->edit)
|
||||||
|
->with('langtag',$this->langtag);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,20 +16,21 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
$middleware->appendToGroup('web', [
|
$middleware->appendToGroup('web', [
|
||||||
ApplicationSession::class,
|
|
||||||
SwapinAuthUser::class,
|
SwapinAuthUser::class,
|
||||||
|
ApplicationSession::class,
|
||||||
CheckUpdate::class,
|
CheckUpdate::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$middleware->prependToGroup('api', [
|
$middleware->prependToGroup('api', [
|
||||||
EncryptCookies::class,
|
EncryptCookies::class,
|
||||||
ApplicationSession::class,
|
|
||||||
SwapinAuthUser::class,
|
SwapinAuthUser::class,
|
||||||
|
ApplicationSession::class,
|
||||||
AllowAnonymous::class,
|
AllowAnonymous::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$middleware->trustProxies(at: [
|
$middleware->trustProxies(at: [
|
||||||
'10.0.0.0/8',
|
'10.0.0.0/8',
|
||||||
|
'127.0.0.0/8',
|
||||||
'172.16.0.0/12',
|
'172.16.0.0/12',
|
||||||
'192.168.0.0/12',
|
'192.168.0.0/12',
|
||||||
]);
|
]);
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"ext-ldap": "*",
|
"ext-ldap": "*",
|
||||||
|
"ext-openssl": "*",
|
||||||
"php": "^8.4",
|
"php": "^8.4",
|
||||||
"directorytree/ldaprecord-laravel": "^3.0",
|
"directorytree/ldaprecord-laravel": "^3.0",
|
||||||
"laravel/framework": "^11.9",
|
"laravel/framework": "^11.9",
|
||||||
|
670
composer.lock
generated
670
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'default' => env('LDAP_CONNECTION', 'openldap'),
|
'default' => env('LDAP_CONNECTION', 'ldap'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -30,7 +30,7 @@ return [
|
|||||||
|
|
||||||
'connections' => [
|
'connections' => [
|
||||||
|
|
||||||
'openldap' => [
|
'ldap' => [
|
||||||
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
||||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||||
@ -42,7 +42,7 @@ return [
|
|||||||
'name' => env('LDAP_NAME','LDAP Server'),
|
'name' => env('LDAP_NAME','LDAP Server'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'openldaps' => [
|
'ldaps' => [
|
||||||
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
||||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||||
@ -54,7 +54,7 @@ return [
|
|||||||
'name' => env('LDAP_NAME','LDAPS Server'),
|
'name' => env('LDAP_NAME','LDAPS Server'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'openldaptls' => [
|
'starttls' => [
|
||||||
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
||||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||||
@ -66,6 +66,7 @@ return [
|
|||||||
'name' => env('LDAP_NAME','LDAP-TLS Server'),
|
'name' => env('LDAP_NAME','LDAP-TLS Server'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
'opendj' => [
|
'opendj' => [
|
||||||
'hosts' => ['opendj'],
|
'hosts' => ['opendj'],
|
||||||
'username' => 'cn=Directory Manager',
|
'username' => 'cn=Directory Manager',
|
||||||
@ -77,6 +78,7 @@ return [
|
|||||||
'use_tls' => env('LDAP_TLS', false),
|
'use_tls' => env('LDAP_TLS', false),
|
||||||
'name' => 'OpenDJ Server',
|
'name' => 'OpenDJ Server',
|
||||||
],
|
],
|
||||||
|
*/
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -120,58 +122,51 @@ return [
|
|||||||
*/
|
*/
|
||||||
'validation' => [
|
'validation' => [
|
||||||
'objectclass' => [
|
'objectclass' => [
|
||||||
'objectclass'=>[
|
'objectclass.*'=>[
|
||||||
'required',
|
|
||||||
'array',
|
|
||||||
'min:1',
|
|
||||||
new HasStructuralObjectClass,
|
new HasStructuralObjectClass,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'gidnumber' => [
|
'gidnumber' => [
|
||||||
'gidnumber'=> [
|
'gidnumber.*'=> [
|
||||||
'sometimes',
|
'sometimes',
|
||||||
'array',
|
|
||||||
'max:1'
|
'max:1'
|
||||||
],
|
],
|
||||||
'gidnumber.*' => [
|
'gidnumber.*.*' => [
|
||||||
'nullable',
|
'nullable',
|
||||||
'integer',
|
'integer',
|
||||||
'max:65535'
|
'max:65535'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'mail' => [
|
'mail' => [
|
||||||
'mail'=>[
|
'mail.*'=>[
|
||||||
'sometimes',
|
'sometimes',
|
||||||
'array',
|
|
||||||
'min:1'
|
'min:1'
|
||||||
],
|
],
|
||||||
'mail.*' => [
|
'mail.*.*' => [
|
||||||
'nullable',
|
'nullable',
|
||||||
'email'
|
'email'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'userpassword' => [
|
'userpassword' => [
|
||||||
'userpassword' => [
|
'userpassword.*' => [
|
||||||
'sometimes',
|
'sometimes',
|
||||||
'array',
|
|
||||||
'min:1'
|
'min:1'
|
||||||
],
|
],
|
||||||
'userpassword.*' => [
|
'userpassword.*.*' => [
|
||||||
'nullable',
|
'nullable',
|
||||||
'min:8'
|
'min:8'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'uidnumber' => [
|
'uidnumber' => [
|
||||||
'uidnumber' => [
|
'uidnumber.*' => [
|
||||||
'sometimes',
|
'sometimes',
|
||||||
'array',
|
|
||||||
'max:1'
|
'max:1'
|
||||||
],
|
],
|
||||||
'uidnumber.*' => [
|
'uidnumber.*.*' => [
|
||||||
'nullable',
|
'nullable',
|
||||||
'integer',
|
'integer',
|
||||||
'max:65535'
|
'max:65535'
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
1.3.6.1.4.1.42.2.27.8.5.1:passwordPolicyRequest
|
1.3.6.1.4.1.42.2.27.8.5.1:passwordPolicyRequest
|
||||||
1.3.6.1.4.1.42.2.27.9.5.2:GetEffectiveRights control::May be used to determine what operations a given user may perform on a specified entry.
|
1.3.6.1.4.1.42.2.27.9.5.2:GetEffectiveRights control::May be used to determine what operations a given user may perform on a specified entry.
|
||||||
1.3.6.1.4.1.1466.101.119.1:Dynamic Directory Services Refresh Request:RFC 2589
|
1.3.6.1.4.1.1466.101.119.1:Dynamic Directory Services Refresh Request:RFC 2589
|
||||||
|
1.3.6.1.4.1.1466.115.121.1.25:"guide" syntax-name:RFC 4517
|
||||||
1.3.6.1.4.1.1466.20036:LDAP_NOTICE_OF_DISCONNECTION
|
1.3.6.1.4.1.1466.20036:LDAP_NOTICE_OF_DISCONNECTION
|
||||||
1.3.6.1.4.1.1466.20037:Transport Layer Security Extension:RFC 2830:This operation provides for TLS establishment in an LDAP association and is defined in terms of an LDAP extended request.
|
1.3.6.1.4.1.1466.20037:Transport Layer Security Extension:RFC 2830:This operation provides for TLS establishment in an LDAP association and is defined in terms of an LDAP extended request.
|
||||||
1.3.6.1.4.1.1466.29539.1:LDAP_CONTROL_ATTR_SIZELIMIT
|
1.3.6.1.4.1.1466.29539.1:LDAP_CONTROL_ATTR_SIZELIMIT
|
||||||
|
549
package-lock.json
generated
549
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@
|
|||||||
"animate-sass": "^0.8.2",
|
"animate-sass": "^0.8.2",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.3.4",
|
||||||
"bootstrap": "^5.2.3",
|
"bootstrap": "^5.2.3",
|
||||||
|
"bootstrap-icons": "^1.11.3",
|
||||||
"jquery": "^3.6.3",
|
"jquery": "^3.6.3",
|
||||||
"jquery-ui": "^1.13.2",
|
"jquery-ui": "^1.13.2",
|
||||||
"jquery.fancytree": "^2.38.3",
|
"jquery.fancytree": "^2.38.3",
|
||||||
|
@ -1 +1 @@
|
|||||||
v2.0.0-rel
|
v2.1.0-rel
|
||||||
|
70
public/css/custom.css
vendored
70
public/css/custom.css
vendored
@ -1,18 +1,72 @@
|
|||||||
img.jpegphoto {
|
|
||||||
display:block;
|
|
||||||
max-width:100px;
|
|
||||||
height:100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** ensure our userpassword has select is next to the password input */
|
/** ensure our userpassword has select is next to the password input */
|
||||||
div#userPassword .select2-container--bootstrap-5 .select2-selection {
|
attribute#userPassword .select2-container--bootstrap-5 .select2-selection {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
width: 9em;
|
width: 9em;
|
||||||
border: #444054 1px solid;
|
border: var(--bs-gray-500) 1px solid;
|
||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group:first-child .select2-container--bootstrap-5 .select2-selection {
|
.input-group:first-child .select2-container--bootstrap-5 .select2-selection {
|
||||||
border-bottom-right-radius: unset;
|
border-bottom-right-radius: unset;
|
||||||
border-top-right-radius: unset;
|
border-top-right-radius: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute#objectClass .input-group-end:not(input.form-control) {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: 0.5em;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.form-control.input-group-end {
|
||||||
|
border-bottom-right-radius: 4px !important;
|
||||||
|
border-top-right-radius: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip-warning {
|
||||||
|
--bs-tooltip-bg: var(--bs-warning);
|
||||||
|
--bs-tooltip-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip-danger {
|
||||||
|
--bs-tooltip-bg: var(--bs-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip {
|
||||||
|
--bs-tooltip-bg: var(--bs-gray-900);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
.custom-tooltip-warning .tooltip-inner {
|
||||||
|
--bs-tooltip-bg: #ffffff;
|
||||||
|
--bs-tooltip-color: var(--bs-warning);
|
||||||
|
--bs-font-size: 85%;
|
||||||
|
border: 2px solid var(--bs-warning);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip-warning .tooltip-arrow::before {
|
||||||
|
--bs-tooltip-bg: var(--bs-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip-danger .tooltip-inner {
|
||||||
|
--bs-tooltip-bg: #ffffff;
|
||||||
|
--bs-tooltip-color: var(--bs-danger);
|
||||||
|
--bs-font-size: 85%;
|
||||||
|
border: 2px solid var(--bs-danger);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tooltip-danger .tooltip-arrow::before {
|
||||||
|
--bs-tooltip-bg: var(--bs-danger);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* hide the site icons when the search is opened */
|
||||||
|
.search-wrapper.active + .header-menu.nav {
|
||||||
|
display: none;
|
||||||
}
|
}
|
71
public/css/fixes.css
vendored
71
public/css/fixes.css
vendored
@ -69,10 +69,6 @@ table.dataTable thead .sorting {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Fancy Tree Fixes **/
|
/** Fancy Tree Fixes **/
|
||||||
/*
|
|
||||||
@todo The unopened lazy branches off the tree are off by 5px. see *-cdl. below
|
|
||||||
@todo The last node is missing some dots, connecting to the previous node
|
|
||||||
*/
|
|
||||||
/* So our tree can be longer than the frame */
|
/* So our tree can be longer than the frame */
|
||||||
.scrollbar-sidebar {
|
.scrollbar-sidebar {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@ -117,7 +113,7 @@ ul.fancytree-container ul {
|
|||||||
}
|
}
|
||||||
.fancytree-node.fancytree-exp-n span.fancytree-expander,
|
.fancytree-node.fancytree-exp-n span.fancytree-expander,
|
||||||
.fancytree-node.fancytree-exp-n span.fancytree-expander:hover { /* node */
|
.fancytree-node.fancytree-exp-n span.fancytree-expander:hover { /* node */
|
||||||
margin-top: 4px;
|
margin-top: 3px;
|
||||||
background-position: 0 -63px;
|
background-position: 0 -63px;
|
||||||
}
|
}
|
||||||
.fancytree-node.fancytree-exp-nl span.fancytree-expander { /* node last */
|
.fancytree-node.fancytree-exp-nl span.fancytree-expander { /* node last */
|
||||||
@ -143,11 +139,6 @@ ul.fancytree-container ul {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix ellipsis icon (top right) on small display with the light background */
|
|
||||||
.closed-sidebar .app-header.header-text-light .app-header__menu .mobile-toggle-header-nav {
|
|
||||||
background: #343a40;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide tree when collapsed and show it when open */
|
/* Hide tree when collapsed and show it when open */
|
||||||
.sidebar-mobile-open:hover #tree, /* small */
|
.sidebar-mobile-open:hover #tree, /* small */
|
||||||
.fixed-sidebar #tree, /* wide */
|
.fixed-sidebar #tree, /* wide */
|
||||||
@ -160,12 +151,6 @@ ul.fancytree-container ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Server icons **/
|
/** Server icons **/
|
||||||
.closed-sidebar .server-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.closed-sidebar .app-sidebar:hover .server-icon, .sidebar-mobile-open .server-icon {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.font-icon-wrapper {
|
.font-icon-wrapper {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: #e9ecef solid 1px;
|
border: #e9ecef solid 1px;
|
||||||
@ -181,47 +166,6 @@ ul.fancytree-container ul {
|
|||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
.font-icon-wrapper {
|
|
||||||
text-align: center;
|
|
||||||
border: $gray-200 solid 1px;
|
|
||||||
@include border-radius($border-radius);
|
|
||||||
margin: 0 0 10px;
|
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
&.font-icon-lg {
|
|
||||||
float: left;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 15px;
|
|
||||||
min-width: 64px;
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: $h1-font-size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $gray-100;
|
|
||||||
color: $primary;
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: $gray-600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: ($font-size-base * 1.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: $gray-500;
|
|
||||||
font-size: calc($font-size-sm / 1.2);
|
|
||||||
margin: 5px 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Ensure our DN menu is at the top **/
|
/** Ensure our DN menu is at the top **/
|
||||||
.app-page-title .page-title-wrapper {
|
.app-page-title .page-title-wrapper {
|
||||||
align-items: start;
|
align-items: start;
|
||||||
@ -297,14 +241,15 @@ select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
div#objectClass .input-group-delete {
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
position: relative;
|
padding: 0.25em 0.45em;
|
||||||
float: inline-end;
|
|
||||||
bottom: 30px;
|
|
||||||
right: 10px;
|
|
||||||
height: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group-text {
|
.input-group-text {
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop showing a border on our user's drop down menu when open */
|
||||||
|
.btn-check:checked+.btn, .btn.active, .btn.show, .btn:first-child:active, :not(.btn-check)+.btn:active {
|
||||||
|
border-color: var(--bs-btn-bg);
|
||||||
}
|
}
|
20
public/js/custom.js
vendored
20
public/js/custom.js
vendored
@ -36,16 +36,18 @@ function getNode(item) {
|
|||||||
case 404:
|
case 404:
|
||||||
$('.main-content').empty().append(e.responseText);
|
$('.main-content').empty().append(e.responseText);
|
||||||
break;
|
break;
|
||||||
case 409:
|
case 409: // Not in root
|
||||||
location.replace('/#'+item);
|
location.replace('/#'+item);
|
||||||
break;
|
break;
|
||||||
case 419:
|
case 419: // Session Expired
|
||||||
alert('Session has expired, reloading the page and try again...');
|
location.replace('/#'+item);
|
||||||
location.reload();
|
location.reload();
|
||||||
break;
|
break;
|
||||||
case 500:
|
case 500:
|
||||||
|
case 555: // Missing Method
|
||||||
$('.main-content').empty().append(e.responseText);
|
$('.main-content').empty().append(e.responseText);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
alert('Well that didnt work? Code ['+e.status+']');
|
alert('Well that didnt work? Code ['+e.status+']');
|
||||||
}
|
}
|
||||||
@ -64,18 +66,14 @@ $(document).ready(function() {
|
|||||||
// and pass the tree options as an argument to the fancytree() function:
|
// and pass the tree options as an argument to the fancytree() function:
|
||||||
$('#tree').fancytree({
|
$('#tree').fancytree({
|
||||||
clickFolderMode: 3,
|
clickFolderMode: 3,
|
||||||
extensions: ['glyph','persist'],
|
extensions: ['persist'],
|
||||||
autoCollapse: true, // Automatically collapse all siblings, when a node is expanded.
|
autoCollapse: true, // Automatically collapse all siblings, when a node is expanded.
|
||||||
autoScroll: true, // Automatically scroll nodes into visible area.
|
autoScroll: true, // Automatically scroll nodes into visible area.
|
||||||
focusOnSelect: true, // Set focus when node is checked by a mouse click
|
focusOnSelect: true, // Set focus when node is checked by a mouse click
|
||||||
glyph: {
|
|
||||||
preset: 'bootstrap3', // @todo look at changing this to awesome5
|
|
||||||
map: {}
|
|
||||||
},
|
|
||||||
persist: {
|
persist: {
|
||||||
// Available options with their default:
|
// Available options with their default:
|
||||||
cookieDelimiter: '~', // character used to join key strings
|
cookieDelimiter: '~', // character used to join key strings
|
||||||
cookiePrefix: undefined, // 'fancytree-<treeId>-' by default
|
cookiePrefix: 'pla-<treeId>-', // 'fancytree-<treeId>-' by default
|
||||||
cookie: { // settings passed to jquery.cookie plugin
|
cookie: { // settings passed to jquery.cookie plugin
|
||||||
raw: false,
|
raw: false,
|
||||||
expires: '',
|
expires: '',
|
||||||
@ -85,14 +83,14 @@ $(document).ready(function() {
|
|||||||
},
|
},
|
||||||
expandLazy: true, // true: recursively expand and load lazy nodes
|
expandLazy: true, // true: recursively expand and load lazy nodes
|
||||||
expandOpts: undefined, // optional `opts` argument passed to setExpanded()
|
expandOpts: undefined, // optional `opts` argument passed to setExpanded()
|
||||||
|
fireActivate: false, //
|
||||||
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
|
overrideSource: true, // true: cookie takes precedence over `source` data attributes.
|
||||||
store: 'auto', // 'cookie': use cookie, 'local': use localStore, 'session': use sessionStore
|
store: 'auto', // 'cookie': use cookie, 'local': use localStore, 'session': use sessionStore
|
||||||
types: 'active expanded focus selected' // which status types to store
|
types: 'active expanded focus selected' // which status types to store
|
||||||
},
|
},
|
||||||
click: function(event,data) {
|
click: function(event,data) {
|
||||||
if (data.targetType == 'title') {
|
if (data.targetType === 'title')
|
||||||
getNode(data.node.data.item);
|
getNode(data.node.data.item);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
source: sources,
|
source: sources,
|
||||||
lazyLoad: function(event,data) {
|
lazyLoad: function(event,data) {
|
||||||
|
3
resources/sass/app.scss
vendored
3
resources/sass/app.scss
vendored
@ -7,3 +7,6 @@
|
|||||||
// Select2
|
// Select2
|
||||||
@import "select2/dist/css/select2";
|
@import "select2/dist/css/select2";
|
||||||
@import "select2-bootstrap-5-theme/dist/select2-bootstrap-5-theme";
|
@import "select2-bootstrap-5-theme/dist/select2-bootstrap-5-theme";
|
||||||
|
|
||||||
|
// Bootstrap icons
|
||||||
|
@import "bootstrap-icons"
|
||||||
|
10
resources/themes/architect/src/app.js
vendored
10
resources/themes/architect/src/app.js
vendored
@ -7,7 +7,6 @@ import 'metismenu';
|
|||||||
// Stylesheets
|
// Stylesheets
|
||||||
|
|
||||||
// import './assets/base.scss';
|
// import './assets/base.scss';
|
||||||
// import '../../themes/architect/src/base.scss';
|
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
|
|
||||||
@ -73,11 +72,14 @@ $(document).ready(() => {
|
|||||||
|
|
||||||
var resizeClass = function () {
|
var resizeClass = function () {
|
||||||
var win = document.body.clientWidth;
|
var win = document.body.clientWidth;
|
||||||
if (win < 1250) {
|
if (win < 768) {
|
||||||
|
$('.app-container').addClass("closed-sidebar closed-sidebar-mobile");
|
||||||
|
$('.app-header').addClass("header-text-light bg-light").removeClass("bg-dark header-text-dark");
|
||||||
|
} else if (win < 1250) {
|
||||||
$('.app-container').addClass('closed-sidebar-mobile closed-sidebar');
|
$('.app-container').addClass('closed-sidebar-mobile closed-sidebar');
|
||||||
$('.app-header').removeClass("heard-text-light bg-dark").addClass("bg-light header-text-dark");
|
$('.app-header').removeClass("header-text-light bg-dark").addClass("bg-light header-text-dark");
|
||||||
} else {
|
} else {
|
||||||
$('.app-header').addClass("heard-text-light bg-dark").removeClass("bg-light header-text-dark");
|
$('.app-header').addClass("header-text-light bg-dark").removeClass("bg-light header-text-dark");
|
||||||
$('.app-container').removeClass('closed-sidebar-mobile closed-sidebar');
|
$('.app-container').removeClass('closed-sidebar-mobile closed-sidebar');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
9
resources/themes/architect/src/base.scss
vendored
9
resources/themes/architect/src/base.scss
vendored
@ -1,9 +1,9 @@
|
|||||||
/*!
|
/*!
|
||||||
=========================================================
|
=========================================================
|
||||||
* ArchitectUI HTML Theme Dashboard - v2.0.0
|
* ArchitectUI HTML Theme Dashboard - v4.1.0
|
||||||
=========================================================
|
=========================================================
|
||||||
* Product Page: https://dashboardpack.com
|
* Product Page: https://dashboardpack.com
|
||||||
* Copyright 2021 DashboardPack (https://dashboardpack.com)
|
* Copyright 2025 DashboardPack (https://dashboardpack.com)
|
||||||
* Licensed under MIT (https://github.com/DashboardPack/architectui-html-theme-free/blob/master/LICENSE)
|
* Licensed under MIT (https://github.com/DashboardPack/architectui-html-theme-free/blob/master/LICENSE)
|
||||||
=========================================================
|
=========================================================
|
||||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
@ -21,6 +21,8 @@
|
|||||||
// 3. Include remainder of required Bootstrap stylesheets
|
// 3. Include remainder of required Bootstrap stylesheets
|
||||||
|
|
||||||
@import "components/bootstrap5/variables";
|
@import "components/bootstrap5/variables";
|
||||||
|
@import "components/bootstrap5/variables-dark";
|
||||||
|
@import "components/bootstrap5/maps";
|
||||||
@import "components/bootstrap5/mixins";
|
@import "components/bootstrap5/mixins";
|
||||||
@import "components/bootstrap5/utilities";
|
@import "components/bootstrap5/utilities";
|
||||||
|
|
||||||
@ -56,9 +58,10 @@
|
|||||||
// @import "components/bootstrap5/carousel";
|
// @import "components/bootstrap5/carousel";
|
||||||
// @import "components/bootstrap5/spinners";
|
// @import "components/bootstrap5/spinners";
|
||||||
// @import "components/bootstrap5/offcanvas";
|
// @import "components/bootstrap5/offcanvas";
|
||||||
// @import "components/bootstrap5/jumbotron";
|
// @import "components/bootstrap5/placeholders";
|
||||||
@import "components/bootstrap5/helpers";
|
@import "components/bootstrap5/helpers";
|
||||||
@import "components/bootstrap5/utilities/api";
|
@import "components/bootstrap5/utilities/api";
|
||||||
|
|
||||||
// LAYOUT
|
// LAYOUT
|
||||||
|
|
||||||
@import "layout/layout";
|
@import "layout/layout";
|
||||||
|
@ -3,11 +3,25 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
|
// scss-docs-start alert-css-vars
|
||||||
|
--#{$prefix}alert-bg: transparent;
|
||||||
|
--#{$prefix}alert-padding-x: #{$alert-padding-x};
|
||||||
|
--#{$prefix}alert-padding-y: #{$alert-padding-y};
|
||||||
|
--#{$prefix}alert-margin-bottom: #{$alert-margin-bottom};
|
||||||
|
--#{$prefix}alert-color: inherit;
|
||||||
|
--#{$prefix}alert-border-color: transparent;
|
||||||
|
--#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color);
|
||||||
|
--#{$prefix}alert-border-radius: #{$alert-border-radius};
|
||||||
|
--#{$prefix}alert-link-color: inherit;
|
||||||
|
// scss-docs-end alert-css-vars
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: $alert-padding-y $alert-padding-x;
|
padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x);
|
||||||
margin-bottom: $alert-margin-bottom;
|
margin-bottom: var(--#{$prefix}alert-margin-bottom);
|
||||||
border: $alert-border-width solid transparent;
|
color: var(--#{$prefix}alert-color);
|
||||||
@include border-radius($alert-border-radius);
|
background-color: var(--#{$prefix}alert-bg);
|
||||||
|
border: var(--#{$prefix}alert-border);
|
||||||
|
@include border-radius(var(--#{$prefix}alert-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headings for larger alerts
|
// Headings for larger alerts
|
||||||
@ -19,6 +33,7 @@
|
|||||||
// Provide class for links that match alerts
|
// Provide class for links that match alerts
|
||||||
.alert-link {
|
.alert-link {
|
||||||
font-weight: $alert-link-font-weight;
|
font-weight: $alert-link-font-weight;
|
||||||
|
color: var(--#{$prefix}alert-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -41,17 +56,13 @@
|
|||||||
|
|
||||||
|
|
||||||
// scss-docs-start alert-modifiers
|
// scss-docs-start alert-modifiers
|
||||||
// Generate contextual modifier classes for colorizing the alert.
|
// Generate contextual modifier classes for colorizing the alert
|
||||||
|
@each $state in map-keys($theme-colors) {
|
||||||
@each $state, $value in $theme-colors {
|
|
||||||
$alert-background: shift-color($value, $alert-bg-scale);
|
|
||||||
$alert-border: shift-color($value, $alert-border-scale);
|
|
||||||
$alert-color: shift-color($value, $alert-color-scale);
|
|
||||||
@if (contrast-ratio($alert-background, $alert-color) < $min-contrast-ratio) {
|
|
||||||
$alert-color: mix($value, color-contrast($alert-background), abs($alert-color-scale));
|
|
||||||
}
|
|
||||||
.alert-#{$state} {
|
.alert-#{$state} {
|
||||||
@include alert-variant($alert-background, $alert-border, $alert-color);
|
--#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
--#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
|
--#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// scss-docs-end alert-modifiers
|
// scss-docs-end alert-modifiers
|
||||||
|
@ -4,16 +4,25 @@
|
|||||||
// `background-color`.
|
// `background-color`.
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
// scss-docs-start badge-css-vars
|
||||||
|
--#{$prefix}badge-padding-x: #{$badge-padding-x};
|
||||||
|
--#{$prefix}badge-padding-y: #{$badge-padding-y};
|
||||||
|
@include rfs($badge-font-size, --#{$prefix}badge-font-size);
|
||||||
|
--#{$prefix}badge-font-weight: #{$badge-font-weight};
|
||||||
|
--#{$prefix}badge-color: #{$badge-color};
|
||||||
|
--#{$prefix}badge-border-radius: #{$badge-border-radius};
|
||||||
|
// scss-docs-end badge-css-vars
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: $badge-padding-y $badge-padding-x;
|
padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x);
|
||||||
@include font-size($badge-font-size);
|
@include font-size(var(--#{$prefix}badge-font-size));
|
||||||
font-weight: $badge-font-weight;
|
font-weight: var(--#{$prefix}badge-font-weight);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: $badge-color;
|
color: var(--#{$prefix}badge-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
@include border-radius($badge-border-radius);
|
@include border-radius(var(--#{$prefix}badge-border-radius));
|
||||||
@include gradient-bg();
|
@include gradient-bg();
|
||||||
|
|
||||||
// Empty badges collapse automatically
|
// Empty badges collapse automatically
|
||||||
|
@ -1,28 +1,40 @@
|
|||||||
.breadcrumb {
|
.breadcrumb {
|
||||||
|
// scss-docs-start breadcrumb-css-vars
|
||||||
|
--#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x};
|
||||||
|
--#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y};
|
||||||
|
--#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom};
|
||||||
|
@include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size);
|
||||||
|
--#{$prefix}breadcrumb-bg: #{$breadcrumb-bg};
|
||||||
|
--#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius};
|
||||||
|
--#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color};
|
||||||
|
--#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x};
|
||||||
|
--#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color};
|
||||||
|
// scss-docs-end breadcrumb-css-vars
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: $breadcrumb-padding-y $breadcrumb-padding-x;
|
padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x);
|
||||||
margin-bottom: $breadcrumb-margin-bottom;
|
margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom);
|
||||||
@include font-size($breadcrumb-font-size);
|
@include font-size(var(--#{$prefix}breadcrumb-font-size));
|
||||||
list-style: none;
|
list-style: none;
|
||||||
background-color: $breadcrumb-bg;
|
background-color: var(--#{$prefix}breadcrumb-bg);
|
||||||
@include border-radius($breadcrumb-border-radius);
|
@include border-radius(var(--#{$prefix}breadcrumb-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
.breadcrumb-item {
|
.breadcrumb-item {
|
||||||
// The separator between breadcrumbs (by default, a forward-slash: "/")
|
// The separator between breadcrumbs (by default, a forward-slash: "/")
|
||||||
+ .breadcrumb-item {
|
+ .breadcrumb-item {
|
||||||
padding-left: $breadcrumb-item-padding-x;
|
padding-left: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
float: left; // Suppress inline spacings and underlining of the separator
|
float: left; // Suppress inline spacings and underlining of the separator
|
||||||
padding-right: $breadcrumb-item-padding-x;
|
padding-right: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||||
color: $breadcrumb-divider-color;
|
color: var(--#{$prefix}breadcrumb-divider-color);
|
||||||
content: var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
|
content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: $breadcrumb-active-color;
|
color: var(--#{$prefix}breadcrumb-item-active-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,14 +34,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
|
@include border-radius($btn-border-radius);
|
||||||
|
|
||||||
// Prevent double borders when buttons are next to each other
|
// Prevent double borders when buttons are next to each other
|
||||||
> .btn:not(:first-child),
|
> :not(.btn-check:first-child) + .btn,
|
||||||
> .btn-group:not(:first-child) {
|
> .btn-group:not(:first-child) {
|
||||||
margin-left: -$btn-border-width;
|
margin-left: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset rounded corners
|
// Reset rounded corners
|
||||||
> .btn:not(:last-child):not(.dropdown-toggle),
|
> .btn:not(:last-child):not(.dropdown-toggle),
|
||||||
|
> .btn.dropdown-toggle-split:first-child,
|
||||||
> .btn-group:not(:last-child) > .btn {
|
> .btn-group:not(:last-child) > .btn {
|
||||||
@include border-end-radius(0);
|
@include border-end-radius(0);
|
||||||
}
|
}
|
||||||
@ -123,7 +126,7 @@
|
|||||||
|
|
||||||
> .btn:not(:first-child),
|
> .btn:not(:first-child),
|
||||||
> .btn-group:not(:first-child) {
|
> .btn-group:not(:first-child) {
|
||||||
margin-top: -$btn-border-width;
|
margin-top: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset rounded corners
|
// Reset rounded corners
|
||||||
|
@ -3,49 +3,112 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
|
// scss-docs-start btn-css-vars
|
||||||
|
--#{$prefix}btn-padding-x: #{$btn-padding-x};
|
||||||
|
--#{$prefix}btn-padding-y: #{$btn-padding-y};
|
||||||
|
--#{$prefix}btn-font-family: #{$btn-font-family};
|
||||||
|
@include rfs($btn-font-size, --#{$prefix}btn-font-size);
|
||||||
|
--#{$prefix}btn-font-weight: #{$btn-font-weight};
|
||||||
|
--#{$prefix}btn-line-height: #{$btn-line-height};
|
||||||
|
--#{$prefix}btn-color: #{$btn-color};
|
||||||
|
--#{$prefix}btn-bg: transparent;
|
||||||
|
--#{$prefix}btn-border-width: #{$btn-border-width};
|
||||||
|
--#{$prefix}btn-border-color: transparent;
|
||||||
|
--#{$prefix}btn-border-radius: #{$btn-border-radius};
|
||||||
|
--#{$prefix}btn-hover-border-color: transparent;
|
||||||
|
--#{$prefix}btn-box-shadow: #{$btn-box-shadow};
|
||||||
|
--#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity};
|
||||||
|
--#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5);
|
||||||
|
// scss-docs-end btn-css-vars
|
||||||
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: $btn-font-family;
|
padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x);
|
||||||
font-weight: $btn-font-weight;
|
font-family: var(--#{$prefix}btn-font-family);
|
||||||
line-height: $btn-line-height;
|
@include font-size(var(--#{$prefix}btn-font-size));
|
||||||
color: $body-color;
|
font-weight: var(--#{$prefix}btn-font-weight);
|
||||||
|
line-height: var(--#{$prefix}btn-line-height);
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
white-space: $btn-white-space;
|
white-space: $btn-white-space;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: if($enable-button-pointers, pointer, null);
|
cursor: if($enable-button-pointers, pointer, null);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: transparent;
|
border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color);
|
||||||
border: $btn-border-width solid transparent;
|
@include border-radius(var(--#{$prefix}btn-border-radius));
|
||||||
@include button-size($btn-padding-y, $btn-padding-x, $btn-font-size, $btn-border-radius);
|
@include gradient-bg(var(--#{$prefix}btn-bg));
|
||||||
|
@include box-shadow(var(--#{$prefix}btn-box-shadow));
|
||||||
@include transition($btn-transition);
|
@include transition($btn-transition);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $body-color;
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
|
background-color: var(--#{$prefix}btn-hover-bg);
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-check:focus + &,
|
.btn-check + &:hover {
|
||||||
&:focus {
|
// override for the checkbox/radio buttons
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
|
background-color: var(--#{$prefix}btn-bg);
|
||||||
|
border-color: var(--#{$prefix}btn-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
|
@include gradient-bg(var(--#{$prefix}btn-hover-bg));
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: $btn-focus-box-shadow;
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-check:focus-visible + & {
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
|
outline: 0;
|
||||||
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-check:checked + &,
|
.btn-check:checked + &,
|
||||||
.btn-check:active + &,
|
:not(.btn-check) + &:active,
|
||||||
&:active,
|
&:first-child:active,
|
||||||
&.active {
|
&.active,
|
||||||
@include box-shadow($btn-active-box-shadow);
|
&.show {
|
||||||
|
color: var(--#{$prefix}btn-active-color);
|
||||||
|
background-color: var(--#{$prefix}btn-active-bg);
|
||||||
|
// Remove CSS gradients if they're enabled
|
||||||
|
background-image: if($enable-gradients, none, null);
|
||||||
|
border-color: var(--#{$prefix}btn-active-border-color);
|
||||||
|
@include box-shadow(var(--#{$prefix}btn-active-shadow));
|
||||||
|
|
||||||
&:focus {
|
&:focus-visible {
|
||||||
@include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow);
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled,
|
&:disabled,
|
||||||
&.disabled,
|
&.disabled,
|
||||||
fieldset:disabled & {
|
fieldset:disabled & {
|
||||||
|
color: var(--#{$prefix}btn-disabled-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: $btn-disabled-opacity;
|
background-color: var(--#{$prefix}btn-disabled-bg);
|
||||||
|
background-image: if($enable-gradients, none, null);
|
||||||
|
border-color: var(--#{$prefix}btn-disabled-border-color);
|
||||||
|
opacity: var(--#{$prefix}btn-disabled-opacity);
|
||||||
@include box-shadow(none);
|
@include box-shadow(none);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +121,27 @@
|
|||||||
// scss-docs-start btn-variant-loops
|
// scss-docs-start btn-variant-loops
|
||||||
@each $color, $value in $theme-colors {
|
@each $color, $value in $theme-colors {
|
||||||
.btn-#{$color} {
|
.btn-#{$color} {
|
||||||
@include button-variant($value, $value);
|
@if $color == "light" {
|
||||||
|
@include button-variant(
|
||||||
|
$value,
|
||||||
|
$value,
|
||||||
|
$hover-background: shade-color($value, $btn-hover-bg-shade-amount),
|
||||||
|
$hover-border: shade-color($value, $btn-hover-border-shade-amount),
|
||||||
|
$active-background: shade-color($value, $btn-active-bg-shade-amount),
|
||||||
|
$active-border: shade-color($value, $btn-active-border-shade-amount)
|
||||||
|
);
|
||||||
|
} @else if $color == "dark" {
|
||||||
|
@include button-variant(
|
||||||
|
$value,
|
||||||
|
$value,
|
||||||
|
$hover-background: tint-color($value, $btn-hover-bg-tint-amount),
|
||||||
|
$hover-border: tint-color($value, $btn-hover-border-tint-amount),
|
||||||
|
$active-background: tint-color($value, $btn-active-bg-tint-amount),
|
||||||
|
$active-border: tint-color($value, $btn-active-border-tint-amount)
|
||||||
|
);
|
||||||
|
} @else {
|
||||||
|
@include button-variant($value, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,22 +159,35 @@
|
|||||||
|
|
||||||
// Make a button look and behave like a link
|
// Make a button look and behave like a link
|
||||||
.btn-link {
|
.btn-link {
|
||||||
font-weight: $font-weight-normal;
|
--#{$prefix}btn-font-weight: #{$font-weight-normal};
|
||||||
color: $btn-link-color;
|
--#{$prefix}btn-color: #{$btn-link-color};
|
||||||
|
--#{$prefix}btn-bg: transparent;
|
||||||
|
--#{$prefix}btn-border-color: transparent;
|
||||||
|
--#{$prefix}btn-hover-color: #{$btn-link-hover-color};
|
||||||
|
--#{$prefix}btn-hover-border-color: transparent;
|
||||||
|
--#{$prefix}btn-active-color: #{$btn-link-hover-color};
|
||||||
|
--#{$prefix}btn-active-border-color: transparent;
|
||||||
|
--#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
|
||||||
|
--#{$prefix}btn-disabled-border-color: transparent;
|
||||||
|
--#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows
|
||||||
|
--#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb};
|
||||||
|
|
||||||
text-decoration: $link-decoration;
|
text-decoration: $link-decoration;
|
||||||
|
@if $enable-gradients {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
text-decoration: $link-hover-decoration;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $btn-link-hover-color;
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
text-decoration: $link-hover-decoration;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
text-decoration: $link-hover-decoration;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled,
|
|
||||||
&.disabled {
|
|
||||||
color: $btn-link-disabled-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need for an active state here
|
// No need for an active state here
|
||||||
|
@ -3,16 +3,40 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
// scss-docs-start card-css-vars
|
||||||
|
--#{$prefix}card-spacer-y: #{$card-spacer-y};
|
||||||
|
--#{$prefix}card-spacer-x: #{$card-spacer-x};
|
||||||
|
--#{$prefix}card-title-spacer-y: #{$card-title-spacer-y};
|
||||||
|
--#{$prefix}card-title-color: #{$card-title-color};
|
||||||
|
--#{$prefix}card-subtitle-color: #{$card-subtitle-color};
|
||||||
|
--#{$prefix}card-border-width: #{$card-border-width};
|
||||||
|
--#{$prefix}card-border-color: #{$card-border-color};
|
||||||
|
--#{$prefix}card-border-radius: #{$card-border-radius};
|
||||||
|
--#{$prefix}card-box-shadow: #{$card-box-shadow};
|
||||||
|
--#{$prefix}card-inner-border-radius: #{$card-inner-border-radius};
|
||||||
|
--#{$prefix}card-cap-padding-y: #{$card-cap-padding-y};
|
||||||
|
--#{$prefix}card-cap-padding-x: #{$card-cap-padding-x};
|
||||||
|
--#{$prefix}card-cap-bg: #{$card-cap-bg};
|
||||||
|
--#{$prefix}card-cap-color: #{$card-cap-color};
|
||||||
|
--#{$prefix}card-height: #{$card-height};
|
||||||
|
--#{$prefix}card-color: #{$card-color};
|
||||||
|
--#{$prefix}card-bg: #{$card-bg};
|
||||||
|
--#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding};
|
||||||
|
--#{$prefix}card-group-margin: #{$card-group-margin};
|
||||||
|
// scss-docs-end card-css-vars
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106
|
min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106
|
||||||
height: $card-height;
|
height: var(--#{$prefix}card-height);
|
||||||
|
color: var(--#{$prefix}body-color);
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
background-color: $card-bg;
|
background-color: var(--#{$prefix}card-bg);
|
||||||
background-clip: border-box;
|
background-clip: border-box;
|
||||||
border: $card-border-width solid $card-border-color;
|
border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
@include border-radius($card-border-radius);
|
@include border-radius(var(--#{$prefix}card-border-radius));
|
||||||
|
@include box-shadow(var(--#{$prefix}card-box-shadow));
|
||||||
|
|
||||||
> hr {
|
> hr {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
@ -25,12 +49,12 @@
|
|||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
@include border-top-radius($card-inner-border-radius);
|
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
@include border-bottom-radius($card-inner-border-radius);
|
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,17 +70,19 @@
|
|||||||
// Enable `flex-grow: 1` for decks and groups so that card blocks take up
|
// Enable `flex-grow: 1` for decks and groups so that card blocks take up
|
||||||
// as much space as possible, ensuring footers are aligned to the bottom.
|
// as much space as possible, ensuring footers are aligned to the bottom.
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
padding: $card-spacer-y $card-spacer-x;
|
padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x);
|
||||||
color: $card-color;
|
color: var(--#{$prefix}card-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
margin-bottom: $card-title-spacer-y;
|
margin-bottom: var(--#{$prefix}card-title-spacer-y);
|
||||||
|
color: var(--#{$prefix}card-title-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-subtitle {
|
.card-subtitle {
|
||||||
margin-top: -$card-title-spacer-y * .5;
|
margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); // stylelint-disable-line function-disallowed-list
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
color: var(--#{$prefix}card-subtitle-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-text:last-child {
|
.card-text:last-child {
|
||||||
@ -65,11 +91,11 @@
|
|||||||
|
|
||||||
.card-link {
|
.card-link {
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ .card-link {
|
+ .card-link {
|
||||||
margin-left: $card-spacer-x;
|
margin-left: var(--#{$prefix}card-spacer-x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,25 +104,25 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
padding: $card-cap-padding-y $card-cap-padding-x;
|
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
|
||||||
margin-bottom: 0; // Removes the default margin-bottom of <hN>
|
margin-bottom: 0; // Removes the default margin-bottom of <hN>
|
||||||
color: $card-cap-color;
|
color: var(--#{$prefix}card-cap-color);
|
||||||
background-color: $card-cap-bg;
|
background-color: var(--#{$prefix}card-cap-bg);
|
||||||
border-bottom: $card-border-width solid $card-border-color;
|
border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
@include border-radius($card-inner-border-radius $card-inner-border-radius 0 0);
|
@include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
padding: $card-cap-padding-y $card-cap-padding-x;
|
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
|
||||||
color: $card-cap-color;
|
color: var(--#{$prefix}card-cap-color);
|
||||||
background-color: $card-cap-bg;
|
background-color: var(--#{$prefix}card-cap-bg);
|
||||||
border-top: $card-border-width solid $card-border-color;
|
border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
@include border-radius(0 0 $card-inner-border-radius $card-inner-border-radius);
|
@include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,22 +132,20 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.card-header-tabs {
|
.card-header-tabs {
|
||||||
margin-right: -$card-cap-padding-x * .5;
|
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
margin-bottom: -$card-cap-padding-y;
|
margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); // stylelint-disable-line function-disallowed-list
|
||||||
margin-left: -$card-cap-padding-x * .5;
|
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
|
|
||||||
@if $nav-tabs-link-active-bg != $card-bg {
|
.nav-link.active {
|
||||||
.nav-link.active {
|
background-color: var(--#{$prefix}card-bg);
|
||||||
background-color: $card-bg;
|
border-bottom-color: var(--#{$prefix}card-bg);
|
||||||
border-bottom-color: $card-bg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header-pills {
|
.card-header-pills {
|
||||||
margin-right: -$card-cap-padding-x * .5;
|
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
margin-left: -$card-cap-padding-x * .5;
|
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
}
|
}
|
||||||
|
|
||||||
// Card image
|
// Card image
|
||||||
@ -131,8 +155,8 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: $card-img-overlay-padding;
|
padding: var(--#{$prefix}card-img-overlay-padding);
|
||||||
@include border-radius($card-inner-border-radius);
|
@include border-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-img,
|
.card-img,
|
||||||
@ -143,12 +167,12 @@
|
|||||||
|
|
||||||
.card-img,
|
.card-img,
|
||||||
.card-img-top {
|
.card-img-top {
|
||||||
@include border-top-radius($card-inner-border-radius);
|
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-img,
|
.card-img,
|
||||||
.card-img-bottom {
|
.card-img-bottom {
|
||||||
@include border-bottom-radius($card-inner-border-radius);
|
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -160,7 +184,7 @@
|
|||||||
// The child selector allows nested `.card` within `.card-group`
|
// The child selector allows nested `.card` within `.card-group`
|
||||||
// to display properly.
|
// to display properly.
|
||||||
> .card {
|
> .card {
|
||||||
margin-bottom: $card-group-margin;
|
margin-bottom: var(--#{$prefix}card-group-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
|
@ -1,40 +1,76 @@
|
|||||||
// transparent background and border properties included for button version.
|
// Transparent background and border properties included for button version.
|
||||||
// iOS requires the button element instead of an anchor tag.
|
// iOS requires the button element instead of an anchor tag.
|
||||||
// If you want the anchor version, it requires `href="#"`.
|
// If you want the anchor version, it requires `href="#"`.
|
||||||
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
|
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
|
||||||
|
|
||||||
|
.header-text-dark * .btn-close {
|
||||||
|
--#{$prefix}btn-close-color: #{$btn-close-color-dark};
|
||||||
|
--#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg-dark) };
|
||||||
|
}
|
||||||
|
.header-text-light *.btn-close {
|
||||||
|
--#{$prefix}btn-close-color: #{$btn-close-color-light};
|
||||||
|
--#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg-light) };
|
||||||
|
}
|
||||||
|
|
||||||
.btn-close {
|
.btn-close {
|
||||||
|
// scss-docs-start close-css-vars
|
||||||
|
--#{$prefix}btn-close-color: #{$btn-close-color};
|
||||||
|
--#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg) };
|
||||||
|
--#{$prefix}btn-close-color-light: #{$btn-close-color-light};
|
||||||
|
--#{$prefix}btn-close-bg-light: #{ escape-svg($btn-close-bg-light) };
|
||||||
|
--#{$prefix}btn-close-color-dark: #{$btn-close-color-dark};
|
||||||
|
--#{$prefix}btn-close-bg-dark: #{ escape-svg($btn-close-bg-dark) };
|
||||||
|
--#{$prefix}btn-close-opacity: #{$btn-close-opacity};
|
||||||
|
--#{$prefix}btn-close-hover-opacity: #{$btn-close-hover-opacity};
|
||||||
|
--#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow};
|
||||||
|
--#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity};
|
||||||
|
--#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity};
|
||||||
|
--#{$prefix}btn-close-white-filter: #{$btn-close-white-filter};
|
||||||
|
// scss-docs-end close-css-vars
|
||||||
|
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
width: $btn-close-width;
|
width: $btn-close-width;
|
||||||
height: $btn-close-height;
|
height: $btn-close-height;
|
||||||
padding: $btn-close-padding-y $btn-close-padding-x;
|
padding: $btn-close-padding-y $btn-close-padding-x;
|
||||||
color: $btn-close-color;
|
color: var(--#{$prefix}btn-close-color);
|
||||||
background: transparent escape-svg($btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements
|
background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements
|
||||||
border: 0; // for button elements
|
border: 0; // for button elements
|
||||||
@include border-radius();
|
@include border-radius();
|
||||||
opacity: $btn-close-opacity;
|
opacity: var(--#{$prefix}btn-close-opacity);
|
||||||
|
|
||||||
// Override <a>'s hover style
|
// Override <a>'s hover style
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $btn-close-color;
|
color: var(--#{$prefix}btn-close-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
opacity: $btn-close-hover-opacity;
|
opacity: var(--#{$prefix}btn-close-hover-opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: $btn-close-focus-shadow;
|
box-shadow: var(--#{$prefix}btn-close-focus-shadow);
|
||||||
opacity: $btn-close-focus-opacity;
|
opacity: var(--#{$prefix}btn-close-focus-opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled,
|
&:disabled,
|
||||||
&.disabled {
|
&.disabled {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
opacity: $btn-close-disabled-opacity;
|
opacity: var(--#{$prefix}btn-close-disabled-opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-close-white {
|
@mixin btn-close-white() {
|
||||||
filter: $btn-close-white-filter;
|
filter: var(--#{$prefix}btn-close-white-filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close-white {
|
||||||
|
@include btn-close-white();
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark) {
|
||||||
|
.btn-close {
|
||||||
|
@include btn-close-white();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Set the container width, and override it for fixed navbars in media queries.
|
// Set the container width, and override it for fixed navbars in media queries.
|
||||||
|
|
||||||
@if $enable-grid-classes {
|
@if $enable-container-classes {
|
||||||
// Single container class with breakpoint max-widths
|
// Single container class with breakpoint max-widths
|
||||||
.container,
|
.container,
|
||||||
// 100% wide container at all breakpoints
|
// 100% wide container at all breakpoints
|
||||||
|
@ -1,507 +0,0 @@
|
|||||||
// Embedded icons from Open Iconic.
|
|
||||||
// Released under MIT and copyright 2014 Waybury.
|
|
||||||
// https://useiconic.com/open
|
|
||||||
|
|
||||||
|
|
||||||
// Checkboxes and radios
|
|
||||||
//
|
|
||||||
// Base class takes care of all the key behavioral aspects.
|
|
||||||
|
|
||||||
.custom-control {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
min-height: $font-size-base * $line-height-base;
|
|
||||||
padding-left: $custom-control-gutter + $custom-control-indicator-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-inline {
|
|
||||||
display: inline-flex;
|
|
||||||
margin-right: $custom-control-spacer-x;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input {
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1; // Put the input behind the label so it doesn't overlay text
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&:checked ~ .custom-control-label::before {
|
|
||||||
color: $custom-control-indicator-checked-color;
|
|
||||||
border-color: $custom-control-indicator-checked-border-color;
|
|
||||||
@include gradient-bg($custom-control-indicator-checked-bg);
|
|
||||||
@include box-shadow($custom-control-indicator-checked-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus ~ .custom-control-label::before {
|
|
||||||
// the mixin is not used here to make sure there is feedback
|
|
||||||
@if $enable-shadows {
|
|
||||||
box-shadow: $input-box-shadow, $input-focus-box-shadow;
|
|
||||||
} @else {
|
|
||||||
box-shadow: $custom-control-indicator-focus-box-shadow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:focus:not(:checked) ~ .custom-control-label::before {
|
|
||||||
border-color: $custom-control-indicator-focus-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(:disabled):active ~ .custom-control-label::before {
|
|
||||||
color: $custom-control-indicator-active-color;
|
|
||||||
background-color: $custom-control-indicator-active-bg;
|
|
||||||
border-color: $custom-control-indicator-active-border-color;
|
|
||||||
@include box-shadow($custom-control-indicator-active-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
~ .custom-control-label {
|
|
||||||
color: $custom-control-label-disabled-color;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
background-color: $custom-control-indicator-disabled-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom control indicators
|
|
||||||
//
|
|
||||||
// Build the custom controls out of pseudo-elements.
|
|
||||||
|
|
||||||
.custom-control-label {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 0;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
// Background-color and (when enabled) gradient
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
top: ($font-size-base * $line-height-base - $custom-control-indicator-size) * 0.5;
|
|
||||||
left: -($custom-control-gutter + $custom-control-indicator-size);
|
|
||||||
display: block;
|
|
||||||
width: $custom-control-indicator-size;
|
|
||||||
height: $custom-control-indicator-size;
|
|
||||||
pointer-events: none;
|
|
||||||
content: "";
|
|
||||||
background-color: $custom-control-indicator-bg;
|
|
||||||
border: $custom-control-indicator-border-color solid $custom-control-indicator-border-width;
|
|
||||||
@include box-shadow($custom-control-indicator-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Foreground (icon)
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
top: ($font-size-base * $line-height-base - $custom-control-indicator-size) * 0.5;
|
|
||||||
left: -($custom-control-gutter + $custom-control-indicator-size);
|
|
||||||
display: block;
|
|
||||||
width: $custom-control-indicator-size;
|
|
||||||
height: $custom-control-indicator-size;
|
|
||||||
content: "";
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center center;
|
|
||||||
background-size: $custom-control-indicator-bg-size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Checkboxes
|
|
||||||
//
|
|
||||||
// Tweak just a few things for checkboxes.
|
|
||||||
|
|
||||||
.custom-checkbox {
|
|
||||||
.custom-control-label::before {
|
|
||||||
@include border-radius($custom-checkbox-indicator-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:checked ~ .custom-control-label {
|
|
||||||
&::after {
|
|
||||||
background-image: $custom-checkbox-indicator-icon-checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:indeterminate ~ .custom-control-label {
|
|
||||||
&::before {
|
|
||||||
border-color: $custom-checkbox-indicator-indeterminate-border-color;
|
|
||||||
@include gradient-bg($custom-checkbox-indicator-indeterminate-bg);
|
|
||||||
@include box-shadow($custom-checkbox-indicator-indeterminate-box-shadow);
|
|
||||||
}
|
|
||||||
&::after {
|
|
||||||
background-image: $custom-checkbox-indicator-icon-indeterminate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:disabled {
|
|
||||||
&:checked ~ .custom-control-label::before {
|
|
||||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
|
||||||
}
|
|
||||||
&:indeterminate ~ .custom-control-label::before {
|
|
||||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Radios
|
|
||||||
//
|
|
||||||
// Tweak just a few things for radios.
|
|
||||||
|
|
||||||
.custom-radio {
|
|
||||||
.custom-control-label::before {
|
|
||||||
border-radius: $custom-radio-indicator-border-radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:checked ~ .custom-control-label {
|
|
||||||
&::after {
|
|
||||||
background-image: $custom-radio-indicator-icon-checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:disabled {
|
|
||||||
&:checked ~ .custom-control-label::before {
|
|
||||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// switches
|
|
||||||
//
|
|
||||||
// Tweak a few things for switches
|
|
||||||
|
|
||||||
.custom-switch {
|
|
||||||
padding-left: $custom-switch-width + $custom-control-gutter;
|
|
||||||
|
|
||||||
.custom-control-label {
|
|
||||||
&::before {
|
|
||||||
left: -($custom-switch-width + $custom-control-gutter);
|
|
||||||
width: $custom-switch-width;
|
|
||||||
pointer-events: all;
|
|
||||||
border-radius: $custom-switch-indicator-border-radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
top: calc(#{(($font-size-base * $line-height-base - $custom-control-indicator-size) * 0.5)} + #{$custom-control-indicator-border-width * 2});
|
|
||||||
left: calc(#{-($custom-switch-width + $custom-control-gutter)} + #{$custom-control-indicator-border-width * 2});
|
|
||||||
width: $custom-switch-indicator-size;
|
|
||||||
height: $custom-switch-indicator-size;
|
|
||||||
background-color: $custom-control-indicator-border-color;
|
|
||||||
border-radius: $custom-switch-indicator-border-radius;
|
|
||||||
@include transition(transform .15s ease-in-out, $custom-forms-transition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:checked ~ .custom-control-label {
|
|
||||||
&::after {
|
|
||||||
background-color: $custom-control-indicator-bg;
|
|
||||||
transform: translateX($custom-switch-width - $custom-control-indicator-size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-input:disabled {
|
|
||||||
&:checked ~ .custom-control-label::before {
|
|
||||||
background-color: $custom-control-indicator-checked-disabled-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Select
|
|
||||||
//
|
|
||||||
// Replaces the browser default select with a custom one, mostly pulled from
|
|
||||||
// https://primer.github.io/.
|
|
||||||
//
|
|
||||||
|
|
||||||
.custom-select {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
height: $custom-select-height;
|
|
||||||
padding: $custom-select-padding-y ($custom-select-padding-x + $custom-select-indicator-padding) $custom-select-padding-y $custom-select-padding-x;
|
|
||||||
font-weight: $custom-select-font-weight;
|
|
||||||
line-height: $custom-select-line-height;
|
|
||||||
color: $custom-select-color;
|
|
||||||
vertical-align: middle;
|
|
||||||
background: $custom-select-background;
|
|
||||||
background-color: $custom-select-bg;
|
|
||||||
border: $custom-select-border-width solid $custom-select-border-color;
|
|
||||||
@if $enable-rounded {
|
|
||||||
border-radius: $custom-select-border-radius;
|
|
||||||
} @else {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
@include box-shadow($custom-select-box-shadow);
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
border-color: $custom-select-focus-border-color;
|
|
||||||
outline: 0;
|
|
||||||
@if $enable-shadows {
|
|
||||||
box-shadow: $custom-select-box-shadow, $custom-select-focus-box-shadow;
|
|
||||||
} @else {
|
|
||||||
box-shadow: $custom-select-focus-box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-value {
|
|
||||||
// For visual consistency with other platforms/browsers,
|
|
||||||
// suppress the default white text on blue background highlight given to
|
|
||||||
// the selected option text when the (still closed) <select> receives focus
|
|
||||||
// in IE and (under certain conditions) Edge.
|
|
||||||
// See https://github.com/twbs/bootstrap/issues/19398.
|
|
||||||
color: $input-color;
|
|
||||||
background-color: $input-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&[multiple],
|
|
||||||
&[size]:not([size="1"]) {
|
|
||||||
height: auto;
|
|
||||||
padding-right: $custom-select-padding-x;
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: $custom-select-disabled-color;
|
|
||||||
background-color: $custom-select-disabled-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hides the default caret in IE11
|
|
||||||
&::-ms-expand {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select-sm {
|
|
||||||
height: $custom-select-height-sm;
|
|
||||||
padding-top: $custom-select-padding-y-sm;
|
|
||||||
padding-bottom: $custom-select-padding-y-sm;
|
|
||||||
padding-left: $custom-select-padding-x-sm;
|
|
||||||
font-size: $custom-select-font-size-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select-lg {
|
|
||||||
height: $custom-select-height-lg;
|
|
||||||
padding-top: $custom-select-padding-y-lg;
|
|
||||||
padding-bottom: $custom-select-padding-y-lg;
|
|
||||||
padding-left: $custom-select-padding-x-lg;
|
|
||||||
font-size: $custom-select-font-size-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// File
|
|
||||||
//
|
|
||||||
// Custom file input.
|
|
||||||
|
|
||||||
.custom-file {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
height: $custom-file-height;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-file-input {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
width: 100%;
|
|
||||||
height: $custom-file-height;
|
|
||||||
margin: 0;
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
&:focus ~ .custom-file-label {
|
|
||||||
border-color: $custom-file-focus-border-color;
|
|
||||||
box-shadow: $custom-file-focus-box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled ~ .custom-file-label {
|
|
||||||
background-color: $custom-file-disabled-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
@each $lang, $value in $custom-file-text {
|
|
||||||
&:lang(#{$lang}) ~ .custom-file-label::after {
|
|
||||||
content: $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~ .custom-file-label[data-browse]::after {
|
|
||||||
content: attr(data-browse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-file-label {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1;
|
|
||||||
height: $custom-file-height;
|
|
||||||
padding: $custom-file-padding-y $custom-file-padding-x;
|
|
||||||
font-weight: $custom-file-font-weight;
|
|
||||||
line-height: $custom-file-line-height;
|
|
||||||
color: $custom-file-color;
|
|
||||||
background-color: $custom-file-bg;
|
|
||||||
border: $custom-file-border-width solid $custom-file-border-color;
|
|
||||||
@include border-radius($custom-file-border-radius);
|
|
||||||
@include box-shadow($custom-file-box-shadow);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 3;
|
|
||||||
display: block;
|
|
||||||
height: $custom-file-height-inner;
|
|
||||||
padding: $custom-file-padding-y $custom-file-padding-x;
|
|
||||||
line-height: $custom-file-line-height;
|
|
||||||
color: $custom-file-button-color;
|
|
||||||
content: "Browse";
|
|
||||||
@include gradient-bg($custom-file-button-bg);
|
|
||||||
border-left: inherit;
|
|
||||||
@include border-radius(0 $custom-file-border-radius $custom-file-border-radius 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range
|
|
||||||
//
|
|
||||||
// Style range inputs the same across browsers. Vendor-specific rules for pseudo
|
|
||||||
// elements cannot be mixed. As such, there are no shared styles for focus or
|
|
||||||
// active states on prefixed selectors.
|
|
||||||
|
|
||||||
.custom-range {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(#{$custom-range-thumb-height} + #{$custom-range-thumb-focus-box-shadow-width * 2});
|
|
||||||
padding: 0; // Need to reset padding
|
|
||||||
background-color: transparent;
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
// Pseudo-elements must be split across multiple rulesets to have an effect.
|
|
||||||
// No box-shadow() mixin for focus accessibility.
|
|
||||||
&::-webkit-slider-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
|
||||||
&::-moz-range-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
|
||||||
&::-ms-thumb { box-shadow: $custom-range-thumb-focus-box-shadow; }
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-focus-outer {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-slider-thumb {
|
|
||||||
width: $custom-range-thumb-width;
|
|
||||||
height: $custom-range-thumb-height;
|
|
||||||
margin-top: ($custom-range-track-height - $custom-range-thumb-height) * 0.5; // Webkit specific
|
|
||||||
@include gradient-bg($custom-range-thumb-bg);
|
|
||||||
border: $custom-range-thumb-border;
|
|
||||||
@include border-radius($custom-range-thumb-border-radius);
|
|
||||||
@include box-shadow($custom-range-thumb-box-shadow);
|
|
||||||
@include transition($custom-forms-transition);
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
@include gradient-bg($custom-range-thumb-active-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-slider-runnable-track {
|
|
||||||
width: $custom-range-track-width;
|
|
||||||
height: $custom-range-track-height;
|
|
||||||
color: transparent; // Why?
|
|
||||||
cursor: $custom-range-track-cursor;
|
|
||||||
background-color: $custom-range-track-bg;
|
|
||||||
border-color: transparent;
|
|
||||||
@include border-radius($custom-range-track-border-radius);
|
|
||||||
@include box-shadow($custom-range-track-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-range-thumb {
|
|
||||||
width: $custom-range-thumb-width;
|
|
||||||
height: $custom-range-thumb-height;
|
|
||||||
@include gradient-bg($custom-range-thumb-bg);
|
|
||||||
border: $custom-range-thumb-border;
|
|
||||||
@include border-radius($custom-range-thumb-border-radius);
|
|
||||||
@include box-shadow($custom-range-thumb-box-shadow);
|
|
||||||
@include transition($custom-forms-transition);
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
@include gradient-bg($custom-range-thumb-active-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-range-track {
|
|
||||||
width: $custom-range-track-width;
|
|
||||||
height: $custom-range-track-height;
|
|
||||||
color: transparent;
|
|
||||||
cursor: $custom-range-track-cursor;
|
|
||||||
background-color: $custom-range-track-bg;
|
|
||||||
border-color: transparent; // Firefox specific?
|
|
||||||
@include border-radius($custom-range-track-border-radius);
|
|
||||||
@include box-shadow($custom-range-track-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-thumb {
|
|
||||||
width: $custom-range-thumb-width;
|
|
||||||
height: $custom-range-thumb-height;
|
|
||||||
margin-top: 0; // Edge specific
|
|
||||||
margin-right: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
|
|
||||||
margin-left: $custom-range-thumb-focus-box-shadow-width; // Workaround that overflowed box-shadow is hidden.
|
|
||||||
@include gradient-bg($custom-range-thumb-bg);
|
|
||||||
border: $custom-range-thumb-border;
|
|
||||||
@include border-radius($custom-range-thumb-border-radius);
|
|
||||||
@include box-shadow($custom-range-thumb-box-shadow);
|
|
||||||
@include transition($custom-forms-transition);
|
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
@include gradient-bg($custom-range-thumb-active-bg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-track {
|
|
||||||
width: $custom-range-track-width;
|
|
||||||
height: $custom-range-track-height;
|
|
||||||
color: transparent;
|
|
||||||
cursor: $custom-range-track-cursor;
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: transparent;
|
|
||||||
border-width: $custom-range-thumb-height * 0.5;
|
|
||||||
@include box-shadow($custom-range-track-box-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-fill-lower {
|
|
||||||
background-color: $custom-range-track-bg;
|
|
||||||
@include border-radius($custom-range-track-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-fill-upper {
|
|
||||||
margin-right: 15px; // arbitrary?
|
|
||||||
background-color: $custom-range-track-bg;
|
|
||||||
@include border-radius($custom-range-track-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
&::-webkit-slider-thumb {
|
|
||||||
background-color: $custom-range-thumb-disabled-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-slider-runnable-track {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-range-thumb {
|
|
||||||
background-color: $custom-range-thumb-disabled-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-moz-range-track {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-ms-thumb {
|
|
||||||
background-color: $custom-range-thumb-disabled-bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-control-label::before,
|
|
||||||
.custom-file-label,
|
|
||||||
.custom-select {
|
|
||||||
@include transition($custom-forms-transition);
|
|
||||||
}
|
|
@ -2,7 +2,9 @@
|
|||||||
.dropup,
|
.dropup,
|
||||||
.dropend,
|
.dropend,
|
||||||
.dropdown,
|
.dropdown,
|
||||||
.dropstart {
|
.dropstart,
|
||||||
|
.dropup-center,
|
||||||
|
.dropdown-center {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,26 +17,67 @@
|
|||||||
|
|
||||||
// The dropdown menu
|
// The dropdown menu
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
|
// scss-docs-start dropdown-css-vars
|
||||||
|
--#{$prefix}dropdown-zindex: #{$zindex-dropdown};
|
||||||
|
--#{$prefix}dropdown-min-width: #{$dropdown-min-width};
|
||||||
|
--#{$prefix}dropdown-padding-x: #{$dropdown-padding-x};
|
||||||
|
--#{$prefix}dropdown-padding-y: #{$dropdown-padding-y};
|
||||||
|
--#{$prefix}dropdown-spacer: #{$dropdown-spacer};
|
||||||
|
@include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size);
|
||||||
|
--#{$prefix}dropdown-color: #{$dropdown-color};
|
||||||
|
--#{$prefix}dropdown-bg: #{$dropdown-bg};
|
||||||
|
--#{$prefix}dropdown-border-color: #{$dropdown-border-color};
|
||||||
|
--#{$prefix}dropdown-border-radius: #{$dropdown-border-radius};
|
||||||
|
--#{$prefix}dropdown-border-width: #{$dropdown-border-width};
|
||||||
|
--#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius};
|
||||||
|
--#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg};
|
||||||
|
--#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y};
|
||||||
|
--#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow};
|
||||||
|
--#{$prefix}dropdown-link-color: #{$dropdown-link-color};
|
||||||
|
--#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color};
|
||||||
|
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg};
|
||||||
|
--#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color};
|
||||||
|
--#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg};
|
||||||
|
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color};
|
||||||
|
--#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x};
|
||||||
|
--#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y};
|
||||||
|
--#{$prefix}dropdown-header-color: #{$dropdown-header-color};
|
||||||
|
--#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x};
|
||||||
|
--#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y};
|
||||||
|
// scss-docs-end dropdown-css-vars
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: $zindex-dropdown;
|
z-index: var(--#{$prefix}dropdown-zindex);
|
||||||
display: none; // none by default, but block on "open" of the menu
|
display: none; // none by default, but block on "open" of the menu
|
||||||
min-width: $dropdown-min-width;
|
min-width: var(--#{$prefix}dropdown-min-width);
|
||||||
padding: $dropdown-padding-y $dropdown-padding-x;
|
padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x);
|
||||||
margin: 0; // Override default margin of ul
|
margin: 0; // Override default margin of ul
|
||||||
@include font-size($dropdown-font-size);
|
@include font-size(var(--#{$prefix}dropdown-font-size));
|
||||||
color: $dropdown-color;
|
color: var(--#{$prefix}dropdown-color);
|
||||||
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
|
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
|
||||||
list-style: none;
|
list-style: none;
|
||||||
background-color: $dropdown-bg;
|
background-color: var(--#{$prefix}dropdown-bg);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: $dropdown-border-width solid $dropdown-border-color;
|
border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color);
|
||||||
@include border-radius($dropdown-border-radius);
|
@include border-radius(var(--#{$prefix}dropdown-border-radius));
|
||||||
@include box-shadow($dropdown-box-shadow);
|
@include box-shadow(var(--#{$prefix}dropdown-box-shadow));
|
||||||
|
|
||||||
&[data-bs-popper] {
|
&[data-bs-popper] {
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
margin-top: $dropdown-spacer;
|
margin-top: var(--#{$prefix}dropdown-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $dropdown-padding-y == 0 {
|
||||||
|
> .dropdown-item:first-child,
|
||||||
|
> li:first-child .dropdown-item {
|
||||||
|
@include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius));
|
||||||
|
}
|
||||||
|
> .dropdown-item:last-child,
|
||||||
|
> li:last-child .dropdown-item {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +117,7 @@
|
|||||||
top: auto;
|
top: auto;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: $dropdown-spacer;
|
margin-bottom: var(--#{$prefix}dropdown-spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-toggle {
|
.dropdown-toggle {
|
||||||
@ -88,7 +131,7 @@
|
|||||||
right: auto;
|
right: auto;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-left: $dropdown-spacer;
|
margin-left: var(--#{$prefix}dropdown-spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-toggle {
|
.dropdown-toggle {
|
||||||
@ -105,7 +148,7 @@
|
|||||||
right: 100%;
|
right: 100%;
|
||||||
left: auto;
|
left: auto;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-right: $dropdown-spacer;
|
margin-right: var(--#{$prefix}dropdown-spacer);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-toggle {
|
.dropdown-toggle {
|
||||||
@ -120,9 +163,10 @@
|
|||||||
// Dividers (basically an `<hr>`) within the dropdown
|
// Dividers (basically an `<hr>`) within the dropdown
|
||||||
.dropdown-divider {
|
.dropdown-divider {
|
||||||
height: 0;
|
height: 0;
|
||||||
margin: $dropdown-divider-margin-y 0;
|
margin: var(--#{$prefix}dropdown-divider-margin-y) 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-top: 1px solid $dropdown-divider-bg;
|
border-top: 1px solid var(--#{$prefix}dropdown-divider-bg);
|
||||||
|
opacity: 1; // Revisit in v6 to de-dupe styles that conflict with <hr> element
|
||||||
}
|
}
|
||||||
|
|
||||||
// Links, buttons, and more within the dropdown menu
|
// Links, buttons, and more within the dropdown menu
|
||||||
@ -131,45 +175,34 @@
|
|||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%; // For `<button>`s
|
width: 100%; // For `<button>`s
|
||||||
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
|
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
|
||||||
clear: both;
|
clear: both;
|
||||||
font-weight: $font-weight-normal;
|
font-weight: $font-weight-normal;
|
||||||
color: $dropdown-link-color;
|
color: var(--#{$prefix}dropdown-link-color);
|
||||||
text-align: inherit; // For `<button>`s
|
text-align: inherit; // For `<button>`s
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
white-space: nowrap; // prevent links from randomly breaking onto new lines
|
white-space: nowrap; // prevent links from randomly breaking onto new lines
|
||||||
background-color: transparent; // For `<button>`s
|
background-color: transparent; // For `<button>`s
|
||||||
border: 0; // For `<button>`s
|
border: 0; // For `<button>`s
|
||||||
|
@include border-radius(var(--#{$prefix}dropdown-item-border-radius, 0));
|
||||||
// Prevent dropdown overflow if there's no padding
|
|
||||||
// See https://github.com/twbs/bootstrap/pull/27703
|
|
||||||
@if $dropdown-padding-y == 0 {
|
|
||||||
&:first-child {
|
|
||||||
@include border-top-radius($dropdown-inner-border-radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
@include border-bottom-radius($dropdown-inner-border-radius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: $dropdown-link-hover-color;
|
color: var(--#{$prefix}dropdown-link-hover-color);
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
@include gradient-bg($dropdown-link-hover-bg);
|
@include gradient-bg(var(--#{$prefix}dropdown-link-hover-bg));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active,
|
&.active,
|
||||||
&:active {
|
&:active {
|
||||||
color: $dropdown-link-active-color;
|
color: var(--#{$prefix}dropdown-link-active-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@include gradient-bg($dropdown-link-active-bg);
|
@include gradient-bg(var(--#{$prefix}dropdown-link-active-bg));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled,
|
&.disabled,
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: $dropdown-link-disabled-color;
|
color: var(--#{$prefix}dropdown-link-disabled-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
// Remove CSS gradients if they're enabled
|
// Remove CSS gradients if they're enabled
|
||||||
@ -184,57 +217,34 @@
|
|||||||
// Dropdown section headers
|
// Dropdown section headers
|
||||||
.dropdown-header {
|
.dropdown-header {
|
||||||
display: block;
|
display: block;
|
||||||
padding: $dropdown-header-padding;
|
padding: var(--#{$prefix}dropdown-header-padding-y) var(--#{$prefix}dropdown-header-padding-x);
|
||||||
margin-bottom: 0; // for use with heading elements
|
margin-bottom: 0; // for use with heading elements
|
||||||
@include font-size($font-size-sm);
|
@include font-size($font-size-sm);
|
||||||
color: $dropdown-header-color;
|
color: var(--#{$prefix}dropdown-header-color);
|
||||||
white-space: nowrap; // as with > li > a
|
white-space: nowrap; // as with > li > a
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dropdown text
|
// Dropdown text
|
||||||
.dropdown-item-text {
|
.dropdown-item-text {
|
||||||
display: block;
|
display: block;
|
||||||
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
|
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
|
||||||
color: $dropdown-link-color;
|
color: var(--#{$prefix}dropdown-link-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dark dropdowns
|
// Dark dropdowns
|
||||||
.dropdown-menu-dark {
|
.dropdown-menu-dark {
|
||||||
color: $dropdown-dark-color;
|
// scss-docs-start dropdown-dark-css-vars
|
||||||
background-color: $dropdown-dark-bg;
|
--#{$prefix}dropdown-color: #{$dropdown-dark-color};
|
||||||
border-color: $dropdown-dark-border-color;
|
--#{$prefix}dropdown-bg: #{$dropdown-dark-bg};
|
||||||
@include box-shadow($dropdown-dark-box-shadow);
|
--#{$prefix}dropdown-border-color: #{$dropdown-dark-border-color};
|
||||||
|
--#{$prefix}dropdown-box-shadow: #{$dropdown-dark-box-shadow};
|
||||||
.dropdown-item {
|
--#{$prefix}dropdown-link-color: #{$dropdown-dark-link-color};
|
||||||
color: $dropdown-dark-link-color;
|
--#{$prefix}dropdown-link-hover-color: #{$dropdown-dark-link-hover-color};
|
||||||
|
--#{$prefix}dropdown-divider-bg: #{$dropdown-dark-divider-bg};
|
||||||
&:hover,
|
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-dark-link-hover-bg};
|
||||||
&:focus {
|
--#{$prefix}dropdown-link-active-color: #{$dropdown-dark-link-active-color};
|
||||||
color: $dropdown-dark-link-hover-color;
|
--#{$prefix}dropdown-link-active-bg: #{$dropdown-dark-link-active-bg};
|
||||||
@include gradient-bg($dropdown-dark-link-hover-bg);
|
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-dark-link-disabled-color};
|
||||||
}
|
--#{$prefix}dropdown-header-color: #{$dropdown-dark-header-color};
|
||||||
|
// scss-docs-end dropdown-dark-css-vars
|
||||||
&.active,
|
|
||||||
&:active {
|
|
||||||
color: $dropdown-dark-link-active-color;
|
|
||||||
@include gradient-bg($dropdown-dark-link-active-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled,
|
|
||||||
&:disabled {
|
|
||||||
color: $dropdown-dark-link-disabled-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-divider {
|
|
||||||
border-color: $dropdown-dark-divider-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item-text {
|
|
||||||
color: $dropdown-dark-link-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-header {
|
|
||||||
color: $dropdown-dark-header-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
@function to-rgb($value) {
|
||||||
|
@return red($value), green($value), blue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stylelint-disable scss/dollar-variable-pattern
|
||||||
|
@function rgba-css-var($identifier, $target) {
|
||||||
|
@if $identifier == "body" and $target == "bg" {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-bg-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
} @if $identifier == "body" and $target == "text" {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-color-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
} @else {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@function map-loop($map, $func, $args...) {
|
||||||
|
$_map: ();
|
||||||
|
|
||||||
|
@each $key, $value in $map {
|
||||||
|
// allow to pass the $key and $value of the map as an function argument
|
||||||
|
$_args: ();
|
||||||
|
@each $arg in $args {
|
||||||
|
$_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$_map: map-merge($_map, ($key: call(get-function($func), $_args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $_map;
|
||||||
|
}
|
||||||
|
// stylelint-enable scss/dollar-variable-pattern
|
||||||
|
|
||||||
|
@function varify($list) {
|
||||||
|
$result: null;
|
||||||
|
@each $entry in $list {
|
||||||
|
$result: append($result, var(--#{$prefix}#{$entry}), space);
|
||||||
|
}
|
||||||
|
@return $result;
|
||||||
|
}
|
||||||
|
|
||||||
// Internal Bootstrap function to turn maps into its negative variant.
|
// Internal Bootstrap function to turn maps into its negative variant.
|
||||||
// It prefixes the keys with `n` and makes the value negative.
|
// It prefixes the keys with `n` and makes the value negative.
|
||||||
@function negativify-map($map) {
|
@function negativify-map($map) {
|
||||||
@ -55,10 +96,20 @@
|
|||||||
@return $result;
|
@return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge multiple maps
|
||||||
|
@function map-merge-multiple($maps...) {
|
||||||
|
$merged-maps: ();
|
||||||
|
|
||||||
|
@each $map in $maps {
|
||||||
|
$merged-maps: map-merge($merged-maps, $map);
|
||||||
|
}
|
||||||
|
@return $merged-maps;
|
||||||
|
}
|
||||||
|
|
||||||
// Replace `$search` with `$replace` in `$string`
|
// Replace `$search` with `$replace` in `$string`
|
||||||
// Used on our SVG icon backgrounds for custom forms.
|
// Used on our SVG icon backgrounds for custom forms.
|
||||||
//
|
//
|
||||||
// @author Hugo Giraudel
|
// @author Kitty Giraudel
|
||||||
// @param {String} $string - Initial string
|
// @param {String} $string - Initial string
|
||||||
// @param {String} $search - Substring to replace
|
// @param {String} $search - Substring to replace
|
||||||
// @param {String} $replace ('') - New value
|
// @param {String} $replace ('') - New value
|
||||||
@ -126,9 +177,9 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
|
|||||||
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
|
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return WCAG2.0 relative luminance
|
// Return WCAG2.1 relative luminance
|
||||||
// See https://www.w3.org/WAI/GL/wiki/Relative_luminance
|
// See https://www.w3.org/TR/WCAG/#dfn-relative-luminance
|
||||||
// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
|
// See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio
|
||||||
@function luminance($color) {
|
@function luminance($color) {
|
||||||
$rgb: (
|
$rgb: (
|
||||||
"r": red($color),
|
"r": red($color),
|
||||||
@ -137,7 +188,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
|
|||||||
);
|
);
|
||||||
|
|
||||||
@each $name, $value in $rgb {
|
@each $name, $value in $rgb {
|
||||||
$value: if(divide($value, 255) < .03928, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1));
|
$value: if(divide($value, 255) < .04045, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1));
|
||||||
$rgb: map-merge($rgb, ($name: $value));
|
$rgb: map-merge($rgb, ($name: $value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +198,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
|
|||||||
// Return opaque color
|
// Return opaque color
|
||||||
// opaque(#fff, rgba(0, 0, 0, .5)) => #808080
|
// opaque(#fff, rgba(0, 0, 0, .5)) => #808080
|
||||||
@function opaque($background, $foreground) {
|
@function opaque($background, $foreground) {
|
||||||
@return mix(rgba($foreground, 1), $background, opacity($foreground) * 100 * 1%);
|
@return mix(rgba($foreground, 1), $background, opacity($foreground) * 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
// scss-docs-start color-functions
|
// scss-docs-start color-functions
|
||||||
@ -181,14 +232,6 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
|
|||||||
@return $value1 + $value2;
|
@return $value1 + $value2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@if type-of($value1) != number {
|
|
||||||
$value1: unquote("(") + $value1 + unquote(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@if type-of($value2) != number {
|
|
||||||
$value2: unquote("(") + $value2 + unquote(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2);
|
@return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,10 +252,6 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003
|
|||||||
@return $value1 - $value2;
|
@return $value1 - $value2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@if type-of($value1) != number {
|
|
||||||
$value1: unquote("(") + $value1 + unquote(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@if type-of($value2) != number {
|
@if type-of($value2) != number {
|
||||||
$value2: unquote("(") + $value2 + unquote(")");
|
$value2: unquote("(") + $value2 + unquote(")");
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
//
|
//
|
||||||
// Rows contain your columns.
|
// Rows contain your columns.
|
||||||
|
|
||||||
|
:root {
|
||||||
|
@each $name, $value in $grid-breakpoints {
|
||||||
|
--#{$prefix}breakpoint-#{$name}: #{$value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@if $enable-grid-classes {
|
@if $enable-grid-classes {
|
||||||
.row {
|
.row {
|
||||||
@include make-row();
|
@include make-row();
|
||||||
@ -12,6 +18,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if $enable-cssgrid {
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);
|
||||||
|
grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);
|
||||||
|
gap: var(--#{$prefix}gap, #{$grid-gutter-width});
|
||||||
|
|
||||||
|
@include make-cssgrid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
//
|
//
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
@import "helpers/clearfix";
|
@import "helpers/clearfix";
|
||||||
|
@import "helpers/color-bg";
|
||||||
@import "helpers/colored-links";
|
@import "helpers/colored-links";
|
||||||
|
@import "helpers/focus-ring";
|
||||||
|
@import "helpers/icon-link";
|
||||||
@import "helpers/ratio";
|
@import "helpers/ratio";
|
||||||
@import "helpers/position";
|
@import "helpers/position";
|
||||||
|
@import "helpers/stacks";
|
||||||
@import "helpers/visually-hidden";
|
@import "helpers/visually-hidden";
|
||||||
@import "helpers/stretched-link";
|
@import "helpers/stretched-link";
|
||||||
@import "helpers/text-truncation";
|
@import "helpers/text-truncation";
|
||||||
|
@import "helpers/vr";
|
||||||
|
@ -1,193 +0,0 @@
|
|||||||
// stylelint-disable selector-no-qualifying-type
|
|
||||||
|
|
||||||
//
|
|
||||||
// Base styles
|
|
||||||
//
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap; // For form validation feedback
|
|
||||||
align-items: stretch;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> .form-control,
|
|
||||||
> .form-control-plaintext,
|
|
||||||
> .custom-select,
|
|
||||||
> .custom-file {
|
|
||||||
position: relative; // For focus state's z-index
|
|
||||||
flex: 1 1 auto;
|
|
||||||
// Add width 1% and flex-basis auto to ensure that button will not wrap out
|
|
||||||
// the column. Applies to IE Edge+ and Firefox. Chrome does not require this.
|
|
||||||
width: 1%;
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
+ .form-control,
|
|
||||||
+ .custom-select,
|
|
||||||
+ .custom-file {
|
|
||||||
margin-left: -$input-border-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bring the "active" form control to the top of surrounding elements
|
|
||||||
> .form-control:focus,
|
|
||||||
> .custom-select:focus,
|
|
||||||
> .custom-file .custom-file-input:focus ~ .custom-file-label {
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bring the custom file input above the label
|
|
||||||
> .custom-file .custom-file-input:focus {
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .form-control,
|
|
||||||
> .custom-select {
|
|
||||||
&:not(:last-child) { @include border-right-radius(0); }
|
|
||||||
&:not(:first-child) { @include border-left-radius(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom file inputs have more complex markup, thus requiring different
|
|
||||||
// border-radius overrides.
|
|
||||||
> .custom-file {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:not(:last-child) .custom-file-label,
|
|
||||||
&:not(:last-child) .custom-file-label::after { @include border-right-radius(0); }
|
|
||||||
&:not(:first-child) .custom-file-label { @include border-left-radius(0); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Prepend and append
|
|
||||||
//
|
|
||||||
// While it requires one extra layer of HTML for each, dedicated prepend and
|
|
||||||
// append elements allow us to 1) be less clever, 2) simplify our selectors, and
|
|
||||||
// 3) support HTML5 form validation.
|
|
||||||
|
|
||||||
.input-group-prepend,
|
|
||||||
.input-group-append {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
// Ensure buttons are always above inputs for more visually pleasing borders.
|
|
||||||
// This isn't needed for `.input-group-text` since it shares the same border-color
|
|
||||||
// as our inputs.
|
|
||||||
.btn {
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn + .btn,
|
|
||||||
.btn + .input-group-text,
|
|
||||||
.input-group-text + .input-group-text,
|
|
||||||
.input-group-text + .btn {
|
|
||||||
margin-left: -$input-border-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-prepend { margin-right: -$input-border-width; }
|
|
||||||
.input-group-append { margin-left: -$input-border-width; }
|
|
||||||
|
|
||||||
|
|
||||||
// Textual addons
|
|
||||||
//
|
|
||||||
// Serves as a catch-all element for any text or radio/checkbox input you wish
|
|
||||||
// to prepend or append to an input.
|
|
||||||
|
|
||||||
.input-group-text {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: $input-padding-y $input-padding-x;
|
|
||||||
margin-bottom: 0; // Allow use of <label> elements by overriding our default margin-bottom
|
|
||||||
font-size: $font-size-base; // Match inputs
|
|
||||||
font-weight: $font-weight-normal;
|
|
||||||
line-height: $input-line-height;
|
|
||||||
color: $input-group-addon-color;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
background-color: $input-group-addon-bg;
|
|
||||||
border: $input-border-width solid $input-group-addon-border-color;
|
|
||||||
@include border-radius($input-border-radius);
|
|
||||||
|
|
||||||
// Nuke default margins from checkboxes and radios to vertically center within.
|
|
||||||
input[type="radio"],
|
|
||||||
input[type="checkbox"] {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sizing
|
|
||||||
//
|
|
||||||
// Remix the default form control sizing classes into new ones for easier
|
|
||||||
// manipulation.
|
|
||||||
|
|
||||||
.input-group-lg > .form-control:not(textarea),
|
|
||||||
.input-group-lg > .custom-select {
|
|
||||||
height: $input-height-lg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-lg > .form-control,
|
|
||||||
.input-group-lg > .custom-select,
|
|
||||||
.input-group-lg > .input-group-prepend > .input-group-text,
|
|
||||||
.input-group-lg > .input-group-append > .input-group-text,
|
|
||||||
.input-group-lg > .input-group-prepend > .btn,
|
|
||||||
.input-group-lg > .input-group-append > .btn {
|
|
||||||
padding: $input-padding-y-lg $input-padding-x-lg;
|
|
||||||
font-size: $input-font-size-lg;
|
|
||||||
line-height: $input-line-height-lg;
|
|
||||||
@include border-radius($input-border-radius-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-sm > .form-control:not(textarea),
|
|
||||||
.input-group-sm > .custom-select {
|
|
||||||
height: $input-height-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-sm > .form-control,
|
|
||||||
.input-group-sm > .custom-select,
|
|
||||||
.input-group-sm > .input-group-prepend > .input-group-text,
|
|
||||||
.input-group-sm > .input-group-append > .input-group-text,
|
|
||||||
.input-group-sm > .input-group-prepend > .btn,
|
|
||||||
.input-group-sm > .input-group-append > .btn {
|
|
||||||
padding: $input-padding-y-sm $input-padding-x-sm;
|
|
||||||
font-size: $input-font-size-sm;
|
|
||||||
line-height: $input-line-height-sm;
|
|
||||||
@include border-radius($input-border-radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-lg > .custom-select,
|
|
||||||
.input-group-sm > .custom-select {
|
|
||||||
padding-right: $custom-select-padding-x + $custom-select-indicator-padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Prepend and append rounded corners
|
|
||||||
//
|
|
||||||
// These rulesets must come after the sizing ones to properly override sm and lg
|
|
||||||
// border-radius values when extending. They're more specific than we'd like
|
|
||||||
// with the `.input-group >` part, but without it, we cannot override the sizing.
|
|
||||||
|
|
||||||
|
|
||||||
.input-group > .input-group-prepend > .btn,
|
|
||||||
.input-group > .input-group-prepend > .input-group-text,
|
|
||||||
.input-group > .input-group-append:not(:last-child) > .btn,
|
|
||||||
.input-group > .input-group-append:not(:last-child) > .input-group-text,
|
|
||||||
.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
|
|
||||||
.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
|
|
||||||
@include border-right-radius(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group > .input-group-append > .btn,
|
|
||||||
.input-group > .input-group-append > .input-group-text,
|
|
||||||
.input-group > .input-group-prepend:not(:first-child) > .btn,
|
|
||||||
.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
|
|
||||||
.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
|
|
||||||
.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
|
|
||||||
@include border-left-radius(0);
|
|
||||||
}
|
|
@ -3,27 +3,46 @@
|
|||||||
// Easily usable on <ul>, <ol>, or <div>.
|
// Easily usable on <ul>, <ol>, or <div>.
|
||||||
|
|
||||||
.list-group {
|
.list-group {
|
||||||
|
// scss-docs-start list-group-css-vars
|
||||||
|
--#{$prefix}list-group-color: #{$list-group-color};
|
||||||
|
--#{$prefix}list-group-bg: #{$list-group-bg};
|
||||||
|
--#{$prefix}list-group-border-color: #{$list-group-border-color};
|
||||||
|
--#{$prefix}list-group-border-width: #{$list-group-border-width};
|
||||||
|
--#{$prefix}list-group-border-radius: #{$list-group-border-radius};
|
||||||
|
--#{$prefix}list-group-item-padding-x: #{$list-group-item-padding-x};
|
||||||
|
--#{$prefix}list-group-item-padding-y: #{$list-group-item-padding-y};
|
||||||
|
--#{$prefix}list-group-action-color: #{$list-group-action-color};
|
||||||
|
--#{$prefix}list-group-action-hover-color: #{$list-group-action-hover-color};
|
||||||
|
--#{$prefix}list-group-action-hover-bg: #{$list-group-hover-bg};
|
||||||
|
--#{$prefix}list-group-action-active-color: #{$list-group-action-active-color};
|
||||||
|
--#{$prefix}list-group-action-active-bg: #{$list-group-action-active-bg};
|
||||||
|
--#{$prefix}list-group-disabled-color: #{$list-group-disabled-color};
|
||||||
|
--#{$prefix}list-group-disabled-bg: #{$list-group-disabled-bg};
|
||||||
|
--#{$prefix}list-group-active-color: #{$list-group-active-color};
|
||||||
|
--#{$prefix}list-group-active-bg: #{$list-group-active-bg};
|
||||||
|
--#{$prefix}list-group-active-border-color: #{$list-group-active-border-color};
|
||||||
|
// scss-docs-end list-group-css-vars
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
// No need to set list-style: none; since .list-group-item is block level
|
// No need to set list-style: none; since .list-group-item is block level
|
||||||
padding-left: 0; // reset padding because ul and ol
|
padding-left: 0; // reset padding because ul and ol
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@include border-radius($list-group-border-radius);
|
@include border-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-group-numbered {
|
.list-group-numbered {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
counter-reset: section;
|
counter-reset: section;
|
||||||
|
|
||||||
> li::before {
|
> .list-group-item::before {
|
||||||
// Increments only this instance of the section counter
|
// Increments only this instance of the section counter
|
||||||
content: counters(section, ".") ". ";
|
content: counters(section, ".") ". ";
|
||||||
counter-increment: section;
|
counter-increment: section;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Interactive list items
|
// Interactive list items
|
||||||
//
|
//
|
||||||
// Use anchor or button elements instead of `li`s or `div`s to create interactive
|
// Use anchor or button elements instead of `li`s or `div`s to create interactive
|
||||||
@ -31,25 +50,24 @@
|
|||||||
|
|
||||||
.list-group-item-action {
|
.list-group-item-action {
|
||||||
width: 100%; // For `<button>`s (anchors become 100% by default though)
|
width: 100%; // For `<button>`s (anchors become 100% by default though)
|
||||||
color: $list-group-action-color;
|
color: var(--#{$prefix}list-group-action-color);
|
||||||
text-align: inherit; // For `<button>`s (anchors inherit)
|
text-align: inherit; // For `<button>`s (anchors inherit)
|
||||||
|
|
||||||
// Hover state
|
// Hover state
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
z-index: 1; // Place hover/focus items above their siblings for proper border styling
|
z-index: 1; // Place hover/focus items above their siblings for proper border styling
|
||||||
color: $list-group-action-hover-color;
|
color: var(--#{$prefix}list-group-action-hover-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background-color: $list-group-hover-bg;
|
background-color: var(--#{$prefix}list-group-action-hover-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
color: $list-group-action-active-color;
|
color: var(--#{$prefix}list-group-action-active-color);
|
||||||
background-color: $list-group-action-active-bg;
|
background-color: var(--#{$prefix}list-group-action-active-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Individual list items
|
// Individual list items
|
||||||
//
|
//
|
||||||
// Use on `li`s or `div`s within the `.list-group` parent.
|
// Use on `li`s or `div`s within the `.list-group` parent.
|
||||||
@ -57,11 +75,11 @@
|
|||||||
.list-group-item {
|
.list-group-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
padding: $list-group-item-padding-y $list-group-item-padding-x;
|
padding: var(--#{$prefix}list-group-item-padding-y) var(--#{$prefix}list-group-item-padding-x);
|
||||||
color: $list-group-color;
|
color: var(--#{$prefix}list-group-color);
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
background-color: $list-group-bg;
|
background-color: var(--#{$prefix}list-group-bg);
|
||||||
border: $list-group-border-width solid $list-group-border-color;
|
border: var(--#{$prefix}list-group-border-width) solid var(--#{$prefix}list-group-border-color);
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
@include border-top-radius(inherit);
|
@include border-top-radius(inherit);
|
||||||
@ -73,30 +91,30 @@
|
|||||||
|
|
||||||
&.disabled,
|
&.disabled,
|
||||||
&:disabled {
|
&:disabled {
|
||||||
color: $list-group-disabled-color;
|
color: var(--#{$prefix}list-group-disabled-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: $list-group-disabled-bg;
|
background-color: var(--#{$prefix}list-group-disabled-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include both here for `<a>`s and `<button>`s
|
// Include both here for `<a>`s and `<button>`s
|
||||||
&.active {
|
&.active {
|
||||||
z-index: 2; // Place active items above their siblings for proper border styling
|
z-index: 2; // Place active items above their siblings for proper border styling
|
||||||
color: $list-group-active-color;
|
color: var(--#{$prefix}list-group-active-color);
|
||||||
background-color: $list-group-active-bg;
|
background-color: var(--#{$prefix}list-group-active-bg);
|
||||||
border-color: $list-group-active-border-color;
|
border-color: var(--#{$prefix}list-group-active-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
& + & {
|
// stylelint-disable-next-line scss/selector-no-redundant-nesting-selector
|
||||||
|
& + .list-group-item {
|
||||||
border-top-width: 0;
|
border-top-width: 0;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
margin-top: -$list-group-border-width;
|
margin-top: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
border-top-width: $list-group-border-width;
|
border-top-width: var(--#{$prefix}list-group-border-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Horizontal
|
// Horizontal
|
||||||
//
|
//
|
||||||
// Change the layout of list group items from vertical (default) to horizontal.
|
// Change the layout of list group items from vertical (default) to horizontal.
|
||||||
@ -109,13 +127,13 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
> .list-group-item {
|
> .list-group-item {
|
||||||
&:first-child {
|
&:first-child:not(:last-child) {
|
||||||
@include border-bottom-start-radius($list-group-border-radius);
|
@include border-bottom-start-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
@include border-top-end-radius(0);
|
@include border-top-end-radius(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child:not(:first-child) {
|
||||||
@include border-top-end-radius($list-group-border-radius);
|
@include border-top-end-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
@include border-bottom-start-radius(0);
|
@include border-bottom-start-radius(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,12 +142,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ .list-group-item {
|
+ .list-group-item {
|
||||||
border-top-width: $list-group-border-width;
|
border-top-width: var(--#{$prefix}list-group-border-width);
|
||||||
border-left-width: 0;
|
border-left-width: 0;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
margin-left: -$list-group-border-width;
|
margin-left: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
border-left-width: $list-group-border-width;
|
border-left-width: var(--#{$prefix}list-group-border-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +165,7 @@
|
|||||||
@include border-radius(0);
|
@include border-radius(0);
|
||||||
|
|
||||||
> .list-group-item {
|
> .list-group-item {
|
||||||
border-width: 0 0 $list-group-border-width;
|
border-width: 0 0 var(--#{$prefix}list-group-border-width);
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
@ -162,13 +180,18 @@
|
|||||||
// Add modifier classes to change text and background color on individual items.
|
// Add modifier classes to change text and background color on individual items.
|
||||||
// Organizationally, this must come after the `:hover` states.
|
// Organizationally, this must come after the `:hover` states.
|
||||||
|
|
||||||
@each $state, $value in $theme-colors {
|
@each $state in map-keys($theme-colors) {
|
||||||
$list-group-variant-bg: shift-color($value, $list-group-item-bg-scale);
|
.list-group-item-#{$state} {
|
||||||
$list-group-variant-color: shift-color($value, $list-group-item-color-scale);
|
--#{$prefix}list-group-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
@if (contrast-ratio($list-group-variant-bg, $list-group-variant-color) < $min-contrast-ratio) {
|
--#{$prefix}list-group-bg: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
$list-group-variant-color: mix($value, color-contrast($list-group-variant-bg), abs($list-group-item-color-scale));
|
--#{$prefix}list-group-border-color: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-action-hover-color: var(--#{$prefix}emphasis-color);
|
||||||
|
--#{$prefix}list-group-action-hover-bg: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-action-active-color: var(--#{$prefix}emphasis-color);
|
||||||
|
--#{$prefix}list-group-action-active-bg: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-active-color: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
|
--#{$prefix}list-group-active-bg: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
--#{$prefix}list-group-active-border-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@include list-group-item-variant($state, $list-group-variant-bg, $list-group-variant-color);
|
|
||||||
}
|
}
|
||||||
// scss-docs-end list-group-modifiers
|
// scss-docs-end list-group-modifiers
|
||||||
|
174
resources/themes/architect/src/components/bootstrap5/_maps.scss
vendored
Normal file
174
resources/themes/architect/src/components/bootstrap5/_maps.scss
vendored
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// Re-assigned maps
|
||||||
|
//
|
||||||
|
// Placed here so that others can override the default Sass maps and see automatic updates to utilities and more.
|
||||||
|
|
||||||
|
// scss-docs-start theme-colors-rgb
|
||||||
|
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default;
|
||||||
|
// scss-docs-end theme-colors-rgb
|
||||||
|
|
||||||
|
// scss-docs-start theme-text-map
|
||||||
|
$theme-colors-text: (
|
||||||
|
"primary": $primary-text-emphasis,
|
||||||
|
"secondary": $secondary-text-emphasis,
|
||||||
|
"success": $success-text-emphasis,
|
||||||
|
"info": $info-text-emphasis,
|
||||||
|
"warning": $warning-text-emphasis,
|
||||||
|
"danger": $danger-text-emphasis,
|
||||||
|
"light": $light-text-emphasis,
|
||||||
|
"dark": $dark-text-emphasis,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-text-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-bg-subtle-map
|
||||||
|
$theme-colors-bg-subtle: (
|
||||||
|
"primary": $primary-bg-subtle,
|
||||||
|
"secondary": $secondary-bg-subtle,
|
||||||
|
"success": $success-bg-subtle,
|
||||||
|
"info": $info-bg-subtle,
|
||||||
|
"warning": $warning-bg-subtle,
|
||||||
|
"danger": $danger-bg-subtle,
|
||||||
|
"light": $light-bg-subtle,
|
||||||
|
"dark": $dark-bg-subtle,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-bg-subtle-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-border-subtle-map
|
||||||
|
$theme-colors-border-subtle: (
|
||||||
|
"primary": $primary-border-subtle,
|
||||||
|
"secondary": $secondary-border-subtle,
|
||||||
|
"success": $success-border-subtle,
|
||||||
|
"info": $info-border-subtle,
|
||||||
|
"warning": $warning-border-subtle,
|
||||||
|
"danger": $danger-border-subtle,
|
||||||
|
"light": $light-border-subtle,
|
||||||
|
"dark": $dark-border-subtle,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-border-subtle-map
|
||||||
|
|
||||||
|
$theme-colors-text-dark: null !default;
|
||||||
|
$theme-colors-bg-subtle-dark: null !default;
|
||||||
|
$theme-colors-border-subtle-dark: null !default;
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
// scss-docs-start theme-text-dark-map
|
||||||
|
$theme-colors-text-dark: (
|
||||||
|
"primary": $primary-text-emphasis-dark,
|
||||||
|
"secondary": $secondary-text-emphasis-dark,
|
||||||
|
"success": $success-text-emphasis-dark,
|
||||||
|
"info": $info-text-emphasis-dark,
|
||||||
|
"warning": $warning-text-emphasis-dark,
|
||||||
|
"danger": $danger-text-emphasis-dark,
|
||||||
|
"light": $light-text-emphasis-dark,
|
||||||
|
"dark": $dark-text-emphasis-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-text-dark-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-bg-subtle-dark-map
|
||||||
|
$theme-colors-bg-subtle-dark: (
|
||||||
|
"primary": $primary-bg-subtle-dark,
|
||||||
|
"secondary": $secondary-bg-subtle-dark,
|
||||||
|
"success": $success-bg-subtle-dark,
|
||||||
|
"info": $info-bg-subtle-dark,
|
||||||
|
"warning": $warning-bg-subtle-dark,
|
||||||
|
"danger": $danger-bg-subtle-dark,
|
||||||
|
"light": $light-bg-subtle-dark,
|
||||||
|
"dark": $dark-bg-subtle-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-bg-subtle-dark-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-border-subtle-dark-map
|
||||||
|
$theme-colors-border-subtle-dark: (
|
||||||
|
"primary": $primary-border-subtle-dark,
|
||||||
|
"secondary": $secondary-border-subtle-dark,
|
||||||
|
"success": $success-border-subtle-dark,
|
||||||
|
"info": $info-border-subtle-dark,
|
||||||
|
"warning": $warning-border-subtle-dark,
|
||||||
|
"danger": $danger-border-subtle-dark,
|
||||||
|
"light": $light-border-subtle-dark,
|
||||||
|
"dark": $dark-border-subtle-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-border-subtle-dark-map
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilities maps
|
||||||
|
//
|
||||||
|
// Extends the default `$theme-colors` maps to help create our utilities.
|
||||||
|
|
||||||
|
// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign.
|
||||||
|
// scss-docs-start utilities-colors
|
||||||
|
$utilities-colors: $theme-colors-rgb !default;
|
||||||
|
// scss-docs-end utilities-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-text-colors
|
||||||
|
$utilities-text: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white),
|
||||||
|
"body": to-rgb($body-color)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default;
|
||||||
|
|
||||||
|
$utilities-text-emphasis-colors: (
|
||||||
|
"primary-emphasis": var(--#{$prefix}primary-text-emphasis),
|
||||||
|
"secondary-emphasis": var(--#{$prefix}secondary-text-emphasis),
|
||||||
|
"success-emphasis": var(--#{$prefix}success-text-emphasis),
|
||||||
|
"info-emphasis": var(--#{$prefix}info-text-emphasis),
|
||||||
|
"warning-emphasis": var(--#{$prefix}warning-text-emphasis),
|
||||||
|
"danger-emphasis": var(--#{$prefix}danger-text-emphasis),
|
||||||
|
"light-emphasis": var(--#{$prefix}light-text-emphasis),
|
||||||
|
"dark-emphasis": var(--#{$prefix}dark-text-emphasis)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-text-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-bg-colors
|
||||||
|
$utilities-bg: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white),
|
||||||
|
"body": to-rgb($body-bg)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default;
|
||||||
|
|
||||||
|
$utilities-bg-subtle: (
|
||||||
|
"primary-subtle": var(--#{$prefix}primary-bg-subtle),
|
||||||
|
"secondary-subtle": var(--#{$prefix}secondary-bg-subtle),
|
||||||
|
"success-subtle": var(--#{$prefix}success-bg-subtle),
|
||||||
|
"info-subtle": var(--#{$prefix}info-bg-subtle),
|
||||||
|
"warning-subtle": var(--#{$prefix}warning-bg-subtle),
|
||||||
|
"danger-subtle": var(--#{$prefix}danger-bg-subtle),
|
||||||
|
"light-subtle": var(--#{$prefix}light-bg-subtle),
|
||||||
|
"dark-subtle": var(--#{$prefix}dark-bg-subtle)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-bg-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-border-colors
|
||||||
|
$utilities-border: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-border-colors: map-loop($utilities-border, rgba-css-var, "$key", "border") !default;
|
||||||
|
|
||||||
|
$utilities-border-subtle: (
|
||||||
|
"primary-subtle": var(--#{$prefix}primary-border-subtle),
|
||||||
|
"secondary-subtle": var(--#{$prefix}secondary-border-subtle),
|
||||||
|
"success-subtle": var(--#{$prefix}success-border-subtle),
|
||||||
|
"info-subtle": var(--#{$prefix}info-border-subtle),
|
||||||
|
"warning-subtle": var(--#{$prefix}warning-border-subtle),
|
||||||
|
"danger-subtle": var(--#{$prefix}danger-border-subtle),
|
||||||
|
"light-subtle": var(--#{$prefix}light-border-subtle),
|
||||||
|
"dark-subtle": var(--#{$prefix}dark-border-subtle)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-border-colors
|
||||||
|
|
||||||
|
$utilities-links-underline: map-loop($utilities-colors, rgba-css-var, "$key", "link-underline") !default;
|
||||||
|
|
||||||
|
$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
|
||||||
|
|
||||||
|
$gutters: $spacers !default;
|
@ -6,10 +6,11 @@
|
|||||||
@import "vendor/rfs";
|
@import "vendor/rfs";
|
||||||
|
|
||||||
// Deprecate
|
// Deprecate
|
||||||
// @import "mixins/deprecate";
|
@import "mixins/deprecate";
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
@import "mixins/breakpoints";
|
@import "mixins/breakpoints";
|
||||||
|
@import "mixins/color-mode";
|
||||||
@import "mixins/color-scheme";
|
@import "mixins/color-scheme";
|
||||||
@import "mixins/image";
|
@import "mixins/image";
|
||||||
@import "mixins/resize";
|
@import "mixins/resize";
|
||||||
@ -21,12 +22,11 @@
|
|||||||
@import "mixins/utilities";
|
@import "mixins/utilities";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@import "mixins/alert";
|
@import "mixins/backdrop";
|
||||||
@import "mixins/buttons";
|
@import "mixins/buttons";
|
||||||
@import "mixins/caret";
|
@import "mixins/caret";
|
||||||
@import "mixins/pagination";
|
@import "mixins/pagination";
|
||||||
@import "mixins/lists";
|
@import "mixins/lists";
|
||||||
@import "mixins/list-group";
|
|
||||||
@import "mixins/forms";
|
@import "mixins/forms";
|
||||||
@import "mixins/table-variants";
|
@import "mixins/table-variants";
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// stylelint-disable function-disallowed-list
|
||||||
|
|
||||||
// .modal-open - body class for killing the scroll
|
// .modal-open - body class for killing the scroll
|
||||||
// .modal - container to scroll within
|
// .modal - container to scroll within
|
||||||
// .modal-dialog - positioning shell for the actual modal
|
// .modal-dialog - positioning shell for the actual modal
|
||||||
@ -6,10 +8,34 @@
|
|||||||
|
|
||||||
// Container that the modal scrolls within
|
// Container that the modal scrolls within
|
||||||
.modal {
|
.modal {
|
||||||
|
// scss-docs-start modal-css-vars
|
||||||
|
--#{$prefix}modal-zindex: #{$zindex-modal};
|
||||||
|
--#{$prefix}modal-width: #{$modal-md};
|
||||||
|
--#{$prefix}modal-padding: #{$modal-inner-padding};
|
||||||
|
--#{$prefix}modal-margin: #{$modal-dialog-margin};
|
||||||
|
--#{$prefix}modal-color: #{$modal-content-color};
|
||||||
|
--#{$prefix}modal-bg: #{$modal-content-bg};
|
||||||
|
--#{$prefix}modal-border-color: #{$modal-content-border-color};
|
||||||
|
--#{$prefix}modal-border-width: #{$modal-content-border-width};
|
||||||
|
--#{$prefix}modal-border-radius: #{$modal-content-border-radius};
|
||||||
|
--#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-xs};
|
||||||
|
--#{$prefix}modal-inner-border-radius: #{$modal-content-inner-border-radius};
|
||||||
|
--#{$prefix}modal-header-padding-x: #{$modal-header-padding-x};
|
||||||
|
--#{$prefix}modal-header-padding-y: #{$modal-header-padding-y};
|
||||||
|
--#{$prefix}modal-header-padding: #{$modal-header-padding}; // Todo in v6: Split this padding into x and y
|
||||||
|
--#{$prefix}modal-header-border-color: #{$modal-header-border-color};
|
||||||
|
--#{$prefix}modal-header-border-width: #{$modal-header-border-width};
|
||||||
|
--#{$prefix}modal-title-line-height: #{$modal-title-line-height};
|
||||||
|
--#{$prefix}modal-footer-gap: #{$modal-footer-margin-between};
|
||||||
|
--#{$prefix}modal-footer-bg: #{$modal-footer-bg};
|
||||||
|
--#{$prefix}modal-footer-border-color: #{$modal-footer-border-color};
|
||||||
|
--#{$prefix}modal-footer-border-width: #{$modal-footer-border-width};
|
||||||
|
// scss-docs-end modal-css-vars
|
||||||
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: $zindex-modal;
|
z-index: var(--#{$prefix}modal-zindex);
|
||||||
display: none;
|
display: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -27,7 +53,7 @@
|
|||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: $modal-dialog-margin;
|
margin: var(--#{$prefix}modal-margin);
|
||||||
// allow clicks to pass through for custom click handling to close modal
|
// allow clicks to pass through for custom click handling to close modal
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
@ -47,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-scrollable {
|
.modal-dialog-scrollable {
|
||||||
height: subtract(100%, $modal-dialog-margin * 2);
|
height: calc(100% - var(--#{$prefix}modal-margin) * 2);
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
@ -62,7 +88,7 @@
|
|||||||
.modal-dialog-centered {
|
.modal-dialog-centered {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: subtract(100%, $modal-dialog-margin * 2);
|
min-height: calc(100% - var(--#{$prefix}modal-margin) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual modal
|
// Actual modal
|
||||||
@ -72,30 +98,26 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
||||||
// counteract the pointer-events: none; in the .modal-dialog
|
// counteract the pointer-events: none; in the .modal-dialog
|
||||||
color: $modal-content-color;
|
color: var(--#{$prefix}modal-color);
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
background-color: $modal-content-bg;
|
background-color: var(--#{$prefix}modal-bg);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: $modal-content-border-width solid $modal-content-border-color;
|
border: var(--#{$prefix}modal-border-width) solid var(--#{$prefix}modal-border-color);
|
||||||
@include border-radius($modal-content-border-radius);
|
@include border-radius(var(--#{$prefix}modal-border-radius));
|
||||||
@include box-shadow($modal-content-box-shadow-xs);
|
@include box-shadow(var(--#{$prefix}modal-box-shadow));
|
||||||
// Remove focus outline from opened modal
|
// Remove focus outline from opened modal
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal background
|
// Modal background
|
||||||
.modal-backdrop {
|
.modal-backdrop {
|
||||||
position: fixed;
|
// scss-docs-start modal-backdrop-css-vars
|
||||||
top: 0;
|
--#{$prefix}backdrop-zindex: #{$zindex-modal-backdrop};
|
||||||
left: 0;
|
--#{$prefix}backdrop-bg: #{$modal-backdrop-bg};
|
||||||
z-index: $zindex-modal-backdrop;
|
--#{$prefix}backdrop-opacity: #{$modal-backdrop-opacity};
|
||||||
width: 100vw;
|
// scss-docs-end modal-backdrop-css-vars
|
||||||
height: 100vh;
|
|
||||||
background-color: $modal-backdrop-bg;
|
|
||||||
|
|
||||||
// Fade for backdrop
|
@include overlay-backdrop(var(--#{$prefix}backdrop-zindex), var(--#{$prefix}backdrop-bg), var(--#{$prefix}backdrop-opacity));
|
||||||
&.fade { opacity: 0; }
|
|
||||||
&.show { opacity: $modal-backdrop-opacity; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal header
|
// Modal header
|
||||||
@ -105,20 +127,20 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
|
justify-content: space-between; // Put modal header elements (title and dismiss) on opposite ends
|
||||||
padding: $modal-header-padding;
|
padding: var(--#{$prefix}modal-header-padding);
|
||||||
border-bottom: $modal-header-border-width solid $modal-header-border-color;
|
border-bottom: var(--#{$prefix}modal-header-border-width) solid var(--#{$prefix}modal-header-border-color);
|
||||||
@include border-top-radius($modal-content-inner-border-radius);
|
@include border-top-radius(var(--#{$prefix}modal-inner-border-radius));
|
||||||
|
|
||||||
.btn-close {
|
.btn-close {
|
||||||
padding: ($modal-header-padding-y * .5) ($modal-header-padding-x * .5);
|
padding: calc(var(--#{$prefix}modal-header-padding-y) * .5) calc(var(--#{$prefix}modal-header-padding-x) * .5);
|
||||||
margin: ($modal-header-padding-y * -.5) ($modal-header-padding-x * -.5) ($modal-header-padding-y * -.5) auto;
|
margin: calc(-.5 * var(--#{$prefix}modal-header-padding-y)) calc(-.5 * var(--#{$prefix}modal-header-padding-x)) calc(-.5 * var(--#{$prefix}modal-header-padding-y)) auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Title text within header
|
// Title text within header
|
||||||
.modal-title {
|
.modal-title {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
line-height: $modal-title-line-height;
|
line-height: var(--#{$prefix}modal-title-line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal body
|
// Modal body
|
||||||
@ -128,60 +150,59 @@
|
|||||||
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
||||||
// when there should be a fixed height on `.modal-dialog`.
|
// when there should be a fixed height on `.modal-dialog`.
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
padding: $modal-inner-padding;
|
padding: var(--#{$prefix}modal-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Footer (for actions)
|
// Footer (for actions)
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center; // vertically center
|
align-items: center; // vertically center
|
||||||
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
||||||
padding: $modal-inner-padding - $modal-footer-margin-between * .5;
|
padding: calc(var(--#{$prefix}modal-padding) - var(--#{$prefix}modal-footer-gap) * .5);
|
||||||
border-top: $modal-footer-border-width solid $modal-footer-border-color;
|
background-color: var(--#{$prefix}modal-footer-bg);
|
||||||
@include border-bottom-radius($modal-content-inner-border-radius);
|
border-top: var(--#{$prefix}modal-footer-border-width) solid var(--#{$prefix}modal-footer-border-color);
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}modal-inner-border-radius));
|
||||||
|
|
||||||
// Place margin between footer elements
|
// Place margin between footer elements
|
||||||
// This solution is far from ideal because of the universal selector usage,
|
// This solution is far from ideal because of the universal selector usage,
|
||||||
// but is needed to fix https://github.com/twbs/bootstrap/issues/24800
|
// but is needed to fix https://github.com/twbs/bootstrap/issues/24800
|
||||||
> * {
|
> * {
|
||||||
margin: $modal-footer-margin-between * .5;
|
margin: calc(var(--#{$prefix}modal-footer-gap) * .5); // Todo in v6: replace with gap on parent class
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale up the modal
|
// Scale up the modal
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
|
.modal {
|
||||||
|
--#{$prefix}modal-margin: #{$modal-dialog-margin-y-sm-up};
|
||||||
|
--#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-sm-up};
|
||||||
|
}
|
||||||
|
|
||||||
// Automatically set modal's width for larger viewports
|
// Automatically set modal's width for larger viewports
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
max-width: $modal-md;
|
max-width: var(--#{$prefix}modal-width);
|
||||||
margin: $modal-dialog-margin-y-sm-up auto;
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-scrollable {
|
.modal-sm {
|
||||||
height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
|
--#{$prefix}modal-width: #{$modal-sm};
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog-centered {
|
|
||||||
min-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
@include box-shadow($modal-content-box-shadow-sm-up);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-sm { max-width: $modal-sm; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(lg) {
|
@include media-breakpoint-up(lg) {
|
||||||
.modal-lg,
|
.modal-lg,
|
||||||
.modal-xl {
|
.modal-xl {
|
||||||
max-width: $modal-lg;
|
--#{$prefix}modal-width: #{$modal-lg};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-up(xl) {
|
@include media-breakpoint-up(xl) {
|
||||||
.modal-xl { max-width: $modal-xl; }
|
.modal-xl {
|
||||||
|
--#{$prefix}modal-width: #{$modal-xl};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// scss-docs-start modal-fullscreen-loop
|
// scss-docs-start modal-fullscreen-loop
|
||||||
@ -202,17 +223,14 @@
|
|||||||
@include border-radius(0);
|
@include border-radius(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header,
|
||||||
|
.modal-footer {
|
||||||
@include border-radius(0);
|
@include border-radius(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
@include border-radius(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,16 @@
|
|||||||
// `<nav>`s, `<ul>`s or `<ol>`s.
|
// `<nav>`s, `<ul>`s or `<ol>`s.
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
|
// scss-docs-start nav-css-vars
|
||||||
|
--#{$prefix}nav-link-padding-x: #{$nav-link-padding-x};
|
||||||
|
--#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
|
||||||
|
@include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
|
||||||
|
--#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
|
||||||
|
--#{$prefix}nav-link-color: #{$nav-link-color};
|
||||||
|
--#{$prefix}nav-link-hover-color: #{$nav-link-hover-color};
|
||||||
|
--#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color};
|
||||||
|
// scss-docs-end nav-css-vars
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@ -13,22 +23,30 @@
|
|||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: $nav-link-padding-y $nav-link-padding-x;
|
padding: var(--#{$prefix}nav-link-padding-y) var(--#{$prefix}nav-link-padding-x);
|
||||||
@include font-size($nav-link-font-size);
|
@include font-size(var(--#{$prefix}nav-link-font-size));
|
||||||
font-weight: $nav-link-font-weight;
|
font-weight: var(--#{$prefix}nav-link-font-weight);
|
||||||
color: $nav-link-color;
|
color: var(--#{$prefix}nav-link-color);
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
@include transition($nav-link-transition);
|
@include transition($nav-link-transition);
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
color: $nav-link-hover-color;
|
color: var(--#{$prefix}nav-link-hover-color);
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: $nav-link-focus-box-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
// Disabled state lightens text
|
// Disabled state lightens text
|
||||||
&.disabled {
|
&.disabled,
|
||||||
color: $nav-link-disabled-color;
|
&:disabled {
|
||||||
|
color: var(--#{$prefix}nav-link-disabled-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
@ -39,38 +57,41 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
|
// scss-docs-start nav-tabs-css-vars
|
||||||
|
--#{$prefix}nav-tabs-border-width: #{$nav-tabs-border-width};
|
||||||
|
--#{$prefix}nav-tabs-border-color: #{$nav-tabs-border-color};
|
||||||
|
--#{$prefix}nav-tabs-border-radius: #{$nav-tabs-border-radius};
|
||||||
|
--#{$prefix}nav-tabs-link-hover-border-color: #{$nav-tabs-link-hover-border-color};
|
||||||
|
--#{$prefix}nav-tabs-link-active-color: #{$nav-tabs-link-active-color};
|
||||||
|
--#{$prefix}nav-tabs-link-active-bg: #{$nav-tabs-link-active-bg};
|
||||||
|
--#{$prefix}nav-tabs-link-active-border-color: #{$nav-tabs-link-active-border-color};
|
||||||
|
// scss-docs-end nav-tabs-css-vars
|
||||||
|
|
||||||
|
border-bottom: var(--#{$prefix}nav-tabs-border-width) solid var(--#{$prefix}nav-tabs-border-color);
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
margin-bottom: -$nav-tabs-border-width;
|
margin-bottom: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
background: none;
|
border: var(--#{$prefix}nav-tabs-border-width) solid transparent;
|
||||||
border: $nav-tabs-border-width solid transparent;
|
@include border-top-radius(var(--#{$prefix}nav-tabs-border-radius));
|
||||||
@include border-top-radius($nav-tabs-border-radius);
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: $nav-tabs-link-hover-border-color;
|
|
||||||
// Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
|
// Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
border-color: var(--#{$prefix}nav-tabs-link-hover-border-color);
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: $nav-link-disabled-color;
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active,
|
.nav-link.active,
|
||||||
.nav-item.show .nav-link {
|
.nav-item.show .nav-link {
|
||||||
color: $nav-tabs-link-active-color;
|
color: var(--#{$prefix}nav-tabs-link-active-color);
|
||||||
background-color: $nav-tabs-link-active-bg;
|
background-color: var(--#{$prefix}nav-tabs-link-active-bg);
|
||||||
border-color: $nav-tabs-link-active-border-color;
|
border-color: var(--#{$prefix}nav-tabs-link-active-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
// Make dropdown border overlap tab border
|
// Make dropdown border overlap tab border
|
||||||
margin-top: -$nav-tabs-border-width;
|
margin-top: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
// Remove the top rounded corners here since there is a hard edge above the menu
|
// Remove the top rounded corners here since there is a hard edge above the menu
|
||||||
@include border-top-radius(0);
|
@include border-top-radius(0);
|
||||||
}
|
}
|
||||||
@ -82,16 +103,53 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.nav-pills {
|
.nav-pills {
|
||||||
|
// scss-docs-start nav-pills-css-vars
|
||||||
|
--#{$prefix}nav-pills-border-radius: #{$nav-pills-border-radius};
|
||||||
|
--#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color};
|
||||||
|
--#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg};
|
||||||
|
// scss-docs-end nav-pills-css-vars
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
background: none;
|
@include border-radius(var(--#{$prefix}nav-pills-border-radius));
|
||||||
border: 0;
|
|
||||||
@include border-radius($nav-pills-border-radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active,
|
.nav-link.active,
|
||||||
.show > .nav-link {
|
.show > .nav-link {
|
||||||
color: $nav-pills-link-active-color;
|
color: var(--#{$prefix}nav-pills-link-active-color);
|
||||||
@include gradient-bg($nav-pills-link-active-bg);
|
@include gradient-bg(var(--#{$prefix}nav-pills-link-active-bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Underline
|
||||||
|
//
|
||||||
|
|
||||||
|
.nav-underline {
|
||||||
|
// scss-docs-start nav-underline-css-vars
|
||||||
|
--#{$prefix}nav-underline-gap: #{$nav-underline-gap};
|
||||||
|
--#{$prefix}nav-underline-border-width: #{$nav-underline-border-width};
|
||||||
|
--#{$prefix}nav-underline-link-active-color: #{$nav-underline-link-active-color};
|
||||||
|
// scss-docs-end nav-underline-css-vars
|
||||||
|
|
||||||
|
gap: var(--#{$prefix}nav-underline-gap);
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
border-bottom: var(--#{$prefix}nav-underline-border-width) solid transparent;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
border-bottom-color: currentcolor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active,
|
||||||
|
.show > .nav-link {
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
color: var(--#{$prefix}nav-underline-link-active-color);
|
||||||
|
border-bottom-color: currentcolor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,29 +1,38 @@
|
|||||||
// Contents
|
|
||||||
//
|
|
||||||
// Navbar
|
|
||||||
// Navbar brand
|
|
||||||
// Navbar nav
|
|
||||||
// Navbar text
|
|
||||||
// Responsive navbar
|
|
||||||
// Navbar position
|
|
||||||
// Navbar themes
|
|
||||||
|
|
||||||
|
|
||||||
// Navbar
|
// Navbar
|
||||||
//
|
//
|
||||||
// Provide a static navbar from which we expand to create full-width, fixed, and
|
// Provide a static navbar from which we expand to create full-width, fixed, and
|
||||||
// other navbar variations.
|
// other navbar variations.
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
|
// scss-docs-start navbar-css-vars
|
||||||
|
--#{$prefix}navbar-padding-x: #{if($navbar-padding-x == null, 0, $navbar-padding-x)};
|
||||||
|
--#{$prefix}navbar-padding-y: #{$navbar-padding-y};
|
||||||
|
--#{$prefix}navbar-color: #{$navbar-light-color};
|
||||||
|
--#{$prefix}navbar-hover-color: #{$navbar-light-hover-color};
|
||||||
|
--#{$prefix}navbar-disabled-color: #{$navbar-light-disabled-color};
|
||||||
|
--#{$prefix}navbar-active-color: #{$navbar-light-active-color};
|
||||||
|
--#{$prefix}navbar-brand-padding-y: #{$navbar-brand-padding-y};
|
||||||
|
--#{$prefix}navbar-brand-margin-end: #{$navbar-brand-margin-end};
|
||||||
|
--#{$prefix}navbar-brand-font-size: #{$navbar-brand-font-size};
|
||||||
|
--#{$prefix}navbar-brand-color: #{$navbar-light-brand-color};
|
||||||
|
--#{$prefix}navbar-brand-hover-color: #{$navbar-light-brand-hover-color};
|
||||||
|
--#{$prefix}navbar-nav-link-padding-x: #{$navbar-nav-link-padding-x};
|
||||||
|
--#{$prefix}navbar-toggler-padding-y: #{$navbar-toggler-padding-y};
|
||||||
|
--#{$prefix}navbar-toggler-padding-x: #{$navbar-toggler-padding-x};
|
||||||
|
--#{$prefix}navbar-toggler-font-size: #{$navbar-toggler-font-size};
|
||||||
|
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-light-toggler-icon-bg)};
|
||||||
|
--#{$prefix}navbar-toggler-border-color: #{$navbar-light-toggler-border-color};
|
||||||
|
--#{$prefix}navbar-toggler-border-radius: #{$navbar-toggler-border-radius};
|
||||||
|
--#{$prefix}navbar-toggler-focus-width: #{$navbar-toggler-focus-width};
|
||||||
|
--#{$prefix}navbar-toggler-transition: #{$navbar-toggler-transition};
|
||||||
|
// scss-docs-end navbar-css-vars
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap; // allow us to do the line break for collapsing content
|
flex-wrap: wrap; // allow us to do the line break for collapsing content
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between; // space out brand from logo
|
justify-content: space-between; // space out brand from logo
|
||||||
padding-top: $navbar-padding-y;
|
padding: var(--#{$prefix}navbar-padding-y) var(--#{$prefix}navbar-padding-x);
|
||||||
padding-right: $navbar-padding-x; // default: null
|
|
||||||
padding-bottom: $navbar-padding-y;
|
|
||||||
padding-left: $navbar-padding-x; // default: null
|
|
||||||
@include gradient-bg();
|
@include gradient-bg();
|
||||||
|
|
||||||
// Because flex properties aren't inherited, we need to redeclare these first
|
// Because flex properties aren't inherited, we need to redeclare these first
|
||||||
@ -54,15 +63,17 @@
|
|||||||
// Used for brand, project, or site names.
|
// Used for brand, project, or site names.
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
padding-top: $navbar-brand-padding-y;
|
padding-top: var(--#{$prefix}navbar-brand-padding-y);
|
||||||
padding-bottom: $navbar-brand-padding-y;
|
padding-bottom: var(--#{$prefix}navbar-brand-padding-y);
|
||||||
margin-right: $navbar-brand-margin-end;
|
margin-right: var(--#{$prefix}navbar-brand-margin-end);
|
||||||
@include font-size($navbar-brand-font-size);
|
@include font-size(var(--#{$prefix}navbar-brand-font-size));
|
||||||
|
color: var(--#{$prefix}navbar-brand-color);
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
|
color: var(--#{$prefix}navbar-brand-hover-color);
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +84,16 @@
|
|||||||
// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
|
// Custom navbar navigation (doesn't require `.nav`, but does make use of `.nav-link`).
|
||||||
|
|
||||||
.navbar-nav {
|
.navbar-nav {
|
||||||
|
// scss-docs-start navbar-nav-css-vars
|
||||||
|
--#{$prefix}nav-link-padding-x: 0;
|
||||||
|
--#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
|
||||||
|
@include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
|
||||||
|
--#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
|
||||||
|
--#{$prefix}nav-link-color: var(--#{$prefix}navbar-color);
|
||||||
|
--#{$prefix}nav-link-hover-color: var(--#{$prefix}navbar-hover-color);
|
||||||
|
--#{$prefix}nav-link-disabled-color: var(--#{$prefix}navbar-disabled-color);
|
||||||
|
// scss-docs-end navbar-nav-css-vars
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
|
flex-direction: column; // cannot use `inherit` to get the `.navbar`s value
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@ -80,8 +101,10 @@
|
|||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
padding-right: 0;
|
&.active,
|
||||||
padding-left: 0;
|
&.show {
|
||||||
|
color: var(--#{$prefix}navbar-active-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
@ -97,6 +120,13 @@
|
|||||||
.navbar-text {
|
.navbar-text {
|
||||||
padding-top: $nav-link-padding-y;
|
padding-top: $nav-link-padding-y;
|
||||||
padding-bottom: $nav-link-padding-y;
|
padding-bottom: $nav-link-padding-y;
|
||||||
|
color: var(--#{$prefix}navbar-color);
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:hover,
|
||||||
|
a:focus {
|
||||||
|
color: var(--#{$prefix}navbar-active-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -118,13 +148,14 @@
|
|||||||
|
|
||||||
// Button for toggling the navbar when in its collapsed state
|
// Button for toggling the navbar when in its collapsed state
|
||||||
.navbar-toggler {
|
.navbar-toggler {
|
||||||
padding: $navbar-toggler-padding-y $navbar-toggler-padding-x;
|
padding: var(--#{$prefix}navbar-toggler-padding-y) var(--#{$prefix}navbar-toggler-padding-x);
|
||||||
@include font-size($navbar-toggler-font-size);
|
@include font-size(var(--#{$prefix}navbar-toggler-font-size));
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
color: var(--#{$prefix}navbar-color);
|
||||||
background-color: transparent; // remove default button style
|
background-color: transparent; // remove default button style
|
||||||
border: $border-width solid transparent; // remove default button style
|
border: var(--#{$prefix}border-width) solid var(--#{$prefix}navbar-toggler-border-color); // remove default button style
|
||||||
@include border-radius($navbar-toggler-border-radius);
|
@include border-radius(var(--#{$prefix}navbar-toggler-border-radius));
|
||||||
@include transition($navbar-toggler-transition);
|
@include transition(var(--#{$prefix}navbar-toggler-transition));
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -133,7 +164,7 @@
|
|||||||
&:focus {
|
&:focus {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: 0 0 0 $navbar-toggler-focus-width;
|
box-shadow: 0 0 0 var(--#{$prefix}navbar-toggler-focus-width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,13 +175,14 @@
|
|||||||
width: 1.5em;
|
width: 1.5em;
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
background-image: var(--#{$prefix}navbar-toggler-icon-bg);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-nav-scroll {
|
.navbar-nav-scroll {
|
||||||
max-height: var(--#{$variable-prefix}scroll-height, 75vh);
|
max-height: var(--#{$prefix}scroll-height, 75vh);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +208,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
padding-right: $navbar-nav-link-padding-x;
|
padding-right: var(--#{$prefix}navbar-nav-link-padding-x);
|
||||||
padding-left: $navbar-nav-link-padding-x;
|
padding-left: var(--#{$prefix}navbar-nav-link-padding-x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,114 +225,65 @@
|
|||||||
.navbar-toggler {
|
.navbar-toggler {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.offcanvas {
|
||||||
|
// stylelint-disable declaration-no-important
|
||||||
|
position: static;
|
||||||
|
z-index: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
@include box-shadow(none);
|
||||||
|
@include transition(none);
|
||||||
|
// stylelint-enable declaration-no-important
|
||||||
|
|
||||||
|
.offcanvas-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offcanvas-body {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// scss-docs-end navbar-expand-loop
|
// scss-docs-end navbar-expand-loop
|
||||||
|
|
||||||
|
|
||||||
// Navbar themes
|
// Navbar themes
|
||||||
//
|
//
|
||||||
// Styles for switching between navbars with light or dark background.
|
// Styles for switching between navbars with light or dark background.
|
||||||
|
|
||||||
// Dark links against a light background
|
|
||||||
.navbar-light {
|
.navbar-light {
|
||||||
.navbar-brand {
|
@include deprecate("`.navbar-light`", "v5.2.0", "v6.0.0", true);
|
||||||
color: $navbar-light-brand-color;
|
}
|
||||||
|
|
||||||
&:hover,
|
.navbar-dark,
|
||||||
&:focus {
|
.navbar[data-bs-theme="dark"] {
|
||||||
color: $navbar-light-brand-hover-color;
|
// scss-docs-start navbar-dark-css-vars
|
||||||
}
|
--#{$prefix}navbar-color: #{$navbar-dark-color};
|
||||||
}
|
--#{$prefix}navbar-hover-color: #{$navbar-dark-hover-color};
|
||||||
|
--#{$prefix}navbar-disabled-color: #{$navbar-dark-disabled-color};
|
||||||
|
--#{$prefix}navbar-active-color: #{$navbar-dark-active-color};
|
||||||
|
--#{$prefix}navbar-brand-color: #{$navbar-dark-brand-color};
|
||||||
|
--#{$prefix}navbar-brand-hover-color: #{$navbar-dark-brand-hover-color};
|
||||||
|
--#{$prefix}navbar-toggler-border-color: #{$navbar-dark-toggler-border-color};
|
||||||
|
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
|
||||||
|
// scss-docs-end navbar-dark-css-vars
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-nav {
|
@if $enable-dark-mode {
|
||||||
.nav-link {
|
@include color-mode(dark) {
|
||||||
color: $navbar-light-color;
|
.navbar-toggler-icon {
|
||||||
|
--#{$prefix}navbar-toggler-icon-bg: #{escape-svg($navbar-dark-toggler-icon-bg)};
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $navbar-light-hover-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: $navbar-light-disabled-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show > .nav-link,
|
|
||||||
.nav-link.active {
|
|
||||||
color: $navbar-light-active-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler {
|
|
||||||
color: $navbar-light-color;
|
|
||||||
border-color: $navbar-light-toggler-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler-icon {
|
|
||||||
background-image: escape-svg($navbar-light-toggler-icon-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-text {
|
|
||||||
color: $navbar-light-color;
|
|
||||||
|
|
||||||
a,
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: $navbar-light-active-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// White links against a dark background
|
|
||||||
.navbar-dark {
|
|
||||||
.navbar-brand {
|
|
||||||
color: $navbar-dark-brand-color;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $navbar-dark-brand-hover-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-nav {
|
|
||||||
.nav-link {
|
|
||||||
color: $navbar-dark-color;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: $navbar-dark-hover-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: $navbar-dark-disabled-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.show > .nav-link,
|
|
||||||
.nav-link.active {
|
|
||||||
color: $navbar-dark-active-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler {
|
|
||||||
color: $navbar-dark-color;
|
|
||||||
border-color: $navbar-dark-toggler-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggler-icon {
|
|
||||||
background-image: escape-svg($navbar-dark-toggler-icon-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-text {
|
|
||||||
color: $navbar-dark-color;
|
|
||||||
a,
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: $navbar-dark-active-color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,27 @@
|
|||||||
.pagination {
|
.pagination {
|
||||||
|
// scss-docs-start pagination-css-vars
|
||||||
|
--#{$prefix}pagination-padding-x: #{$pagination-padding-x};
|
||||||
|
--#{$prefix}pagination-padding-y: #{$pagination-padding-y};
|
||||||
|
@include rfs($pagination-font-size, --#{$prefix}pagination-font-size);
|
||||||
|
--#{$prefix}pagination-color: #{$pagination-color};
|
||||||
|
--#{$prefix}pagination-bg: #{$pagination-bg};
|
||||||
|
--#{$prefix}pagination-border-width: #{$pagination-border-width};
|
||||||
|
--#{$prefix}pagination-border-color: #{$pagination-border-color};
|
||||||
|
--#{$prefix}pagination-border-radius: #{$pagination-border-radius};
|
||||||
|
--#{$prefix}pagination-hover-color: #{$pagination-hover-color};
|
||||||
|
--#{$prefix}pagination-hover-bg: #{$pagination-hover-bg};
|
||||||
|
--#{$prefix}pagination-hover-border-color: #{$pagination-hover-border-color};
|
||||||
|
--#{$prefix}pagination-focus-color: #{$pagination-focus-color};
|
||||||
|
--#{$prefix}pagination-focus-bg: #{$pagination-focus-bg};
|
||||||
|
--#{$prefix}pagination-focus-box-shadow: #{$pagination-focus-box-shadow};
|
||||||
|
--#{$prefix}pagination-active-color: #{$pagination-active-color};
|
||||||
|
--#{$prefix}pagination-active-bg: #{$pagination-active-bg};
|
||||||
|
--#{$prefix}pagination-active-border-color: #{$pagination-active-border-color};
|
||||||
|
--#{$prefix}pagination-disabled-color: #{$pagination-disabled-color};
|
||||||
|
--#{$prefix}pagination-disabled-bg: #{$pagination-disabled-bg};
|
||||||
|
--#{$prefix}pagination-disabled-border-color: #{$pagination-disabled-border-color};
|
||||||
|
// scss-docs-end pagination-css-vars
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
@include list-unstyled();
|
@include list-unstyled();
|
||||||
}
|
}
|
||||||
@ -6,26 +29,44 @@
|
|||||||
.page-link {
|
.page-link {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
color: $pagination-color;
|
padding: var(--#{$prefix}pagination-padding-y) var(--#{$prefix}pagination-padding-x);
|
||||||
|
@include font-size(var(--#{$prefix}pagination-font-size));
|
||||||
|
color: var(--#{$prefix}pagination-color);
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
background-color: $pagination-bg;
|
background-color: var(--#{$prefix}pagination-bg);
|
||||||
border: $pagination-border-width solid $pagination-border-color;
|
border: var(--#{$prefix}pagination-border-width) solid var(--#{$prefix}pagination-border-color);
|
||||||
@include transition($pagination-transition);
|
@include transition($pagination-transition);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
color: $pagination-hover-color;
|
color: var(--#{$prefix}pagination-hover-color);
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
background-color: $pagination-hover-bg;
|
background-color: var(--#{$prefix}pagination-hover-bg);
|
||||||
border-color: $pagination-hover-border-color;
|
border-color: var(--#{$prefix}pagination-hover-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
color: $pagination-focus-color;
|
color: var(--#{$prefix}pagination-focus-color);
|
||||||
background-color: $pagination-focus-bg;
|
background-color: var(--#{$prefix}pagination-focus-bg);
|
||||||
outline: $pagination-focus-outline;
|
outline: $pagination-focus-outline;
|
||||||
box-shadow: $pagination-focus-box-shadow;
|
box-shadow: var(--#{$prefix}pagination-focus-box-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
.active > & {
|
||||||
|
z-index: 3;
|
||||||
|
color: var(--#{$prefix}pagination-active-color);
|
||||||
|
@include gradient-bg(var(--#{$prefix}pagination-active-bg));
|
||||||
|
border-color: var(--#{$prefix}pagination-active-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled,
|
||||||
|
.disabled > & {
|
||||||
|
color: var(--#{$prefix}pagination-disabled-color);
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--#{$prefix}pagination-disabled-bg);
|
||||||
|
border-color: var(--#{$prefix}pagination-disabled-border-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,18 +75,23 @@
|
|||||||
margin-left: $pagination-margin-start;
|
margin-left: $pagination-margin-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active .page-link {
|
@if $pagination-margin-start == calc(#{$pagination-border-width} * -1) {
|
||||||
z-index: 3;
|
&:first-child {
|
||||||
color: $pagination-active-color;
|
.page-link {
|
||||||
@include gradient-bg($pagination-active-bg);
|
@include border-start-radius(var(--#{$prefix}pagination-border-radius));
|
||||||
border-color: $pagination-active-border-color;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled .page-link {
|
&:last-child {
|
||||||
color: $pagination-disabled-color;
|
.page-link {
|
||||||
pointer-events: none;
|
@include border-end-radius(var(--#{$prefix}pagination-border-radius));
|
||||||
background-color: $pagination-disabled-bg;
|
}
|
||||||
border-color: $pagination-disabled-border-color;
|
}
|
||||||
|
} @else {
|
||||||
|
// Add border-radius to all pageLinks in case they have left margin
|
||||||
|
.page-link {
|
||||||
|
@include border-radius(var(--#{$prefix}pagination-border-radius));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +99,6 @@
|
|||||||
//
|
//
|
||||||
// Sizing
|
// Sizing
|
||||||
//
|
//
|
||||||
@include pagination-size($pagination-padding-y, $pagination-padding-x, null, $pagination-border-radius);
|
|
||||||
|
|
||||||
.pagination-lg {
|
.pagination-lg {
|
||||||
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $pagination-border-radius-lg);
|
@include pagination-size($pagination-padding-y-lg, $pagination-padding-x-lg, $font-size-lg, $pagination-border-radius-lg);
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
// stylelint-disable declaration-no-important, selector-no-qualifying-type
|
|
||||||
|
|
||||||
// Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css
|
|
||||||
|
|
||||||
// ==========================================================================
|
|
||||||
// Print styles.
|
|
||||||
// Inlined to avoid the additional HTTP request:
|
|
||||||
// https://www.phpied.com/delay-loading-your-print-css/
|
|
||||||
// ==========================================================================
|
|
||||||
|
|
||||||
@if $enable-print-styles {
|
|
||||||
@media print {
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
// Bootstrap specific; comment out `color` and `background`
|
|
||||||
//color: $black !important; // Black prints faster
|
|
||||||
text-shadow: none !important;
|
|
||||||
//background: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
&:not(.btn) {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap specific; comment the following selector out
|
|
||||||
//a[href]::after {
|
|
||||||
// content: " (" attr(href) ")";
|
|
||||||
//}
|
|
||||||
|
|
||||||
abbr[title]::after {
|
|
||||||
content: " (" attr(title) ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap specific; comment the following selector out
|
|
||||||
//
|
|
||||||
// Don't show links that are fragment identifiers,
|
|
||||||
// or use the `javascript:` pseudo protocol
|
|
||||||
//
|
|
||||||
|
|
||||||
//a[href^="#"]::after,
|
|
||||||
//a[href^="javascript:"]::after {
|
|
||||||
// content: "";
|
|
||||||
//}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap !important;
|
|
||||||
}
|
|
||||||
pre,
|
|
||||||
blockquote {
|
|
||||||
border: $border-width solid $gray-500; // Bootstrap custom code; using `$border-width` instead of 1px
|
|
||||||
page-break-inside: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Printing Tables:
|
|
||||||
// http://css-discuss.incutio.com/wiki/Printing_Tables
|
|
||||||
//
|
|
||||||
|
|
||||||
thead {
|
|
||||||
display: table-header-group;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr,
|
|
||||||
img {
|
|
||||||
page-break-inside: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
orphans: 3;
|
|
||||||
widows: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2,
|
|
||||||
h3 {
|
|
||||||
page-break-after: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap specific changes start
|
|
||||||
|
|
||||||
// Specify a size and min-width to make printing closer across browsers.
|
|
||||||
// We don't set margin here because it breaks `size` in Chrome. We also
|
|
||||||
// don't use `!important` on `size` as it breaks in Chrome.
|
|
||||||
@page {
|
|
||||||
size: $print-page-size;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
min-width: $print-body-min-width !important;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
min-width: $print-body-min-width !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap components
|
|
||||||
.navbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.badge {
|
|
||||||
border: $border-width solid $black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
border-collapse: collapse !important;
|
|
||||||
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
background-color: $white !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-bordered {
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px solid $gray-300 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-dark {
|
|
||||||
color: inherit;
|
|
||||||
|
|
||||||
th,
|
|
||||||
td,
|
|
||||||
thead th,
|
|
||||||
tbody + tbody {
|
|
||||||
border-color: $table-border-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table .thead-dark th {
|
|
||||||
color: inherit;
|
|
||||||
border-color: $table-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bootstrap specific changes end
|
|
||||||
}
|
|
||||||
}
|
|
@ -26,7 +26,9 @@
|
|||||||
// null by default, thus nothing is generated.
|
// null by default, thus nothing is generated.
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-size: $font-size-root;
|
@if $font-size-root != null {
|
||||||
|
@include font-size(var(--#{$prefix}root-font-size));
|
||||||
|
}
|
||||||
|
|
||||||
@if $enable-smooth-scroll {
|
@if $enable-smooth-scroll {
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
@ -43,37 +45,34 @@
|
|||||||
// 3. Prevent adjustments of font size after orientation changes in iOS.
|
// 3. Prevent adjustments of font size after orientation changes in iOS.
|
||||||
// 4. Change the default tap highlight to be completely transparent in iOS.
|
// 4. Change the default tap highlight to be completely transparent in iOS.
|
||||||
|
|
||||||
|
// scss-docs-start reboot-body-rules
|
||||||
body {
|
body {
|
||||||
margin: 0; // 1
|
margin: 0; // 1
|
||||||
font-family: $font-family-base;
|
font-family: var(--#{$prefix}body-font-family);
|
||||||
@include font-size($font-size-base);
|
@include font-size(var(--#{$prefix}body-font-size));
|
||||||
font-weight: $font-weight-base;
|
font-weight: var(--#{$prefix}body-font-weight);
|
||||||
line-height: $line-height-base;
|
line-height: var(--#{$prefix}body-line-height);
|
||||||
color: $body-color;
|
color: var(--#{$prefix}body-color);
|
||||||
text-align: $body-text-align;
|
text-align: var(--#{$prefix}body-text-align);
|
||||||
background-color: $body-bg; // 2
|
background-color: var(--#{$prefix}body-bg); // 2
|
||||||
-webkit-text-size-adjust: 100%; // 3
|
-webkit-text-size-adjust: 100%; // 3
|
||||||
-webkit-tap-highlight-color: rgba($black, 0); // 4
|
-webkit-tap-highlight-color: rgba($black, 0); // 4
|
||||||
}
|
}
|
||||||
|
// scss-docs-end reboot-body-rules
|
||||||
|
|
||||||
|
|
||||||
// Content grouping
|
// Content grouping
|
||||||
//
|
//
|
||||||
// 1. Reset Firefox's gray color
|
// 1. Reset Firefox's gray color
|
||||||
// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: $hr-margin-y 0;
|
margin: $hr-margin-y 0;
|
||||||
color: $hr-color; // 1
|
color: $hr-color; // 1
|
||||||
background-color: currentColor;
|
|
||||||
border: 0;
|
border: 0;
|
||||||
|
border-top: $hr-border-width solid $hr-border-color;
|
||||||
opacity: $hr-opacity;
|
opacity: $hr-opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr:not([size]) {
|
|
||||||
height: $hr-height; // 2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Typography
|
// Typography
|
||||||
//
|
//
|
||||||
@ -88,7 +87,7 @@ hr:not([size]) {
|
|||||||
font-style: $headings-font-style;
|
font-style: $headings-font-style;
|
||||||
font-weight: $headings-font-weight;
|
font-weight: $headings-font-weight;
|
||||||
line-height: $headings-line-height;
|
line-height: $headings-line-height;
|
||||||
color: $headings-color;
|
color: var(--#{$prefix}heading-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@ -135,16 +134,14 @@ p {
|
|||||||
|
|
||||||
// Abbreviations
|
// Abbreviations
|
||||||
//
|
//
|
||||||
// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin
|
// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
|
||||||
// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
|
// 2. Add explicit cursor to indicate changed behavior.
|
||||||
// 3. Add explicit cursor to indicate changed behavior.
|
// 3. Prevent the text-decoration to be skipped.
|
||||||
// 4. Prevent the text-decoration to be skipped.
|
|
||||||
|
|
||||||
abbr[title],
|
abbr[title] {
|
||||||
abbr[data-bs-original-title] { // 1
|
text-decoration: underline dotted; // 1
|
||||||
text-decoration: underline dotted; // 2
|
cursor: help; // 2
|
||||||
cursor: help; // 3
|
text-decoration-skip-ink: none; // 3
|
||||||
text-decoration-skip-ink: none; // 4
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -220,7 +217,8 @@ small {
|
|||||||
|
|
||||||
mark {
|
mark {
|
||||||
padding: $mark-padding;
|
padding: $mark-padding;
|
||||||
background-color: $mark-bg;
|
color: var(--#{$prefix}highlight-color);
|
||||||
|
background-color: var(--#{$prefix}highlight-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -244,11 +242,11 @@ sup { top: -.5em; }
|
|||||||
// Links
|
// Links
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $link-color;
|
color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1));
|
||||||
text-decoration: $link-decoration;
|
text-decoration: $link-decoration;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $link-hover-color;
|
--#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb);
|
||||||
text-decoration: $link-hover-decoration;
|
text-decoration: $link-hover-decoration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,8 +273,6 @@ kbd,
|
|||||||
samp {
|
samp {
|
||||||
font-family: $font-family-code;
|
font-family: $font-family-code;
|
||||||
@include font-size(1em); // Correct the odd `em` font sizing in all browsers.
|
@include font-size(1em); // Correct the odd `em` font sizing in all browsers.
|
||||||
direction: ltr #{"/* rtl:ignore */"};
|
|
||||||
unicode-bidi: bidi-override;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Remove browser default top margin
|
// 1. Remove browser default top margin
|
||||||
@ -301,7 +297,7 @@ pre {
|
|||||||
|
|
||||||
code {
|
code {
|
||||||
@include font-size($code-font-size);
|
@include font-size($code-font-size);
|
||||||
color: $code-color;
|
color: var(--#{$prefix}code-color);
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
// Streamline the style when inside anchors to avoid broken underline and more
|
// Streamline the style when inside anchors to avoid broken underline and more
|
||||||
@ -441,11 +437,11 @@ select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the dropdown arrow in Chrome from inputs built with datalists.
|
// Remove the dropdown arrow only from text type inputs built with datalists in Chrome.
|
||||||
// See https://stackoverflow.com/a/54997118
|
// See https://stackoverflow.com/a/54997118
|
||||||
|
|
||||||
[list]::-webkit-calendar-picker-indicator {
|
[list]:not([type="date"]):not([type="datetime-local"]):not([type="month"]):not([type="week"]):not([type="time"])::-webkit-calendar-picker-indicator {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
@ -529,15 +525,15 @@ legend {
|
|||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Correct the outline style in Safari.
|
// 1. This overrides the extra rounded corners on search inputs in iOS so that our
|
||||||
// 2. This overrides the extra rounded corners on search inputs in iOS so that our
|
|
||||||
// `.form-control` class can properly style them. Note that this cannot simply
|
// `.form-control` class can properly style them. Note that this cannot simply
|
||||||
// be added to `.form-control` as it's not specific enough. For details, see
|
// be added to `.form-control` as it's not specific enough. For details, see
|
||||||
// https://github.com/twbs/bootstrap/issues/11586.
|
// https://github.com/twbs/bootstrap/issues/11586.
|
||||||
|
// 2. Correct the outline style in Safari.
|
||||||
|
|
||||||
[type="search"] {
|
[type="search"] {
|
||||||
outline-offset: -2px; // 1
|
-webkit-appearance: textfield; // 1
|
||||||
-webkit-appearance: textfield; // 2
|
outline-offset: -2px; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. A few input types should stay LTR
|
// 1. A few input types should stay LTR
|
||||||
@ -567,16 +563,10 @@ legend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Inherit font family and line height for file input buttons
|
// 1. Inherit font family and line height for file input buttons
|
||||||
|
|
||||||
::file-selector-button {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Change font properties to `inherit`
|
|
||||||
// 2. Correct the inability to style clickable types in iOS and Safari.
|
// 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
|
||||||
::-webkit-file-upload-button {
|
::file-selector-button {
|
||||||
font: inherit; // 1
|
font: inherit; // 1
|
||||||
-webkit-appearance: button; // 2
|
-webkit-appearance: button; // 2
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,187 @@
|
|||||||
:root {
|
:root,
|
||||||
// Custom variable values only support SassScript inside `#{}`.
|
[data-bs-theme="light"] {
|
||||||
|
// Note: Custom variable values only support SassScript inside `#{}`.
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
//
|
||||||
|
// Generate palettes for full colors, grays, and theme colors.
|
||||||
|
|
||||||
@each $color, $value in $colors {
|
@each $color, $value in $colors {
|
||||||
--#{$variable-prefix}#{$color}: #{$value};
|
--#{$prefix}#{$color}: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $grays {
|
||||||
|
--#{$prefix}gray-#{$color}: #{$value};
|
||||||
}
|
}
|
||||||
|
|
||||||
@each $color, $value in $theme-colors {
|
@each $color, $value in $theme-colors {
|
||||||
--#{$variable-prefix}#{$color}: #{$value};
|
--#{$prefix}#{$color}: #{$value};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use `inspect` for lists so that quoted items keep the quotes.
|
@each $color, $value in $theme-colors-rgb {
|
||||||
|
--#{$prefix}#{$color}-rgb: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-text {
|
||||||
|
--#{$prefix}#{$color}-text-emphasis: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-bg-subtle {
|
||||||
|
--#{$prefix}#{$color}-bg-subtle: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-border-subtle {
|
||||||
|
--#{$prefix}#{$color}-border-subtle: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
--#{$prefix}white-rgb: #{to-rgb($white)};
|
||||||
|
--#{$prefix}black-rgb: #{to-rgb($black)};
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
|
||||||
|
// Note: Use `inspect` for lists so that quoted items keep the quotes.
|
||||||
// See https://github.com/sass/sass/issues/2383#issuecomment-336349172
|
// See https://github.com/sass/sass/issues/2383#issuecomment-336349172
|
||||||
--#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};
|
--#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)};
|
||||||
--#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};
|
--#{$prefix}font-monospace: #{inspect($font-family-monospace)};
|
||||||
--#{$variable-prefix}gradient: #{$gradient};
|
--#{$prefix}gradient: #{$gradient};
|
||||||
|
|
||||||
|
// Root and body
|
||||||
|
// scss-docs-start root-body-variables
|
||||||
|
@if $font-size-root != null {
|
||||||
|
--#{$prefix}root-font-size: #{$font-size-root};
|
||||||
|
}
|
||||||
|
--#{$prefix}body-font-family: #{inspect($font-family-base)};
|
||||||
|
@include rfs($font-size-base, --#{$prefix}body-font-size);
|
||||||
|
--#{$prefix}body-font-weight: #{$font-weight-base};
|
||||||
|
--#{$prefix}body-line-height: #{$line-height-base};
|
||||||
|
@if $body-text-align != null {
|
||||||
|
--#{$prefix}body-text-align: #{$body-text-align};
|
||||||
|
}
|
||||||
|
|
||||||
|
--#{$prefix}body-color: #{$body-color};
|
||||||
|
--#{$prefix}body-color-rgb: #{to-rgb($body-color)};
|
||||||
|
--#{$prefix}body-bg: #{$body-bg};
|
||||||
|
--#{$prefix}body-bg-rgb: #{to-rgb($body-bg)};
|
||||||
|
|
||||||
|
--#{$prefix}emphasis-color: #{$body-emphasis-color};
|
||||||
|
--#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color)};
|
||||||
|
|
||||||
|
--#{$prefix}secondary-color: #{$body-secondary-color};
|
||||||
|
--#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color)};
|
||||||
|
--#{$prefix}secondary-bg: #{$body-secondary-bg};
|
||||||
|
--#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg)};
|
||||||
|
|
||||||
|
--#{$prefix}tertiary-color: #{$body-tertiary-color};
|
||||||
|
--#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color)};
|
||||||
|
--#{$prefix}tertiary-bg: #{$body-tertiary-bg};
|
||||||
|
--#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg)};
|
||||||
|
// scss-docs-end root-body-variables
|
||||||
|
|
||||||
|
--#{$prefix}heading-color: #{$headings-color};
|
||||||
|
|
||||||
|
--#{$prefix}link-color: #{$link-color};
|
||||||
|
--#{$prefix}link-color-rgb: #{to-rgb($link-color)};
|
||||||
|
--#{$prefix}link-decoration: #{$link-decoration};
|
||||||
|
|
||||||
|
--#{$prefix}link-hover-color: #{$link-hover-color};
|
||||||
|
--#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color)};
|
||||||
|
|
||||||
|
@if $link-hover-decoration != null {
|
||||||
|
--#{$prefix}link-hover-decoration: #{$link-hover-decoration};
|
||||||
|
}
|
||||||
|
|
||||||
|
--#{$prefix}code-color: #{$code-color};
|
||||||
|
--#{$prefix}highlight-color: #{$mark-color};
|
||||||
|
--#{$prefix}highlight-bg: #{$mark-bg};
|
||||||
|
|
||||||
|
// scss-docs-start root-border-var
|
||||||
|
--#{$prefix}border-width: #{$border-width};
|
||||||
|
--#{$prefix}border-style: #{$border-style};
|
||||||
|
--#{$prefix}border-color: #{$border-color};
|
||||||
|
--#{$prefix}border-color-translucent: #{$border-color-translucent};
|
||||||
|
|
||||||
|
--#{$prefix}border-radius: #{$border-radius};
|
||||||
|
--#{$prefix}border-radius-sm: #{$border-radius-sm};
|
||||||
|
--#{$prefix}border-radius-lg: #{$border-radius-lg};
|
||||||
|
--#{$prefix}border-radius-xl: #{$border-radius-xl};
|
||||||
|
--#{$prefix}border-radius-xxl: #{$border-radius-xxl};
|
||||||
|
--#{$prefix}border-radius-2xl: var(--#{$prefix}border-radius-xxl); // Deprecated in v5.3.0 for consistency
|
||||||
|
--#{$prefix}border-radius-pill: #{$border-radius-pill};
|
||||||
|
// scss-docs-end root-border-var
|
||||||
|
|
||||||
|
--#{$prefix}box-shadow: #{$box-shadow};
|
||||||
|
--#{$prefix}box-shadow-sm: #{$box-shadow-sm};
|
||||||
|
--#{$prefix}box-shadow-lg: #{$box-shadow-lg};
|
||||||
|
--#{$prefix}box-shadow-inset: #{$box-shadow-inset};
|
||||||
|
|
||||||
|
// Focus styles
|
||||||
|
// scss-docs-start root-focus-variables
|
||||||
|
--#{$prefix}focus-ring-width: #{$focus-ring-width};
|
||||||
|
--#{$prefix}focus-ring-opacity: #{$focus-ring-opacity};
|
||||||
|
--#{$prefix}focus-ring-color: #{$focus-ring-color};
|
||||||
|
// scss-docs-end root-focus-variables
|
||||||
|
|
||||||
|
// scss-docs-start root-form-validation-variables
|
||||||
|
--#{$prefix}form-valid-color: #{$form-valid-color};
|
||||||
|
--#{$prefix}form-valid-border-color: #{$form-valid-border-color};
|
||||||
|
--#{$prefix}form-invalid-color: #{$form-invalid-color};
|
||||||
|
--#{$prefix}form-invalid-border-color: #{$form-invalid-border-color};
|
||||||
|
// scss-docs-end root-form-validation-variables
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark, true) {
|
||||||
|
color-scheme: dark;
|
||||||
|
|
||||||
|
// scss-docs-start root-dark-mode-vars
|
||||||
|
--#{$prefix}body-color: #{$body-color-dark};
|
||||||
|
--#{$prefix}body-color-rgb: #{to-rgb($body-color-dark)};
|
||||||
|
--#{$prefix}body-bg: #{$body-bg-dark};
|
||||||
|
--#{$prefix}body-bg-rgb: #{to-rgb($body-bg-dark)};
|
||||||
|
|
||||||
|
--#{$prefix}emphasis-color: #{$body-emphasis-color-dark};
|
||||||
|
--#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color-dark)};
|
||||||
|
|
||||||
|
--#{$prefix}secondary-color: #{$body-secondary-color-dark};
|
||||||
|
--#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color-dark)};
|
||||||
|
--#{$prefix}secondary-bg: #{$body-secondary-bg-dark};
|
||||||
|
--#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg-dark)};
|
||||||
|
|
||||||
|
--#{$prefix}tertiary-color: #{$body-tertiary-color-dark};
|
||||||
|
--#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color-dark)};
|
||||||
|
--#{$prefix}tertiary-bg: #{$body-tertiary-bg-dark};
|
||||||
|
--#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg-dark)};
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-text-dark {
|
||||||
|
--#{$prefix}#{$color}-text-emphasis: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-bg-subtle-dark {
|
||||||
|
--#{$prefix}#{$color}-bg-subtle: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors-border-subtle-dark {
|
||||||
|
--#{$prefix}#{$color}-border-subtle: #{$value};
|
||||||
|
}
|
||||||
|
|
||||||
|
--#{$prefix}heading-color: #{$headings-color-dark};
|
||||||
|
|
||||||
|
--#{$prefix}link-color: #{$link-color-dark};
|
||||||
|
--#{$prefix}link-hover-color: #{$link-hover-color-dark};
|
||||||
|
--#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)};
|
||||||
|
--#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)};
|
||||||
|
|
||||||
|
--#{$prefix}code-color: #{$code-color-dark};
|
||||||
|
--#{$prefix}highlight-color: #{$mark-color-dark};
|
||||||
|
--#{$prefix}highlight-bg: #{$mark-bg-dark};
|
||||||
|
|
||||||
|
--#{$prefix}border-color: #{$border-color-dark};
|
||||||
|
--#{$prefix}border-color-translucent: #{$border-color-translucent-dark};
|
||||||
|
|
||||||
|
--#{$prefix}form-valid-color: #{$form-valid-color-dark};
|
||||||
|
--#{$prefix}form-valid-border-color: #{$form-valid-border-color-dark};
|
||||||
|
--#{$prefix}form-invalid-color: #{$form-invalid-color-dark};
|
||||||
|
--#{$prefix}form-invalid-border-color: #{$form-invalid-border-color-dark};
|
||||||
|
// scss-docs-end root-dark-mode-vars
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,27 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
--#{$variable-prefix}table-bg: #{$table-bg};
|
// Reset needed for nesting tables
|
||||||
--#{$variable-prefix}table-accent-bg: #{$table-accent-bg};
|
--#{$prefix}table-color-type: initial;
|
||||||
--#{$variable-prefix}table-striped-color: #{$table-striped-color};
|
--#{$prefix}table-bg-type: initial;
|
||||||
--#{$variable-prefix}table-striped-bg: #{$table-striped-bg};
|
--#{$prefix}table-color-state: initial;
|
||||||
--#{$variable-prefix}table-active-color: #{$table-active-color};
|
--#{$prefix}table-bg-state: initial;
|
||||||
--#{$variable-prefix}table-active-bg: #{$table-active-bg};
|
// End of reset
|
||||||
--#{$variable-prefix}table-hover-color: #{$table-hover-color};
|
--#{$prefix}table-color: #{$table-color};
|
||||||
--#{$variable-prefix}table-hover-bg: #{$table-hover-bg};
|
--#{$prefix}table-bg: #{$table-bg};
|
||||||
|
--#{$prefix}table-border-color: #{$table-border-color};
|
||||||
|
--#{$prefix}table-accent-bg: #{$table-accent-bg};
|
||||||
|
--#{$prefix}table-striped-color: #{$table-striped-color};
|
||||||
|
--#{$prefix}table-striped-bg: #{$table-striped-bg};
|
||||||
|
--#{$prefix}table-active-color: #{$table-active-color};
|
||||||
|
--#{$prefix}table-active-bg: #{$table-active-bg};
|
||||||
|
--#{$prefix}table-hover-color: #{$table-hover-color};
|
||||||
|
--#{$prefix}table-hover-bg: #{$table-hover-bg};
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: $spacer;
|
margin-bottom: $spacer;
|
||||||
color: $table-color;
|
|
||||||
vertical-align: $table-cell-vertical-align;
|
vertical-align: $table-cell-vertical-align;
|
||||||
border-color: $table-border-color;
|
border-color: var(--#{$prefix}table-border-color);
|
||||||
|
|
||||||
// Target th & td
|
// Target th & td
|
||||||
// We need the child combinator to prevent styles leaking to nested tables which doesn't have a `.table` class.
|
// We need the child combinator to prevent styles leaking to nested tables which doesn't have a `.table` class.
|
||||||
@ -25,9 +32,11 @@
|
|||||||
// stylelint-disable-next-line selector-max-universal
|
// stylelint-disable-next-line selector-max-universal
|
||||||
> :not(caption) > * > * {
|
> :not(caption) > * > * {
|
||||||
padding: $table-cell-padding-y $table-cell-padding-x;
|
padding: $table-cell-padding-y $table-cell-padding-x;
|
||||||
background-color: var(--#{$variable-prefix}table-bg);
|
// Following the precept of cascades: https://codepen.io/miriamsuzanne/full/vYNgodb
|
||||||
|
color: var(--#{$prefix}table-color-state, var(--#{$prefix}table-color-type, var(--#{$prefix}table-color)));
|
||||||
|
background-color: var(--#{$prefix}table-bg);
|
||||||
border-bottom-width: $table-border-width;
|
border-bottom-width: $table-border-width;
|
||||||
box-shadow: inset 0 0 0 9999px var(--#{$variable-prefix}table-accent-bg);
|
box-shadow: inset 0 0 0 9999px var(--#{$prefix}table-bg-state, var(--#{$prefix}table-bg-type, var(--#{$prefix}table-accent-bg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
> tbody {
|
> tbody {
|
||||||
@ -37,13 +46,11 @@
|
|||||||
> thead {
|
> thead {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight border color between thead, tbody and tfoot.
|
|
||||||
> :not(:last-child) > :last-child > * {
|
|
||||||
border-bottom-color: $table-group-separator-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-group-divider {
|
||||||
|
border-top: calc(#{$table-border-width} * 2) solid $table-group-separator-color; // stylelint-disable-line function-disallowed-list
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Change placement of captions with a class
|
// Change placement of captions with a class
|
||||||
@ -91,16 +98,29 @@
|
|||||||
> :not(caption) > * > * {
|
> :not(caption) > * > * {
|
||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> :not(:first-child) {
|
||||||
|
border-top-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zebra-striping
|
// Zebra-striping
|
||||||
//
|
//
|
||||||
// Default zebra-stripe styles (alternating gray and transparent backgrounds)
|
// Default zebra-stripe styles (alternating gray and transparent backgrounds)
|
||||||
|
|
||||||
|
// For rows
|
||||||
.table-striped {
|
.table-striped {
|
||||||
> tbody > tr:nth-of-type(#{$table-striped-order}) {
|
> tbody > tr:nth-of-type(#{$table-striped-order}) > * {
|
||||||
--#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-striped-bg);
|
--#{$prefix}table-color-type: var(--#{$prefix}table-striped-color);
|
||||||
color: var(--#{$variable-prefix}table-striped-color);
|
--#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For columns
|
||||||
|
.table-striped-columns {
|
||||||
|
> :not(caption) > tr > :nth-child(#{$table-striped-columns-order}) {
|
||||||
|
--#{$prefix}table-color-type: var(--#{$prefix}table-striped-color);
|
||||||
|
--#{$prefix}table-bg-type: var(--#{$prefix}table-striped-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +129,8 @@
|
|||||||
// The `.table-active` class can be added to highlight rows or cells
|
// The `.table-active` class can be added to highlight rows or cells
|
||||||
|
|
||||||
.table-active {
|
.table-active {
|
||||||
--#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-active-bg);
|
--#{$prefix}table-color-state: var(--#{$prefix}table-active-color);
|
||||||
color: var(--#{$variable-prefix}table-active-color);
|
--#{$prefix}table-bg-state: var(--#{$prefix}table-active-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hover effect
|
// Hover effect
|
||||||
@ -118,9 +138,9 @@
|
|||||||
// Placed here since it has to come after the potential zebra striping
|
// Placed here since it has to come after the potential zebra striping
|
||||||
|
|
||||||
.table-hover {
|
.table-hover {
|
||||||
> tbody > tr:hover {
|
> tbody > tr:hover > * {
|
||||||
--#{$variable-prefix}table-accent-bg: var(--#{$variable-prefix}table-hover-bg);
|
--#{$prefix}table-color-state: var(--#{$prefix}table-hover-color);
|
||||||
color: var(--#{$variable-prefix}table-hover-color);
|
--#{$prefix}table-bg-state: var(--#{$prefix}table-hover-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +1,38 @@
|
|||||||
// Base class
|
// Base class
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: absolute;
|
// scss-docs-start tooltip-css-vars
|
||||||
z-index: $zindex-tooltip;
|
--#{$prefix}tooltip-zindex: #{$zindex-tooltip};
|
||||||
|
--#{$prefix}tooltip-max-width: #{$tooltip-max-width};
|
||||||
|
--#{$prefix}tooltip-padding-x: #{$tooltip-padding-x};
|
||||||
|
--#{$prefix}tooltip-padding-y: #{$tooltip-padding-y};
|
||||||
|
--#{$prefix}tooltip-margin: #{$tooltip-margin};
|
||||||
|
@include rfs($tooltip-font-size, --#{$prefix}tooltip-font-size);
|
||||||
|
--#{$prefix}tooltip-color: #{$tooltip-color};
|
||||||
|
--#{$prefix}tooltip-bg: #{$tooltip-bg};
|
||||||
|
--#{$prefix}tooltip-border-radius: #{$tooltip-border-radius};
|
||||||
|
--#{$prefix}tooltip-opacity: #{$tooltip-opacity};
|
||||||
|
--#{$prefix}tooltip-arrow-width: #{$tooltip-arrow-width};
|
||||||
|
--#{$prefix}tooltip-arrow-height: #{$tooltip-arrow-height};
|
||||||
|
// scss-docs-end tooltip-css-vars
|
||||||
|
|
||||||
|
z-index: var(--#{$prefix}tooltip-zindex);
|
||||||
display: block;
|
display: block;
|
||||||
margin: $tooltip-margin;
|
margin: var(--#{$prefix}tooltip-margin);
|
||||||
|
@include deprecate("`$tooltip-margin`", "v5", "v5.x", true);
|
||||||
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
|
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
|
||||||
// So reset our font and text properties to avoid inheriting weird values.
|
// So reset our font and text properties to avoid inheriting weird values.
|
||||||
@include reset-text();
|
@include reset-text();
|
||||||
@include font-size($tooltip-font-size);
|
@include font-size(var(--#{$prefix}tooltip-font-size));
|
||||||
// Allow breaking very long words so they don't overflow the tooltip's bounds
|
// Allow breaking very long words so they don't overflow the tooltip's bounds
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
&.show { opacity: $tooltip-opacity; }
|
&.show { opacity: var(--#{$prefix}tooltip-opacity); }
|
||||||
|
|
||||||
.tooltip-arrow {
|
.tooltip-arrow {
|
||||||
position: absolute;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: $tooltip-arrow-width;
|
width: var(--#{$prefix}tooltip-arrow-width);
|
||||||
height: $tooltip-arrow-height;
|
height: var(--#{$prefix}tooltip-arrow-height);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -29,66 +43,56 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bs-tooltip-top {
|
.bs-tooltip-top .tooltip-arrow {
|
||||||
padding: $tooltip-arrow-height 0;
|
bottom: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
|
||||||
|
|
||||||
.tooltip-arrow {
|
&::before {
|
||||||
bottom: 0;
|
top: -1px;
|
||||||
|
border-width: var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
|
||||||
&::before {
|
border-top-color: var(--#{$prefix}tooltip-bg);
|
||||||
top: -1px;
|
|
||||||
border-width: $tooltip-arrow-height ($tooltip-arrow-width * .5) 0;
|
|
||||||
border-top-color: $tooltip-arrow-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bs-tooltip-end {
|
/* rtl:begin:ignore */
|
||||||
padding: 0 $tooltip-arrow-height;
|
.bs-tooltip-end .tooltip-arrow {
|
||||||
|
left: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
|
||||||
|
width: var(--#{$prefix}tooltip-arrow-height);
|
||||||
|
height: var(--#{$prefix}tooltip-arrow-width);
|
||||||
|
|
||||||
.tooltip-arrow {
|
&::before {
|
||||||
left: 0;
|
right: -1px;
|
||||||
width: $tooltip-arrow-height;
|
border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
|
||||||
height: $tooltip-arrow-width;
|
border-right-color: var(--#{$prefix}tooltip-bg);
|
||||||
|
|
||||||
&::before {
|
|
||||||
right: -1px;
|
|
||||||
border-width: ($tooltip-arrow-width * .5) $tooltip-arrow-height ($tooltip-arrow-width * .5) 0;
|
|
||||||
border-right-color: $tooltip-arrow-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bs-tooltip-bottom {
|
/* rtl:end:ignore */
|
||||||
padding: $tooltip-arrow-height 0;
|
|
||||||
|
|
||||||
.tooltip-arrow {
|
.bs-tooltip-bottom .tooltip-arrow {
|
||||||
top: 0;
|
top: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
bottom: -1px;
|
bottom: -1px;
|
||||||
border-width: 0 ($tooltip-arrow-width * .5) $tooltip-arrow-height;
|
border-width: 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
|
||||||
border-bottom-color: $tooltip-arrow-color;
|
border-bottom-color: var(--#{$prefix}tooltip-bg);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bs-tooltip-start {
|
/* rtl:begin:ignore */
|
||||||
padding: 0 $tooltip-arrow-height;
|
.bs-tooltip-start .tooltip-arrow {
|
||||||
|
right: calc(-1 * var(--#{$prefix}tooltip-arrow-height)); // stylelint-disable-line function-disallowed-list
|
||||||
|
width: var(--#{$prefix}tooltip-arrow-height);
|
||||||
|
height: var(--#{$prefix}tooltip-arrow-width);
|
||||||
|
|
||||||
.tooltip-arrow {
|
&::before {
|
||||||
right: 0;
|
left: -1px;
|
||||||
width: $tooltip-arrow-height;
|
border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
|
||||||
height: $tooltip-arrow-width;
|
border-left-color: var(--#{$prefix}tooltip-bg);
|
||||||
|
|
||||||
&::before {
|
|
||||||
left: -1px;
|
|
||||||
border-width: ($tooltip-arrow-width * .5) 0 ($tooltip-arrow-width * .5) $tooltip-arrow-height;
|
|
||||||
border-left-color: $tooltip-arrow-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* rtl:end:ignore */
|
||||||
|
|
||||||
.bs-tooltip-auto {
|
.bs-tooltip-auto {
|
||||||
&[data-popper-placement^="top"] {
|
&[data-popper-placement^="top"] {
|
||||||
@extend .bs-tooltip-top;
|
@extend .bs-tooltip-top;
|
||||||
@ -106,10 +110,10 @@
|
|||||||
|
|
||||||
// Wrapper for the tooltip content
|
// Wrapper for the tooltip content
|
||||||
.tooltip-inner {
|
.tooltip-inner {
|
||||||
max-width: $tooltip-max-width;
|
max-width: var(--#{$prefix}tooltip-max-width);
|
||||||
padding: $tooltip-padding-y $tooltip-padding-x;
|
padding: var(--#{$prefix}tooltip-padding-y) var(--#{$prefix}tooltip-padding-x);
|
||||||
color: $tooltip-color;
|
color: var(--#{$prefix}tooltip-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: $tooltip-bg;
|
background-color: var(--#{$prefix}tooltip-bg);
|
||||||
@include border-radius($tooltip-border-radius);
|
@include border-radius(var(--#{$prefix}tooltip-border-radius));
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,11 @@
|
|||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@include transition($transition-collapse);
|
@include transition($transition-collapse);
|
||||||
|
|
||||||
|
&.collapse-horizontal {
|
||||||
|
width: 0;
|
||||||
|
height: auto;
|
||||||
|
@include transition($transition-collapse-width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// scss-docs-end collapse-classes
|
// scss-docs-end collapse-classes
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
@each $display, $font-size in $display-font-sizes {
|
@each $display, $font-size in $display-font-sizes {
|
||||||
.display-#{$display} {
|
.display-#{$display} {
|
||||||
@include font-size($font-size);
|
@include font-size($font-size);
|
||||||
|
font-family: $display-font-family;
|
||||||
|
font-style: $display-font-style;
|
||||||
font-weight: $display-font-weight;
|
font-weight: $display-font-weight;
|
||||||
line-height: $display-line-height;
|
line-height: $display-line-height;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// stylelint-disable indentation
|
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
||||||
$utilities: () !default;
|
$utilities: () !default;
|
||||||
@ -24,11 +22,46 @@ $utilities: map-merge(
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
// scss-docs-end utils-float
|
// scss-docs-end utils-float
|
||||||
|
// Object Fit utilities
|
||||||
|
// scss-docs-start utils-object-fit
|
||||||
|
"object-fit": (
|
||||||
|
responsive: true,
|
||||||
|
property: object-fit,
|
||||||
|
values: (
|
||||||
|
contain: contain,
|
||||||
|
cover: cover,
|
||||||
|
fill: fill,
|
||||||
|
scale: scale-down,
|
||||||
|
none: none,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// scss-docs-end utils-object-fit
|
||||||
|
// Opacity utilities
|
||||||
|
// scss-docs-start utils-opacity
|
||||||
|
"opacity": (
|
||||||
|
property: opacity,
|
||||||
|
values: (
|
||||||
|
0: 0,
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
// scss-docs-end utils-opacity
|
||||||
// scss-docs-start utils-overflow
|
// scss-docs-start utils-overflow
|
||||||
"overflow": (
|
"overflow": (
|
||||||
property: overflow,
|
property: overflow,
|
||||||
values: auto hidden visible scroll,
|
values: auto hidden visible scroll,
|
||||||
),
|
),
|
||||||
|
"overflow-x": (
|
||||||
|
property: overflow-x,
|
||||||
|
values: auto hidden visible scroll,
|
||||||
|
),
|
||||||
|
"overflow-y": (
|
||||||
|
property: overflow-y,
|
||||||
|
values: auto hidden visible scroll,
|
||||||
|
),
|
||||||
// scss-docs-end utils-overflow
|
// scss-docs-end utils-overflow
|
||||||
// scss-docs-start utils-display
|
// scss-docs-start utils-display
|
||||||
"display": (
|
"display": (
|
||||||
@ -36,7 +69,7 @@ $utilities: map-merge(
|
|||||||
print: true,
|
print: true,
|
||||||
property: display,
|
property: display,
|
||||||
class: d,
|
class: d,
|
||||||
values: inline inline-block block grid table table-row table-cell flex inline-flex none
|
values: inline inline-block block grid inline-grid table table-row table-cell flex inline-flex none
|
||||||
),
|
),
|
||||||
// scss-docs-end utils-display
|
// scss-docs-end utils-display
|
||||||
// scss-docs-start utils-shadow
|
// scss-docs-start utils-shadow
|
||||||
@ -44,13 +77,21 @@ $utilities: map-merge(
|
|||||||
property: box-shadow,
|
property: box-shadow,
|
||||||
class: shadow,
|
class: shadow,
|
||||||
values: (
|
values: (
|
||||||
null: $box-shadow,
|
null: var(--#{$prefix}box-shadow),
|
||||||
sm: $box-shadow-sm,
|
sm: var(--#{$prefix}box-shadow-sm),
|
||||||
lg: $box-shadow-lg,
|
lg: var(--#{$prefix}box-shadow-lg),
|
||||||
none: none,
|
none: none,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
// scss-docs-end utils-shadow
|
// scss-docs-end utils-shadow
|
||||||
|
// scss-docs-start utils-focus-ring
|
||||||
|
"focus-ring": (
|
||||||
|
css-var: true,
|
||||||
|
css-variable-name: focus-ring-color,
|
||||||
|
class: focus-ring,
|
||||||
|
values: map-loop($theme-colors-rgb, rgba-css-var, "$key", "focus-ring")
|
||||||
|
),
|
||||||
|
// scss-docs-end utils-focus-ring
|
||||||
// scss-docs-start utils-position
|
// scss-docs-start utils-position
|
||||||
"position": (
|
"position": (
|
||||||
property: position,
|
property: position,
|
||||||
@ -88,14 +129,14 @@ $utilities: map-merge(
|
|||||||
"border": (
|
"border": (
|
||||||
property: border,
|
property: border,
|
||||||
values: (
|
values: (
|
||||||
null: $border-width solid $border-color,
|
null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
|
||||||
0: 0,
|
0: 0,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"border-top": (
|
"border-top": (
|
||||||
property: border-top,
|
property: border-top,
|
||||||
values: (
|
values: (
|
||||||
null: $border-width solid $border-color,
|
null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
|
||||||
0: 0,
|
0: 0,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -103,14 +144,14 @@ $utilities: map-merge(
|
|||||||
property: border-right,
|
property: border-right,
|
||||||
class: border-end,
|
class: border-end,
|
||||||
values: (
|
values: (
|
||||||
null: $border-width solid $border-color,
|
null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
|
||||||
0: 0,
|
0: 0,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"border-bottom": (
|
"border-bottom": (
|
||||||
property: border-bottom,
|
property: border-bottom,
|
||||||
values: (
|
values: (
|
||||||
null: $border-width solid $border-color,
|
null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
|
||||||
0: 0,
|
0: 0,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -118,20 +159,39 @@ $utilities: map-merge(
|
|||||||
property: border-left,
|
property: border-left,
|
||||||
class: border-start,
|
class: border-start,
|
||||||
values: (
|
values: (
|
||||||
null: $border-width solid $border-color,
|
null: var(--#{$prefix}border-width) var(--#{$prefix}border-style) var(--#{$prefix}border-color),
|
||||||
0: 0,
|
0: 0,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"border-color": (
|
"border-color": (
|
||||||
property: border-color,
|
property: border-color,
|
||||||
class: border,
|
class: border,
|
||||||
values: map-merge($theme-colors, ("white": $white))
|
local-vars: (
|
||||||
|
"border-opacity": 1
|
||||||
|
),
|
||||||
|
values: $utilities-border-colors
|
||||||
|
),
|
||||||
|
"subtle-border-color": (
|
||||||
|
property: border-color,
|
||||||
|
class: border,
|
||||||
|
values: $utilities-border-subtle
|
||||||
),
|
),
|
||||||
"border-width": (
|
"border-width": (
|
||||||
property: border-width,
|
property: border-width,
|
||||||
class: border,
|
class: border,
|
||||||
values: $border-widths
|
values: $border-widths
|
||||||
),
|
),
|
||||||
|
"border-opacity": (
|
||||||
|
css-var: true,
|
||||||
|
class: border-opacity,
|
||||||
|
values: (
|
||||||
|
10: .1,
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1
|
||||||
|
)
|
||||||
|
),
|
||||||
// scss-docs-end utils-borders
|
// scss-docs-end utils-borders
|
||||||
// Sizing utilities
|
// Sizing utilities
|
||||||
// scss-docs-start utils-sizing
|
// scss-docs-start utils-sizing
|
||||||
@ -225,12 +285,6 @@ $utilities: map-merge(
|
|||||||
class: flex,
|
class: flex,
|
||||||
values: wrap nowrap wrap-reverse
|
values: wrap nowrap wrap-reverse
|
||||||
),
|
),
|
||||||
"gap": (
|
|
||||||
responsive: true,
|
|
||||||
property: gap,
|
|
||||||
class: gap,
|
|
||||||
values: $spacers
|
|
||||||
),
|
|
||||||
"justify-content": (
|
"justify-content": (
|
||||||
responsive: true,
|
responsive: true,
|
||||||
property: justify-content,
|
property: justify-content,
|
||||||
@ -423,13 +477,32 @@ $utilities: map-merge(
|
|||||||
class: ps,
|
class: ps,
|
||||||
values: $spacers
|
values: $spacers
|
||||||
),
|
),
|
||||||
|
// Gap utility
|
||||||
|
"gap": (
|
||||||
|
responsive: true,
|
||||||
|
property: gap,
|
||||||
|
class: gap,
|
||||||
|
values: $spacers
|
||||||
|
),
|
||||||
|
"row-gap": (
|
||||||
|
responsive: true,
|
||||||
|
property: row-gap,
|
||||||
|
class: row-gap,
|
||||||
|
values: $spacers
|
||||||
|
),
|
||||||
|
"column-gap": (
|
||||||
|
responsive: true,
|
||||||
|
property: column-gap,
|
||||||
|
class: column-gap,
|
||||||
|
values: $spacers
|
||||||
|
),
|
||||||
// scss-docs-end utils-spacing
|
// scss-docs-end utils-spacing
|
||||||
// Text
|
// Text
|
||||||
// scss-docs-start utils-text
|
// scss-docs-start utils-text
|
||||||
"font-family": (
|
"font-family": (
|
||||||
property: font-family,
|
property: font-family,
|
||||||
class: font,
|
class: font,
|
||||||
values: (monospace: var(--#{$variable-prefix}font-monospace))
|
values: (monospace: var(--#{$prefix}font-monospace))
|
||||||
),
|
),
|
||||||
"font-size": (
|
"font-size": (
|
||||||
rfs: true,
|
rfs: true,
|
||||||
@ -446,9 +519,11 @@ $utilities: map-merge(
|
|||||||
property: font-weight,
|
property: font-weight,
|
||||||
class: fw,
|
class: fw,
|
||||||
values: (
|
values: (
|
||||||
light: $font-weight-light,
|
|
||||||
lighter: $font-weight-lighter,
|
lighter: $font-weight-lighter,
|
||||||
|
light: $font-weight-light,
|
||||||
normal: $font-weight-normal,
|
normal: $font-weight-normal,
|
||||||
|
medium: $font-weight-medium,
|
||||||
|
semibold: $font-weight-semibold,
|
||||||
bold: $font-weight-bold,
|
bold: $font-weight-bold,
|
||||||
bolder: $font-weight-bolder
|
bolder: $font-weight-bolder
|
||||||
)
|
)
|
||||||
@ -501,37 +576,125 @@ $utilities: map-merge(
|
|||||||
"color": (
|
"color": (
|
||||||
property: color,
|
property: color,
|
||||||
class: text,
|
class: text,
|
||||||
|
local-vars: (
|
||||||
|
"text-opacity": 1
|
||||||
|
),
|
||||||
values: map-merge(
|
values: map-merge(
|
||||||
$theme-colors,
|
$utilities-text-colors,
|
||||||
(
|
(
|
||||||
"white": $white,
|
"muted": var(--#{$prefix}secondary-color), // deprecated
|
||||||
"body": $body-color,
|
"black-50": rgba($black, .5), // deprecated
|
||||||
"muted": $text-muted,
|
"white-50": rgba($white, .5), // deprecated
|
||||||
"black-50": rgba($black, .5),
|
"body-secondary": var(--#{$prefix}secondary-color),
|
||||||
"white-50": rgba($white, .5),
|
"body-tertiary": var(--#{$prefix}tertiary-color),
|
||||||
|
"body-emphasis": var(--#{$prefix}emphasis-color),
|
||||||
"reset": inherit,
|
"reset": inherit,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
"text-opacity": (
|
||||||
|
css-var: true,
|
||||||
|
class: text-opacity,
|
||||||
|
values: (
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"text-color": (
|
||||||
|
property: color,
|
||||||
|
class: text,
|
||||||
|
values: $utilities-text-emphasis-colors
|
||||||
|
),
|
||||||
// scss-docs-end utils-color
|
// scss-docs-end utils-color
|
||||||
|
// scss-docs-start utils-links
|
||||||
|
"link-opacity": (
|
||||||
|
css-var: true,
|
||||||
|
class: link-opacity,
|
||||||
|
state: hover,
|
||||||
|
values: (
|
||||||
|
10: .1,
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"link-offset": (
|
||||||
|
property: text-underline-offset,
|
||||||
|
class: link-offset,
|
||||||
|
state: hover,
|
||||||
|
values: (
|
||||||
|
1: .125em,
|
||||||
|
2: .25em,
|
||||||
|
3: .375em,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"link-underline": (
|
||||||
|
property: text-decoration-color,
|
||||||
|
class: link-underline,
|
||||||
|
local-vars: (
|
||||||
|
"link-underline-opacity": 1
|
||||||
|
),
|
||||||
|
values: map-merge(
|
||||||
|
$utilities-links-underline,
|
||||||
|
(
|
||||||
|
null: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-underline-opacity, 1)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"link-underline-opacity": (
|
||||||
|
css-var: true,
|
||||||
|
class: link-underline-opacity,
|
||||||
|
state: hover,
|
||||||
|
values: (
|
||||||
|
0: 0,
|
||||||
|
10: .1,
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// scss-docs-end utils-links
|
||||||
// scss-docs-start utils-bg-color
|
// scss-docs-start utils-bg-color
|
||||||
"background-color": (
|
"background-color": (
|
||||||
property: background-color,
|
property: background-color,
|
||||||
class: bg,
|
class: bg,
|
||||||
|
local-vars: (
|
||||||
|
"bg-opacity": 1
|
||||||
|
),
|
||||||
values: map-merge(
|
values: map-merge(
|
||||||
$theme-colors,
|
$utilities-bg-colors,
|
||||||
(
|
(
|
||||||
"body": $body-bg,
|
"transparent": transparent,
|
||||||
"white": $white,
|
"body-secondary": rgba(var(--#{$prefix}secondary-bg-rgb), var(--#{$prefix}bg-opacity)),
|
||||||
"transparent": transparent
|
"body-tertiary": rgba(var(--#{$prefix}tertiary-bg-rgb), var(--#{$prefix}bg-opacity)),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
"bg-opacity": (
|
||||||
|
css-var: true,
|
||||||
|
class: bg-opacity,
|
||||||
|
values: (
|
||||||
|
10: .1,
|
||||||
|
25: .25,
|
||||||
|
50: .5,
|
||||||
|
75: .75,
|
||||||
|
100: 1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"subtle-background-color": (
|
||||||
|
property: background-color,
|
||||||
|
class: bg,
|
||||||
|
values: $utilities-bg-subtle
|
||||||
|
),
|
||||||
// scss-docs-end utils-bg-color
|
// scss-docs-end utils-bg-color
|
||||||
"gradient": (
|
"gradient": (
|
||||||
property: background-image,
|
property: background-image,
|
||||||
class: bg,
|
class: bg,
|
||||||
values: (gradient: var(--#{$variable-prefix}gradient))
|
values: (gradient: var(--#{$prefix}gradient))
|
||||||
),
|
),
|
||||||
// scss-docs-start utils-interaction
|
// scss-docs-start utils-interaction
|
||||||
"user-select": (
|
"user-select": (
|
||||||
@ -549,34 +712,76 @@ $utilities: map-merge(
|
|||||||
property: border-radius,
|
property: border-radius,
|
||||||
class: rounded,
|
class: rounded,
|
||||||
values: (
|
values: (
|
||||||
null: $border-radius,
|
null: var(--#{$prefix}border-radius),
|
||||||
0: 0,
|
0: 0,
|
||||||
1: $border-radius-sm,
|
1: var(--#{$prefix}border-radius-sm),
|
||||||
2: $border-radius,
|
2: var(--#{$prefix}border-radius),
|
||||||
3: $border-radius-lg,
|
3: var(--#{$prefix}border-radius-lg),
|
||||||
|
4: var(--#{$prefix}border-radius-xl),
|
||||||
|
5: var(--#{$prefix}border-radius-xxl),
|
||||||
circle: 50%,
|
circle: 50%,
|
||||||
pill: $border-radius-pill
|
pill: var(--#{$prefix}border-radius-pill)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"rounded-top": (
|
"rounded-top": (
|
||||||
property: border-top-left-radius border-top-right-radius,
|
property: border-top-left-radius border-top-right-radius,
|
||||||
class: rounded-top,
|
class: rounded-top,
|
||||||
values: (null: $border-radius)
|
values: (
|
||||||
|
null: var(--#{$prefix}border-radius),
|
||||||
|
0: 0,
|
||||||
|
1: var(--#{$prefix}border-radius-sm),
|
||||||
|
2: var(--#{$prefix}border-radius),
|
||||||
|
3: var(--#{$prefix}border-radius-lg),
|
||||||
|
4: var(--#{$prefix}border-radius-xl),
|
||||||
|
5: var(--#{$prefix}border-radius-xxl),
|
||||||
|
circle: 50%,
|
||||||
|
pill: var(--#{$prefix}border-radius-pill)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
"rounded-end": (
|
"rounded-end": (
|
||||||
property: border-top-right-radius border-bottom-right-radius,
|
property: border-top-right-radius border-bottom-right-radius,
|
||||||
class: rounded-end,
|
class: rounded-end,
|
||||||
values: (null: $border-radius)
|
values: (
|
||||||
|
null: var(--#{$prefix}border-radius),
|
||||||
|
0: 0,
|
||||||
|
1: var(--#{$prefix}border-radius-sm),
|
||||||
|
2: var(--#{$prefix}border-radius),
|
||||||
|
3: var(--#{$prefix}border-radius-lg),
|
||||||
|
4: var(--#{$prefix}border-radius-xl),
|
||||||
|
5: var(--#{$prefix}border-radius-xxl),
|
||||||
|
circle: 50%,
|
||||||
|
pill: var(--#{$prefix}border-radius-pill)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
"rounded-bottom": (
|
"rounded-bottom": (
|
||||||
property: border-bottom-right-radius border-bottom-left-radius,
|
property: border-bottom-right-radius border-bottom-left-radius,
|
||||||
class: rounded-bottom,
|
class: rounded-bottom,
|
||||||
values: (null: $border-radius)
|
values: (
|
||||||
|
null: var(--#{$prefix}border-radius),
|
||||||
|
0: 0,
|
||||||
|
1: var(--#{$prefix}border-radius-sm),
|
||||||
|
2: var(--#{$prefix}border-radius),
|
||||||
|
3: var(--#{$prefix}border-radius-lg),
|
||||||
|
4: var(--#{$prefix}border-radius-xl),
|
||||||
|
5: var(--#{$prefix}border-radius-xxl),
|
||||||
|
circle: 50%,
|
||||||
|
pill: var(--#{$prefix}border-radius-pill)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
"rounded-start": (
|
"rounded-start": (
|
||||||
property: border-bottom-left-radius border-top-left-radius,
|
property: border-bottom-left-radius border-top-left-radius,
|
||||||
class: rounded-start,
|
class: rounded-start,
|
||||||
values: (null: $border-radius)
|
values: (
|
||||||
|
null: var(--#{$prefix}border-radius),
|
||||||
|
0: 0,
|
||||||
|
1: var(--#{$prefix}border-radius-sm),
|
||||||
|
2: var(--#{$prefix}border-radius),
|
||||||
|
3: var(--#{$prefix}border-radius-lg),
|
||||||
|
4: var(--#{$prefix}border-radius-xl),
|
||||||
|
5: var(--#{$prefix}border-radius-xxl),
|
||||||
|
circle: 50%,
|
||||||
|
pill: var(--#{$prefix}border-radius-pill)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
// scss-docs-end utils-border-radius
|
// scss-docs-end utils-border-radius
|
||||||
// scss-docs-start utils-visibility
|
// scss-docs-start utils-visibility
|
||||||
@ -587,8 +792,15 @@ $utilities: map-merge(
|
|||||||
visible: visible,
|
visible: visible,
|
||||||
invisible: hidden,
|
invisible: hidden,
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
// scss-docs-end utils-visibility
|
// scss-docs-end utils-visibility
|
||||||
|
// scss-docs-start utils-zindex
|
||||||
|
"z-index": (
|
||||||
|
property: z-index,
|
||||||
|
class: z,
|
||||||
|
values: $zindex-levels,
|
||||||
|
)
|
||||||
|
// scss-docs-end utils-zindex
|
||||||
),
|
),
|
||||||
$utilities
|
$utilities
|
||||||
);
|
);
|
||||||
|
87
resources/themes/architect/src/components/bootstrap5/_variables-dark.scss
vendored
Normal file
87
resources/themes/architect/src/components/bootstrap5/_variables-dark.scss
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Dark color mode variables
|
||||||
|
//
|
||||||
|
// Custom variables for the `[data-bs-theme="dark"]` theme. Use this as a starting point for your own custom color modes by creating a new theme-specific file like `_variables-dark.scss` and adding the variables you need.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Global colors
|
||||||
|
//
|
||||||
|
|
||||||
|
// scss-docs-start sass-dark-mode-vars
|
||||||
|
// scss-docs-start theme-text-dark-variables
|
||||||
|
$primary-text-emphasis-dark: tint-color($primary, 40%) !default;
|
||||||
|
$secondary-text-emphasis-dark: tint-color($secondary, 40%) !default;
|
||||||
|
$success-text-emphasis-dark: tint-color($success, 40%) !default;
|
||||||
|
$info-text-emphasis-dark: tint-color($info, 40%) !default;
|
||||||
|
$warning-text-emphasis-dark: tint-color($warning, 40%) !default;
|
||||||
|
$danger-text-emphasis-dark: tint-color($danger, 40%) !default;
|
||||||
|
$light-text-emphasis-dark: $gray-100 !default;
|
||||||
|
$dark-text-emphasis-dark: $gray-300 !default;
|
||||||
|
// scss-docs-end theme-text-dark-variables
|
||||||
|
|
||||||
|
// scss-docs-start theme-bg-subtle-dark-variables
|
||||||
|
$primary-bg-subtle-dark: shade-color($primary, 80%) !default;
|
||||||
|
$secondary-bg-subtle-dark: shade-color($secondary, 80%) !default;
|
||||||
|
$success-bg-subtle-dark: shade-color($success, 80%) !default;
|
||||||
|
$info-bg-subtle-dark: shade-color($info, 80%) !default;
|
||||||
|
$warning-bg-subtle-dark: shade-color($warning, 80%) !default;
|
||||||
|
$danger-bg-subtle-dark: shade-color($danger, 80%) !default;
|
||||||
|
$light-bg-subtle-dark: $gray-800 !default;
|
||||||
|
$dark-bg-subtle-dark: mix($gray-800, $black) !default;
|
||||||
|
// scss-docs-end theme-bg-subtle-dark-variables
|
||||||
|
|
||||||
|
// scss-docs-start theme-border-subtle-dark-variables
|
||||||
|
$primary-border-subtle-dark: shade-color($primary, 40%) !default;
|
||||||
|
$secondary-border-subtle-dark: shade-color($secondary, 40%) !default;
|
||||||
|
$success-border-subtle-dark: shade-color($success, 40%) !default;
|
||||||
|
$info-border-subtle-dark: shade-color($info, 40%) !default;
|
||||||
|
$warning-border-subtle-dark: shade-color($warning, 40%) !default;
|
||||||
|
$danger-border-subtle-dark: shade-color($danger, 40%) !default;
|
||||||
|
$light-border-subtle-dark: $gray-700 !default;
|
||||||
|
$dark-border-subtle-dark: $gray-800 !default;
|
||||||
|
// scss-docs-end theme-border-subtle-dark-variables
|
||||||
|
|
||||||
|
$body-color-dark: $gray-300 !default;
|
||||||
|
$body-bg-dark: $gray-900 !default;
|
||||||
|
$body-secondary-color-dark: rgba($body-color-dark, .75) !default;
|
||||||
|
$body-secondary-bg-dark: $gray-800 !default;
|
||||||
|
$body-tertiary-color-dark: rgba($body-color-dark, .5) !default;
|
||||||
|
$body-tertiary-bg-dark: mix($gray-800, $gray-900, 50%) !default;
|
||||||
|
$body-emphasis-color-dark: $white !default;
|
||||||
|
$border-color-dark: $gray-700 !default;
|
||||||
|
$border-color-translucent-dark: rgba($white, .15) !default;
|
||||||
|
$headings-color-dark: inherit !default;
|
||||||
|
$link-color-dark: tint-color($primary, 40%) !default;
|
||||||
|
$link-hover-color-dark: shift-color($link-color-dark, -$link-shade-percentage) !default;
|
||||||
|
$code-color-dark: tint-color($code-color, 40%) !default;
|
||||||
|
$mark-color-dark: $body-color-dark !default;
|
||||||
|
$mark-bg-dark: $yellow-800 !default;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Forms
|
||||||
|
//
|
||||||
|
|
||||||
|
$form-select-indicator-color-dark: $body-color-dark !default;
|
||||||
|
$form-select-indicator-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='#{$form-select-indicator-color-dark}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/></svg>") !default;
|
||||||
|
|
||||||
|
$form-switch-color-dark: rgba($white, .25) !default;
|
||||||
|
$form-switch-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color-dark}'/></svg>") !default;
|
||||||
|
|
||||||
|
// scss-docs-start form-validation-colors-dark
|
||||||
|
$form-valid-color-dark: $green-300 !default;
|
||||||
|
$form-valid-border-color-dark: $green-300 !default;
|
||||||
|
$form-invalid-color-dark: $red-300 !default;
|
||||||
|
$form-invalid-border-color-dark: $red-300 !default;
|
||||||
|
// scss-docs-end form-validation-colors-dark
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Accordion
|
||||||
|
//
|
||||||
|
|
||||||
|
$accordion-icon-color-dark: $primary-text-emphasis-dark !default;
|
||||||
|
$accordion-icon-active-color-dark: $primary-text-emphasis-dark !default;
|
||||||
|
|
||||||
|
$accordion-button-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>") !default;
|
||||||
|
$accordion-button-active-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>") !default;
|
||||||
|
// scss-docs-end sass-dark-mode-vars
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,13 @@
|
|||||||
/*!
|
@import "mixins/banner";
|
||||||
* Bootstrap Grid v5.0.2 (https://getbootstrap.com/)
|
@include bsBanner(Grid);
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
*/
|
|
||||||
|
|
||||||
$include-column-box-sizing: true !default;
|
$include-column-box-sizing: true !default;
|
||||||
|
|
||||||
@import "functions";
|
@import "functions";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
@import "variables-dark";
|
||||||
|
@import "maps";
|
||||||
|
|
||||||
@import "mixins/lists";
|
|
||||||
@import "mixins/breakpoints";
|
@import "mixins/breakpoints";
|
||||||
@import "mixins/container";
|
@import "mixins/container";
|
||||||
@import "mixins/grid";
|
@import "mixins/grid";
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
/*!
|
@import "mixins/banner";
|
||||||
* Bootstrap Reboot v5.0.2 (https://getbootstrap.com/)
|
@include bsBanner(Reboot);
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "functions";
|
@import "functions";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
// Prevent the usage of custom properties since we don't add them to `:root` in reboot
|
@import "variables-dark";
|
||||||
$font-family-base: $font-family-sans-serif; // stylelint-disable-line scss/dollar-variable-default
|
@import "maps";
|
||||||
$font-family-code: $font-family-monospace; // stylelint-disable-line scss/dollar-variable-default
|
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
@import "root";
|
||||||
@import "reboot";
|
@import "reboot";
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
/*!
|
@import "mixins/banner";
|
||||||
* Bootstrap Utilities v5.0.2 (https://getbootstrap.com/)
|
@include bsBanner(Utilities);
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
@import "functions";
|
@import "functions";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
@import "variables-dark";
|
||||||
|
@import "maps";
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
@import "utilities";
|
@import "utilities";
|
||||||
|
|
||||||
|
// Layout & components
|
||||||
|
@import "root";
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
@import "helpers";
|
@import "helpers";
|
||||||
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
/*!
|
@import "mixins/banner";
|
||||||
* Bootstrap v5.0.2 (https://getbootstrap.com/)
|
@include bsBanner("");
|
||||||
* Copyright 2011-2021 The Bootstrap Authors
|
|
||||||
* Copyright 2011-2021 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
||||||
*/
|
|
||||||
|
|
||||||
// scss-docs-start import-stack
|
// scss-docs-start import-stack
|
||||||
// Configuration
|
// Configuration
|
||||||
@import "functions";
|
@import "functions";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
@import "variables-dark";
|
||||||
|
@import "maps";
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
@import "utilities";
|
@import "utilities";
|
||||||
|
|
||||||
@ -43,6 +42,7 @@
|
|||||||
@import "carousel";
|
@import "carousel";
|
||||||
@import "spinners";
|
@import "spinners";
|
||||||
@import "offcanvas";
|
@import "offcanvas";
|
||||||
|
@import "placeholders";
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
@import "helpers";
|
@import "helpers";
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
> .form-control,
|
> .form-control,
|
||||||
|
> .form-control-plaintext,
|
||||||
> .form-select {
|
> .form-select {
|
||||||
height: $form-floating-height;
|
height: $form-floating-height;
|
||||||
|
min-height: $form-floating-height;
|
||||||
line-height: $form-floating-line-height;
|
line-height: $form-floating-line-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,16 +13,21 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
height: 100%; // allow textareas
|
height: 100%; // allow textareas
|
||||||
padding: $form-floating-padding-y $form-floating-padding-x;
|
padding: $form-floating-padding-y $form-floating-padding-x;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: start;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
|
border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
@include transition($form-floating-transition);
|
@include transition($form-floating-transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// stylelint-disable no-duplicate-selectors
|
> .form-control,
|
||||||
> .form-control {
|
> .form-control-plaintext {
|
||||||
padding: $form-floating-padding-y $form-floating-padding-x;
|
padding: $form-floating-padding-y $form-floating-padding-x;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@ -46,18 +53,43 @@
|
|||||||
|
|
||||||
> .form-control:focus,
|
> .form-control:focus,
|
||||||
> .form-control:not(:placeholder-shown),
|
> .form-control:not(:placeholder-shown),
|
||||||
|
> .form-control-plaintext,
|
||||||
> .form-select {
|
> .form-select {
|
||||||
~ label {
|
~ label {
|
||||||
opacity: $form-floating-label-opacity;
|
color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
|
||||||
transform: $form-floating-label-transform;
|
transform: $form-floating-label-transform;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
inset: $form-floating-padding-y ($form-floating-padding-x * .5);
|
||||||
|
z-index: -1;
|
||||||
|
height: $form-floating-label-height;
|
||||||
|
content: "";
|
||||||
|
background-color: $input-bg;
|
||||||
|
@include border-radius($input-border-radius);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
|
// Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
|
||||||
> .form-control:-webkit-autofill {
|
> .form-control:-webkit-autofill {
|
||||||
~ label {
|
~ label {
|
||||||
opacity: $form-floating-label-opacity;
|
color: rgba(var(--#{$prefix}body-color-rgb), #{$form-floating-label-opacity});
|
||||||
transform: $form-floating-label-transform;
|
transform: $form-floating-label-transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// stylelint-enable no-duplicate-selectors
|
|
||||||
|
> .form-control-plaintext {
|
||||||
|
~ label {
|
||||||
|
border-width: $input-border-width 0; // Required to properly position label text - as explained above
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> :disabled ~ label,
|
||||||
|
> .form-control:disabled ~ label { // Required for `.form-control`s because of specificity
|
||||||
|
color: $form-floating-label-disabled-color;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-color: $input-disabled-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,34 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-check-reverse {
|
||||||
|
padding-right: $form-check-padding-start;
|
||||||
|
padding-left: 0;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
.form-check-input {
|
||||||
|
float: right;
|
||||||
|
margin-right: $form-check-padding-start * -1;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-check-input {
|
.form-check-input {
|
||||||
|
--#{$prefix}form-check-bg: #{$form-check-input-bg};
|
||||||
|
|
||||||
|
flex-shrink: 0;
|
||||||
width: $form-check-input-width;
|
width: $form-check-input-width;
|
||||||
height: $form-check-input-width;
|
height: $form-check-input-width;
|
||||||
margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
|
margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
background-color: $form-check-input-bg;
|
appearance: none;
|
||||||
|
background-color: var(--#{$prefix}form-check-bg);
|
||||||
|
background-image: var(--#{$prefix}form-check-bg-image);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
border: $form-check-input-border;
|
border: $form-check-input-border;
|
||||||
appearance: none;
|
print-color-adjust: exact; // Keep themed appearance for print
|
||||||
color-adjust: exact; // Keep themed appearance for print
|
|
||||||
@include transition($form-check-transition);
|
@include transition($form-check-transition);
|
||||||
|
|
||||||
&[type="checkbox"] {
|
&[type="checkbox"] {
|
||||||
@ -53,17 +69,17 @@
|
|||||||
|
|
||||||
&[type="checkbox"] {
|
&[type="checkbox"] {
|
||||||
@if $enable-gradients {
|
@if $enable-gradients {
|
||||||
background-image: escape-svg($form-check-input-checked-bg-image), var(--#{$variable-prefix}gradient);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)}, var(--#{$prefix}gradient);
|
||||||
} @else {
|
} @else {
|
||||||
background-image: escape-svg($form-check-input-checked-bg-image);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-checked-bg-image)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[type="radio"] {
|
&[type="radio"] {
|
||||||
@if $enable-gradients {
|
@if $enable-gradients {
|
||||||
background-image: escape-svg($form-check-radio-checked-bg-image), var(--#{$variable-prefix}gradient);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)}, var(--#{$prefix}gradient);
|
||||||
} @else {
|
} @else {
|
||||||
background-image: escape-svg($form-check-radio-checked-bg-image);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-radio-checked-bg-image)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,9 +89,9 @@
|
|||||||
border-color: $form-check-input-indeterminate-border-color;
|
border-color: $form-check-input-indeterminate-border-color;
|
||||||
|
|
||||||
@if $enable-gradients {
|
@if $enable-gradients {
|
||||||
background-image: escape-svg($form-check-input-indeterminate-bg-image), var(--#{$variable-prefix}gradient);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)}, var(--#{$prefix}gradient);
|
||||||
} @else {
|
} @else {
|
||||||
background-image: escape-svg($form-check-input-indeterminate-bg-image);
|
--#{$prefix}form-check-bg-image: #{escape-svg($form-check-input-indeterminate-bg-image)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +106,7 @@
|
|||||||
&[disabled],
|
&[disabled],
|
||||||
&:disabled {
|
&:disabled {
|
||||||
~ .form-check-label {
|
~ .form-check-label {
|
||||||
|
cursor: default;
|
||||||
opacity: $form-check-label-disabled-opacity;
|
opacity: $form-check-label-disabled-opacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,27 +125,39 @@
|
|||||||
padding-left: $form-switch-padding-start;
|
padding-left: $form-switch-padding-start;
|
||||||
|
|
||||||
.form-check-input {
|
.form-check-input {
|
||||||
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image)};
|
||||||
|
|
||||||
width: $form-switch-width;
|
width: $form-switch-width;
|
||||||
margin-left: $form-switch-padding-start * -1;
|
margin-left: $form-switch-padding-start * -1;
|
||||||
background-image: escape-svg($form-switch-bg-image);
|
background-image: var(--#{$prefix}form-switch-bg);
|
||||||
background-position: left center;
|
background-position: left center;
|
||||||
@include border-radius($form-switch-border-radius);
|
@include border-radius($form-switch-border-radius);
|
||||||
@include transition($form-switch-transition);
|
@include transition($form-switch-transition);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background-image: escape-svg($form-switch-focus-bg-image);
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-focus-bg-image)};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:checked {
|
&:checked {
|
||||||
background-position: $form-switch-checked-bg-position;
|
background-position: $form-switch-checked-bg-position;
|
||||||
|
|
||||||
@if $enable-gradients {
|
@if $enable-gradients {
|
||||||
background-image: escape-svg($form-switch-checked-bg-image), var(--#{$variable-prefix}gradient);
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)}, var(--#{$prefix}gradient);
|
||||||
} @else {
|
} @else {
|
||||||
background-image: escape-svg($form-switch-checked-bg-image);
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-checked-bg-image)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.form-check-reverse {
|
||||||
|
padding-right: $form-switch-padding-start;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
.form-check-input {
|
||||||
|
margin-right: $form-switch-padding-start * -1;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-check-inline {
|
.form-check-inline {
|
||||||
@ -150,3 +179,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark) {
|
||||||
|
.form-switch .form-check-input:not(:checked):not(:focus) {
|
||||||
|
--#{$prefix}form-switch-bg: #{escape-svg($form-switch-bg-image-dark)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
font-weight: $input-font-weight;
|
font-weight: $input-font-weight;
|
||||||
line-height: $input-line-height;
|
line-height: $input-line-height;
|
||||||
color: $input-color;
|
color: $input-color;
|
||||||
|
appearance: none; // Fix appearance for date inputs in Safari
|
||||||
background-color: $input-bg;
|
background-color: $input-bg;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: $input-border-width solid $input-border-color;
|
border: $input-border-width solid $input-border-color;
|
||||||
appearance: none; // Fix appearance for date inputs in Safari
|
|
||||||
|
|
||||||
// Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
// Note: This has no effect on <select>s in some browsers, due to the limited stylability of `<select>`s in CSS.
|
||||||
@include border-radius($input-border-radius, 0);
|
@include border-radius($input-border-radius, 0);
|
||||||
@ -44,12 +44,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add some height to date inputs on iOS
|
|
||||||
// https://github.com/twbs/bootstrap/issues/23307
|
|
||||||
// TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved
|
|
||||||
&::-webkit-date-and-time-value {
|
&::-webkit-date-and-time-value {
|
||||||
|
// On Android Chrome, form-control's "width: 100%" makes the input width too small
|
||||||
|
// Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109
|
||||||
|
//
|
||||||
|
// On iOS Safari, form-control's "appearance: none" + "width: 100%" makes the input width too small
|
||||||
|
// Tested under iOS 16.2 / Safari 16.2
|
||||||
|
min-width: 85px; // Seems to be a good minimum safe width
|
||||||
|
|
||||||
|
// Add some height to date inputs on iOS
|
||||||
|
// https://github.com/twbs/bootstrap/issues/23307
|
||||||
|
// TODO: we can remove this workaround once https://bugs.webkit.org/show_bug.cgi?id=198959 is resolved
|
||||||
// Multiply line-height by 1em if it has no unit
|
// Multiply line-height by 1em if it has no unit
|
||||||
height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
|
height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
|
||||||
|
|
||||||
|
// Android Chrome type="date" is taller than the other inputs
|
||||||
|
// because of "margin: 1px 24px 1px 4px" inside the shadow DOM
|
||||||
|
// Tested under Android 11 / Chrome 89, Android 12 / Chrome 100, Android 13 / Chrome 109
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent excessive date input height in Webkit
|
||||||
|
// https://github.com/twbs/bootstrap/issues/34433
|
||||||
|
&::-webkit-datetime-edit {
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder
|
// Placeholder
|
||||||
@ -59,13 +78,13 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disabled and read-only inputs
|
// Disabled inputs
|
||||||
//
|
//
|
||||||
// HTML5 says that controls under a fieldset > legend:first-child won't be
|
// HTML5 says that controls under a fieldset > legend:first-child won't be
|
||||||
// disabled if the fieldset is disabled. Due to implementation difficulty, we
|
// disabled if the fieldset is disabled. Due to implementation difficulty, we
|
||||||
// don't honor that edge case; we style them as disabled anyway.
|
// don't honor that edge case; we style them as disabled anyway.
|
||||||
&:disabled,
|
&:disabled {
|
||||||
&[readonly] {
|
color: $input-disabled-color;
|
||||||
background-color: $input-disabled-bg;
|
background-color: $input-disabled-bg;
|
||||||
border-color: $input-disabled-border-color;
|
border-color: $input-disabled-border-color;
|
||||||
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
|
// iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655.
|
||||||
@ -91,25 +110,6 @@
|
|||||||
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
&:hover:not(:disabled):not([readonly])::file-selector-button {
|
||||||
background-color: $form-file-button-hover-bg;
|
background-color: $form-file-button-hover-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-file-upload-button {
|
|
||||||
padding: $input-padding-y $input-padding-x;
|
|
||||||
margin: (-$input-padding-y) (-$input-padding-x);
|
|
||||||
margin-inline-end: $input-padding-x;
|
|
||||||
color: $form-file-button-color;
|
|
||||||
@include gradient-bg($form-file-button-bg);
|
|
||||||
pointer-events: none;
|
|
||||||
border-color: inherit;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 0;
|
|
||||||
border-inline-end-width: $input-border-width;
|
|
||||||
border-radius: 0; // stylelint-disable-line property-disallowed-list
|
|
||||||
@include transition($btn-transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {
|
|
||||||
background-color: $form-file-button-hover-bg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readonly controls as plain text
|
// Readonly controls as plain text
|
||||||
@ -128,6 +128,10 @@
|
|||||||
border: solid transparent;
|
border: solid transparent;
|
||||||
border-width: $input-border-width 0;
|
border-width: $input-border-width 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&.form-control-sm,
|
&.form-control-sm,
|
||||||
&.form-control-lg {
|
&.form-control-lg {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
@ -153,12 +157,6 @@
|
|||||||
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
|
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
|
||||||
margin-inline-end: $input-padding-x-sm;
|
margin-inline-end: $input-padding-x-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-file-upload-button {
|
|
||||||
padding: $input-padding-y-sm $input-padding-x-sm;
|
|
||||||
margin: (-$input-padding-y-sm) (-$input-padding-x-sm);
|
|
||||||
margin-inline-end: $input-padding-x-sm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control-lg {
|
.form-control-lg {
|
||||||
@ -172,12 +170,6 @@
|
|||||||
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
|
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
|
||||||
margin-inline-end: $input-padding-x-lg;
|
margin-inline-end: $input-padding-x-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-file-upload-button {
|
|
||||||
padding: $input-padding-y-lg $input-padding-x-lg;
|
|
||||||
margin: (-$input-padding-y-lg) (-$input-padding-x-lg);
|
|
||||||
margin-inline-end: $input-padding-x-lg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure textareas don't shrink too much when resized
|
// Make sure textareas don't shrink too much when resized
|
||||||
@ -199,8 +191,8 @@ textarea {
|
|||||||
// stylelint-enable selector-no-qualifying-type
|
// stylelint-enable selector-no-qualifying-type
|
||||||
|
|
||||||
.form-control-color {
|
.form-control-color {
|
||||||
max-width: 3rem;
|
width: $form-color-width;
|
||||||
height: auto; // Override fixed browser height
|
height: $input-height;
|
||||||
padding: $input-padding-y;
|
padding: $input-padding-y;
|
||||||
|
|
||||||
&:not(:disabled):not([readonly]) {
|
&:not(:disabled):not([readonly]) {
|
||||||
@ -208,12 +200,15 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&::-moz-color-swatch {
|
&::-moz-color-swatch {
|
||||||
height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
|
border: 0 !important; // stylelint-disable-line declaration-no-important
|
||||||
@include border-radius($input-border-radius);
|
@include border-radius($input-border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-color-swatch {
|
&::-webkit-color-swatch {
|
||||||
height: if(unit($input-line-height) == "", $input-line-height * 1em, $input-line-height);
|
border: 0 !important; // stylelint-disable-line declaration-no-important
|
||||||
@include border-radius($input-border-radius);
|
@include border-radius($input-border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.form-control-sm { height: $input-height-sm; }
|
||||||
|
&.form-control-lg { height: $input-height-lg; }
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2);
|
height: add($form-range-thumb-height, $form-range-thumb-focus-box-shadow-width * 2);
|
||||||
padding: 0; // Need to reset padding
|
padding: 0; // Need to reset padding
|
||||||
background-color: transparent;
|
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
@ -28,12 +28,12 @@
|
|||||||
width: $form-range-thumb-width;
|
width: $form-range-thumb-width;
|
||||||
height: $form-range-thumb-height;
|
height: $form-range-thumb-height;
|
||||||
margin-top: ($form-range-track-height - $form-range-thumb-height) * .5; // Webkit specific
|
margin-top: ($form-range-track-height - $form-range-thumb-height) * .5; // Webkit specific
|
||||||
|
appearance: none;
|
||||||
@include gradient-bg($form-range-thumb-bg);
|
@include gradient-bg($form-range-thumb-bg);
|
||||||
border: $form-range-thumb-border;
|
border: $form-range-thumb-border;
|
||||||
@include border-radius($form-range-thumb-border-radius);
|
@include border-radius($form-range-thumb-border-radius);
|
||||||
@include box-shadow($form-range-thumb-box-shadow);
|
@include box-shadow($form-range-thumb-box-shadow);
|
||||||
@include transition($form-range-thumb-transition);
|
@include transition($form-range-thumb-transition);
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@include gradient-bg($form-range-thumb-active-bg);
|
@include gradient-bg($form-range-thumb-active-bg);
|
||||||
@ -54,12 +54,12 @@
|
|||||||
&::-moz-range-thumb {
|
&::-moz-range-thumb {
|
||||||
width: $form-range-thumb-width;
|
width: $form-range-thumb-width;
|
||||||
height: $form-range-thumb-height;
|
height: $form-range-thumb-height;
|
||||||
|
appearance: none;
|
||||||
@include gradient-bg($form-range-thumb-bg);
|
@include gradient-bg($form-range-thumb-bg);
|
||||||
border: $form-range-thumb-border;
|
border: $form-range-thumb-border;
|
||||||
@include border-radius($form-range-thumb-border-radius);
|
@include border-radius($form-range-thumb-border-radius);
|
||||||
@include box-shadow($form-range-thumb-box-shadow);
|
@include box-shadow($form-range-thumb-box-shadow);
|
||||||
@include transition($form-range-thumb-transition);
|
@include transition($form-range-thumb-transition);
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
@include gradient-bg($form-range-thumb-active-bg);
|
@include gradient-bg($form-range-thumb-active-bg);
|
||||||
|
@ -4,18 +4,19 @@
|
|||||||
// https://primer.github.io/.
|
// https://primer.github.io/.
|
||||||
|
|
||||||
.form-select {
|
.form-select {
|
||||||
|
--#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator)};
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x;
|
padding: $form-select-padding-y $form-select-indicator-padding $form-select-padding-y $form-select-padding-x;
|
||||||
// stylelint-disable-next-line property-no-vendor-prefix
|
|
||||||
-moz-padding-start: subtract($form-select-padding-x, 3px); // See https://github.com/twbs/bootstrap/issues/32636
|
|
||||||
font-family: $form-select-font-family;
|
font-family: $form-select-font-family;
|
||||||
@include font-size($form-select-font-size);
|
@include font-size($form-select-font-size);
|
||||||
font-weight: $form-select-font-weight;
|
font-weight: $form-select-font-weight;
|
||||||
line-height: $form-select-line-height;
|
line-height: $form-select-line-height;
|
||||||
color: $form-select-color;
|
color: $form-select-color;
|
||||||
|
appearance: none;
|
||||||
background-color: $form-select-bg;
|
background-color: $form-select-bg;
|
||||||
background-image: escape-svg($form-select-indicator);
|
background-image: var(--#{$prefix}form-select-bg-img), var(--#{$prefix}form-select-bg-icon, none);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: $form-select-bg-position;
|
background-position: $form-select-bg-position;
|
||||||
background-size: $form-select-bg-size;
|
background-size: $form-select-bg-size;
|
||||||
@ -23,7 +24,6 @@
|
|||||||
@include border-radius($form-select-border-radius, 0);
|
@include border-radius($form-select-border-radius, 0);
|
||||||
@include box-shadow($form-select-box-shadow);
|
@include box-shadow($form-select-box-shadow);
|
||||||
@include transition($form-select-transition);
|
@include transition($form-select-transition);
|
||||||
appearance: none;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: $form-select-focus-border-color;
|
border-color: $form-select-focus-border-color;
|
||||||
@ -60,6 +60,7 @@
|
|||||||
padding-bottom: $form-select-padding-y-sm;
|
padding-bottom: $form-select-padding-y-sm;
|
||||||
padding-left: $form-select-padding-x-sm;
|
padding-left: $form-select-padding-x-sm;
|
||||||
@include font-size($form-select-font-size-sm);
|
@include font-size($form-select-font-size-sm);
|
||||||
|
@include border-radius($form-select-border-radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-select-lg {
|
.form-select-lg {
|
||||||
@ -67,4 +68,13 @@
|
|||||||
padding-bottom: $form-select-padding-y-lg;
|
padding-bottom: $form-select-padding-y-lg;
|
||||||
padding-left: $form-select-padding-x-lg;
|
padding-left: $form-select-padding-x-lg;
|
||||||
@include font-size($form-select-font-size-lg);
|
@include font-size($form-select-font-size-lg);
|
||||||
|
@include border-radius($form-select-border-radius-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark) {
|
||||||
|
.form-select {
|
||||||
|
--#{$prefix}form-select-bg-img: #{escape-svg($form-select-indicator-dark)};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
> .form-control,
|
> .form-control,
|
||||||
> .form-select {
|
> .form-select,
|
||||||
|
> .form-floating {
|
||||||
position: relative; // For focus state's z-index
|
position: relative; // For focus state's z-index
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
width: 1%;
|
width: 1%;
|
||||||
@ -19,8 +20,9 @@
|
|||||||
|
|
||||||
// Bring the "active" form control to the top of surrounding elements
|
// Bring the "active" form control to the top of surrounding elements
|
||||||
> .form-control:focus,
|
> .form-control:focus,
|
||||||
> .form-select:focus {
|
> .form-select:focus,
|
||||||
z-index: 3;
|
> .form-floating:focus-within {
|
||||||
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure buttons are always above inputs for more visually pleasing borders.
|
// Ensure buttons are always above inputs for more visually pleasing borders.
|
||||||
@ -31,7 +33,7 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
z-index: 3;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,15 +98,19 @@
|
|||||||
// stylelint-disable-next-line no-duplicate-selectors
|
// stylelint-disable-next-line no-duplicate-selectors
|
||||||
.input-group {
|
.input-group {
|
||||||
&:not(.has-validation) {
|
&:not(.has-validation) {
|
||||||
> :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),
|
> :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
|
||||||
> .dropdown-toggle:nth-last-child(n + 3) {
|
> .dropdown-toggle:nth-last-child(n + 3),
|
||||||
|
> .form-floating:not(:last-child) > .form-control,
|
||||||
|
> .form-floating:not(:last-child) > .form-select {
|
||||||
@include border-end-radius(0);
|
@include border-end-radius(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.has-validation {
|
&.has-validation {
|
||||||
> :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu),
|
> :nth-last-child(n + 3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),
|
||||||
> .dropdown-toggle:nth-last-child(n + 4) {
|
> .dropdown-toggle:nth-last-child(n + 4),
|
||||||
|
> .form-floating:nth-last-child(n + 3) > .form-control,
|
||||||
|
> .form-floating:nth-last-child(n + 3) > .form-select {
|
||||||
@include border-end-radius(0);
|
@include border-end-radius(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +121,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
> :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
|
> :not(:first-child):not(.dropdown-menu)#{$validation-messages} {
|
||||||
margin-left: -$input-border-width;
|
margin-left: calc(#{$input-border-width} * -1); // stylelint-disable-line function-disallowed-list
|
||||||
|
@include border-start-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .form-floating:not(:first-child) > .form-control,
|
||||||
|
> .form-floating:not(:first-child) > .form-select {
|
||||||
@include border-start-radius(0);
|
@include border-start-radius(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
resources/themes/architect/src/components/bootstrap5/helpers/_color-bg.scss
vendored
Normal file
7
resources/themes/architect/src/components/bootstrap5/helpers/_color-bg.scss
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// All-caps `RGBA()` function used because of this Sass bug: https://github.com/sass/node-sass/issues/2251
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
.text-bg-#{$color} {
|
||||||
|
color: color-contrast($value) if($enable-important-utilities, !important, null);
|
||||||
|
background-color: RGBA(var(--#{$prefix}#{$color}-rgb), var(--#{$prefix}bg-opacity, 1)) if($enable-important-utilities, !important, null);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user